Why doesnt Window.FindName() discover the x:Name of a button in a child UserControl? AKA how do NameScopes work?

8.8k views Asked by At

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

There are 1 answers

1
Reed Copsey On BEST ANSWER

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.