WPF - FindName Returns null when it should not
Asked Answered
P

8

43

FindName is broken for me :(

The object I am looking for is there. I have proof.

Here is the scenario:

ToggleButton button = (ToggleButton)sender;
Popup popup = (Popup)button.FindName("popSelectIteration");

popup is null but not always. Just sometimes. But even when it is set to null the child I am looking for is there.

I put a break point in when it was null and grabbed these two screenshots.

The is where FindName is returning null for "popSelectIteration".

But if you dig into the watch, you see that the child is there.

So what am I missing? Why does FindName not find it? As you can see from the screen shot this is not a timing issue (the FindName watch is null but the direct path is fine).

Is there a better way to find a control?

Side Note: If you are intersted in the XAML for the toggle button in question it can be found in this question: WPF - FrameworkElement - Enumerate all decendents?.


Update: I did some digging to see why this fails some times and other times it works. I have an animation that calls NameScope.SetNameScope((DependencyObject)form, new NameScope()); (Full method code here). Right after that call the FindName starts to fail.

I don't really understand that call. I think I copied and pasted the code. Anyway, I commented it out. But I would love know why this is failing.

Percent answered 18/2, 2010 at 0:27 Comment(0)
W
47

I would guess it has to do with the difference between the visual and logical tree. The control is in the logical tree but maybe the template for this control has not been applied yet and therefore FindName won't return anything useful.

You could try to call ApplyTemplate(); on the container first.

This would also explain why it returns something sometimes.

Weaponeer answered 18/2, 2010 at 6:18 Comment(4)
Worked for me. Was having this issue with Template.FindName()Klemm
This worked for me, it was an issue more for controls that were hidden initially or on a tab that was not initially visible.Wristband
I was having an issue with GetTemplateChild("PART_EditableTextBox") with a ComboBox. Calling ApplyTemplate() beforehand solved the issue. Thanks!Elytron
Similar situation when overriding Template Changed metadata to attach bindings to menu items. Worked beautifully.Colubrid
C
42

Try

LogicalTreeHelper.FindLogicalNode(button, "popSelectIteration");
Consciousness answered 2/12, 2010 at 15:30 Comment(1)
It just happen to me! i spend 2 hours on it, finally, i try your method and works!!!Walls
C
14

Little late to the party (and not actually answer to OP question), but

when you add elements dynamically, they are not findable by FindName. You need to register them by calling RegisterName.

Example:

string number = GenerateNumber();

Button myButton = new Button();
myButton.Content = number;
myButton.Name = "button_" + number;

RegisterName(myButton.Name, myButton);

Panel.Children.Add(myButton);
object o = Panel.FindName(myButton.Name);

Maybe someone might find this useful.

Coolth answered 22/10, 2019 at 9:47 Comment(2)
Great and workable explanation with simplest words.Tav
Exactly what I was looking for. tksDemetria
R
6

In my experience, this happens when you add items via code-behind. I've found that you can fool FindName() (or the animation framework) via name scopes. That is, when you create your control, you do

    NameScope.GetNameScope(yourContainer).RegisterName("name of your control", yourControlInstance);

For this to work reliably, though, you must make sure that you unregister the name:

    NameScope.GetNameScope(yourContainer).UnregisterName("name of your control");

Posting this for future reference.

Raffinate answered 7/4, 2014 at 16:19 Comment(0)
T
3

I have meet the same question now, but I use the method like so:

    #region Override - OnApplyTemplate

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        this.PART_ListViewLeft      = GetTemplateChild(cPART_ListViewLeft)      as ListView;
        this.PART_ListViewCenter    = GetTemplateChild(cPART_ListViewCenter)    as ListView;
        this.PART_ListViewRight     = GetTemplateChild(cPART_ListViewRight)     as ListView;

        this.PART_GridViewLeft      = GetTemplateChild(cPART_GridViewLeft)      as DsxGridView;
        this.PART_GridViewCenter    = GetTemplateChild(cPART_GridViewCenter)    as DsxGridView;
        this.PART_GridViewRight     = GetTemplateChild(cPART_GridViewRight)     as DsxGridView;
        if(this.PART_ListViewLeft!=null)
            this.PART_ListViewLeft      .AlternationCount = this.AlternatingRowBrushes.Count;
        if(this.PART_ListViewCenter!=null)
            this.PART_ListViewCenter    .AlternationCount = this.AlternatingRowBrushes.Count;
        if(this.PART_ListViewRight!=null)
            this.PART_ListViewRight     .AlternationCount = this.AlternatingRowBrushes.Count;
      //  ApplyTempleted = true;
        CreateColumnLayout();
    }
    #endregion

If the Control is dynamic create and of which or whose container the 'Visibility' is set to hide or Collapsed, then the code this.PART_ListViewLeft = GetTemplateChild(cPART_ListViewLeft) as ListView; will always return null, the reason is that the datatemplete has not yet been applied before OnApplyTemplate being called.

Torchbearer answered 25/10, 2013 at 13:32 Comment(0)
H
1

I would suggest to avoid using FindName function, based on my experience, expecially problematic when you try to find something in the DataTemplate applied to some control. Instead , if it possible (based on your software architecture) declare Popup in XAML and refer to it like resource or use Binding to set some Model property to it's reference. Good luck.

Huldahuldah answered 18/2, 2010 at 16:10 Comment(0)
C
-1

Try to use button.FindResource("popSelectIteration")

Currajong answered 3/4, 2013 at 13:47 Comment(0)
R
-1

ellipseStoryboard.Children.Add(myRectAnimation); containerCanvas.Children.Add(myPath); After you add register the controls like RegisterName("TextBlock1", Var_TextBox); or RegisterName(myRectAnimation.Name,myRectAnimation); RegisterName(myPath.Name,myPath);

Rumph answered 13/9, 2019 at 15:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.