views:

637

answers:

1

So in the example code below, I create a UserControl UserControldChild which is a child of the main Window, Window1.xaml. Why does the FindName() method fail to find the "myButton" in the code below?

This must have to do with the WPF XAML NameScopes, but I have yet to find a good explanation as to how NameScope works. Can someone enlighten me?

//(xml) Window1.xaml    
<Window x:Class="VisualTreeTestApplication.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:VisualTreeTestApp="clr-namespace:VisualTreeTestApplication"
    Title="Window1" Height="400" Width="400">
    <Grid>
        <VisualTreeTestApp:UserControlChild/>
    </Grid>
</Window>

//(c#) Window1.xaml.cs
namespace VisualTreeTestApplication
{
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class Window1 : Window
  {
    public Window1()
    {
      InitializeComponent();
      Button btnTest = (Button)Application.Current.MainWindow.FindName("myButton");
      // btnTest is null!
    }
  }
}

UserControl below:

//(wpf) UserControlChild.xaml
<UserControl x:Class="VisualTreeTestApplication.UserControlChild"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid x:Name="myGrid">      
        <Button x:Name="myButton" Margin="20" >Button</Button>
    </Grid>
</UserControl>

//(c#) UserControlChild.xaml.cs (no changes)
namespace VisualTreeTestApplication
{
  /// <summary>
  /// Interaction logic for UserControlChild.xaml
  /// </summary>
  public partial class UserControlChild : UserControl
  {
    public UserControlChild()
    {
      InitializeComponent();
    }
  }
}

In case this doesn't get answered properly, I found an alternative to using FindName() documented in the post here.

+1  A: 

You are correct - this has to do with XAML Namescopes.

This is (somewhat poorly) documented in the Name related APIs section of the XAML Namescopes page.

Basically, if you have a FrameworkElement or FrameworkContentElement, it will define its own name scope. If you call FindName() on a type that doesn't have a namescope, WPF searches up thet ree until it finds an element that does define a namescope, then searches within that namescope.

In your case, it's searching at Window's namescope (it's a FrameworkContentElement, so it defines its own scope). It just searches elements defined in that scope.

In your case, the button is in the UserControl's namescope, though, so Window.FindName() doesn't find it. There is no automatically searching down the tree into lower level scopes.

This is a good thing - your "Window" shouldn't know or want to know anything about internal details of a UserControl it's using. If you need properties within the UserControl, they should be exposed at the UserControl level - let the control manage its own children.

Reed Copsey
Thank you for the clarification! It seems as though FindName wouldn't be the best tool for the job in this situation, aka you can't easily search down in scope. Can you think of a better method as to how to find the name of a child button in the visual tree other than my recursive solution detailed here? http://stackoverflow.com/questions/636383/wpf-ways-to-find-controls/1759923#1759923
CrimsonX