Powered by MSDN

US - English
NEW! Silverlight 5 is available Learn More

FindName Recursive? RSS

12 replies

Last post Jun 16, 2011 09:16 AM by mepfuso

(0)
  • mmoser

    mmoser

    Member

    2 Points

    5 Posts

    FindName Recursive?

    Oct 20, 2008 09:58 PM | LINK

    It seems that FindName is not Recursive.  True?  

     

    I am getting an unexpected behavior from FrameworkElement.FindName.  In the MSDN documentation, it says that the method is recursive and will search all children.

     

    http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.findname(VS.95).aspx

     

    “If the object has child objects, the child objects are all searched recursively for the requested named object. In general, a FindName call searches the current XAML namescope, in both the "up" and "down" directions in terms of the object tree. “

     

    Namescope definition: http://msdn.microsoft.com/en-us/library/cc189026(VS.95).aspx

     

    However when I create a simple sample application where:

    1.       Page.xaml has an instance of MyControl

    2.       MyControl has a TextBlock named ChildControl

    3.       On the Loaded event of Page.xaml’s LayoutRoot I use this.FindName to try to find ChildControl and it returns null.  However if I do a FindName on MyControl, it’s found.

     

    This tells me that it is not recursive, despite what the documentation says and how I understood this to work. 

     

    If FindName truly does not work for recursive child controls, does anyone have a way that I can recursivly walk the children and look for a control by a certian name?  I am having trouble finding how to do this.  It seems that only Panel and Grid and such have children, but what if I have a UserControl with children.  Thanks in advance for the help.

     

    FindName

  • Wolf Schmidt (MSFT)

    Wolf Schmidt...

    Participant

    1088 Points

    158 Posts

    Microsoft

    Re: FindName Recursive?

    Oct 21, 2008 01:49 AM | LINK

    Where is ChildControl? In the template XAML for MyControl? A page-level namescope specifically does not extend into any template generated content. It can't, because a template applied more than once would be a name collision. You need to use GetTemplateChild instead.

  • mmoser

    mmoser

    Member

    2 Points

    5 Posts

    Re: Re: FindName Recursive?

    Oct 21, 2008 04:14 PM | LINK

    Thanks for the reply Wolf.  ChildControl is simple a TextBlock in the LayoutRoot of MyControl:

    <UserControl x:Class="SilverlightApplication4.MyControl"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="400" Height="300" Loaded="UserControl_Loaded">

    <Grid x:Name="LayoutRoot" Background="White">

    <TextBlock x:Name="ChildControl" Text="dsfsdfdsf dsf ds"/>

    </Grid>

    </UserControl>

    Should this work?  It seems like it should.

  • Wolf Schmidt (MSFT)

    Wolf Schmidt...

    Participant

    1088 Points

    158 Posts

    Microsoft

    Re: Re: Re: FindName Recursive?

    Oct 21, 2008 09:21 PM | LINK

    Yes it SHOULD work. And it does work for me so it's surprising not working for you. Here's my quickie handler:

    public partial class UserControl{
        public Page() {
        // InitializeComponent is here
        }
        void UserControl_Loaded(object sender, RoutedEventArgs e
        {
            FrameworkElement fe = sender as FrameworkElement;
            TextBlock tb = (TextBlock)fe.FindName("ChildControl");
            tb.Text = "Found by name ...";
        }
    }

    It's possible you are running into a timing issue with Loaded, although I thought that was isolated to cases where templates are applied and you try to find the parts of the template. Just as a failsafe or a sanity check, another thing you could try is handling LayoutUpdated instead.

  • JimBG

    JimBG

    Member

    4 Points

    2 Posts

    Re: Re: Re: FindName Recursive?

    Oct 23, 2008 04:09 PM | LINK

    I am having the same results as mmoser. The XAML for Page contains only the declaration for MyControl. The XAML for MyControl contains the declaration for ChildControl (TextBlock). Calling this.FindName("ChildControl") within MyControl returns the TextBlock. Calling this.FindName("ChildControl") within Page returns a null.

    In testing I copied the ChildControl (TextBlock) XAML within MyControl and pasted it into Page. Page would then return a reference to the newly pasted ChildControl.

    What I (and mmoser) am looking to do is to recurse through the elements contained within a FrameworkElement, regardless of where the declaring XAML is located.

    [pseudo]
    Page.xaml
        MyControl
    
    MyControl.xaml
        TextBlock x:Name="ChildControl"
    

    I'm looking to find a way to obtain a reference to "ChildControl" from Page. This is just a simple example. In practice, the target element may be nested 5+ levels.

    I have also tried handling the call in LayoutUpdated as well, as you indicated. Same result.

  • mmoser

    mmoser

    Member

    2 Points

    5 Posts

    Re: Re: Re: Re: FindName Recursive?

    Oct 23, 2008 05:20 PM | LINK

    Does anyone understand namescopes?  Is the child control (MyControl) in a different namescope?  Do I have to register it with the Page's namescope.  I don't fully understand how that works, I do know that FindName searches within the NameScope.  Just don't know how the scope is defined.

  • Wolf Schmidt (MSFT)

    Wolf Schmidt...

    Participant

    1088 Points

    158 Posts

    Microsoft

    Re: Re: Re: Re: Re: FindName Recursive?

    Oct 23, 2008 09:20 PM | LINK

    OK sorry now I get it, I did not read the original question carefully enough.

    So you have this: a project that builds a UserControl named MyControl. MyControl includes in its definition XAML that has a <TextBlock x:Name="ChildControl"/>. Then, you have an application that references the MyControl assembly, maps it (say to "my"), then includes a MyControl instance eg <my:MyControl .../>. From the application you might have a Loaded at the top:

    <UserControl //default mappings, my mappings, x:Class etc Loaded="TryFindChildControl">
      <my:MyControl/>
    </UserControl>

    In this case, no, the namescope of the application and of MyControl do not combine. Even though you have combined them object-tree wise by putting a MyControl instance into the only-settable-in-XAML "Content" of your application UserControl subclass, the combination of object trees does not equate to combination of namescopes.

    You have to think of namescopes as being a pretty XAML-oriented concept, and that namescopes are defined by a behavior of a XAML parser. In this case, the namescope that contains ChildControl got created when you parsed out the XAML that defines the appearance of MyControl, and the namescope is defined at the level of the MyControl class / UserControl subclass. Likewise, another namescope was defined when you parsed the UserControl subclass for the application. But that parse didn't see any "Name" attributes named ChildControl. In fact all it saw was the <my:MyControl/>, and that won't be created/walked into until runtime, so unless there were runtime combination of namescopes (which there isn't) the two namescopes never shall meet.

    The idea of "registering to a namescope" was mentioned. That won't help you. WPF has a Namescope class that enables registering into existing namescopes, but Silverlight does not.

    What you COULD do here though is just provide a Name at the <my:MyControl Name="MyControlInstance"/> level, to enable a bridge between the namescopes.

    First make a FindName call that returns your MyControlInstance. When this object is returned, its consideration of what is under its OWN namescope is the one that MyControl defines. NOW you can call FindName("ChildControl") on the first FindName's return and get into the part of the object tree that MyControl defines in its markup.

    Your complaints about this case not being in the documentation are totally valid, I don't believe I covered this situation. If you were creating a ContentControl and not a UserControl I would probably tell you that if you really wanted named elements to be found you should define and name them in the control's template (in which case the namescope considerations for templates kick in). But since for UserControl what you are doing is the only way to declare the content and name it, I should definitely have noted a UserControl usage in the "namescopes" conceptual documentation: this being another case where a namescope "break" happens, just like with templates, or any object tree segment created through XamlReader.Load.

  • JimBG

    JimBG

    Member

    4 Points

    2 Posts

    Re: Re: Re: Re: Re: FindName Recursive?

    Oct 27, 2008 03:04 PM | LINK

    Thank you for your time.  It does appear to be a matter of XAML namescope.  I was attempting to find a way to walk the visual tree, regardless of namescope.  I ended up figuring out a way to do this using the VisualTreeHelper class.  I blogged a bit about my implementation here should anyone else be interested in the same type of functionality.  Thanks again for the help, Wolf!

     Here's that link again: http://blogs.vertigo.com/personal/jimbg/Blog/archive/2008/10/24/walk-the-visual-tree.aspx

  • footnick

    footnick

    Member

    46 Points

    71 Posts

    Re: Re: Re: Re: Re: Re: FindName Recursive?

    Nov 13, 2008 02:43 PM | LINK

    Hi Jim

    Your VisualTreeHelper class solved a similar problem for me.

     See this thread http://silverlight.net/forums/t/48535.aspx

    Thanks

     Nick

  • gvinsot

    gvinsot

    Member

    47 Points

    9 Posts

    Re: Re: Re: Re: Re: Re: Re: FindName Recursive?

    Dec 10, 2009 10:13 AM | LINK

    You can also try this : FrameworkElement FindNameInTree(FrameworkElement whereToFind, String name) { if (whereToFind == null) return null; if (whereToFind.Name == name) return whereToFind; if (whereToFind as Panel != null) { Panel panelToExplore = whereToFind as Panel; foreach (FrameworkElement child in panelToExplore.Children) { FrameworkElement result = FindNameInTree(child, name); if (result != null) return result; } return null; } else if (whereToFind as UserControl != null) { FrameworkElement content = ((UserControl)whereToFind).GetValue(UserControl.ContentProperty) as FrameworkElement; return FindNameInTree(content, name); } else if (whereToFind as ItemsControl != null) { ItemCollection items = (whereToFind as ItemsControl).Items; foreach (FrameworkElement child in items) { FrameworkElement result = FindNameInTree(child, name); if (result != null) return result; } return null; } else if (whereToFind as ScrollViewer != null) { FrameworkElement fre = (FrameworkElement)((ScrollViewer)whereToFind).Content; return FindNameInTree(fre, name); } return null; }