I have created a user control that I reuse for different purposes. I have defined a Dependency Property that holds a UIElement, which I present as the content of one of the areas in the user control.
I noticed that when I use this control. and give names to the elements inside the content property, they always appear as null at run time.
MyContainer.xaml:
<UserControl x:Class="BSSApp.UI.Tests.MyContainer" x:Name="userControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Border x:Name="LayoutRoot" Background="Green" CornerRadius="20" BorderBrush="#005500" BorderThickness="10">
<ContentPresenter Content="{Binding ElementName=userControl, Path=MyContent}" Margin="20"/>
</Border>
</UserControl>
MyContainer.xaml.cs
namespace BSSApp.UI.Tests
{
public partial class MyContainer : UserControl
{
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent",
typeof(UIElement),
typeof(MyContainer),
new PropertyMetadata(new Grid()));
public UIElement MyContent
{
get
{
return (UIElement)GetValue(MyContentProperty);
}
set
{
SetValue(MyContentProperty, value);
}
}
public MyContainer()
{
InitializeComponent();
}
}
}
And the using class: UserControlContentBug.xaml:
<UserControl x:Class="BSSApp.UI.Tests.UserControlContentBug"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:t="clr-namespace:BSSApp.UI.Tests"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<t:MyContainer>
<t:MyContainer.MyContent>
<Button Name="btnWithName" Click="btnWithName_Click">Click Me</Button>
</t:MyContainer.MyContent>
</t:MyContainer>
</Grid>
</UserControl>
and the code behind: UserControlContentBug.xaml.cs
namespace BSSApp.UI.Tests
{
public partial class UserControlContentBug : UserControl
{
public UserControlContentBug()
{
InitializeComponent();
}
private void btnWithName_Click(object sender, RoutedEventArgs e)
{
// this throws an exception
btnWithName.Tag=2;
}
}
}
So there is a declaration of a button, named "btnWithName". the variable is indeed declared in the code behind, but it holds null... It only happens when the button is declared inside a property of another user control.
Does anybody know how to solve this?
Thanks
I just ran into this problem myself. The generated partial class (e.g., obj\UserControlContentBug.xaml.g.cs) which defines the code-accessible named elements in your XAML uses FrameworkElement.FindName() to make those assignments.
According to MSDN:
A run-time API such as FindName is working against an object tree. These objects are loaded into the content area and the CLR runtime engine of the overall Silverlight plug-in. When part of the object tree is created from templates or run-time loaded XAML, a XAML namescope is typically not contiguous with the entirety of that object tree. The result is that there might be a named object in the object tree that a given FindName call cannot find.
Since MyContainer establishes its own namescope boundary as a UserControl, calling FindName() from UserControlContentBug will not explore that part of the object tree (where btnWithName is defined).
There are probably a few different ways to work around this, but the simplest one that works for me is to:
Give MyContainer a name (x:Name="myContainer"
).
Remove the name from the Button (btnWithName) so that the superfluous, always-null variable of the same name isn't reserved in the generated C# code.
Create a reference to the Button in the codebehind.
Like so:
namespace BSSApp.UI.Tests
{
public partial class UserControlContentBug : UserControl
{
private Button btnWithName;
public UserControlContentBug()
{
Loaded += UserControlContentBugLoaded;
InitializeComponent();
}
private void UserControlContentBugLoaded(object sender, System.Windows.RoutedEventArgs e)
{
btnWithName = myContainer.MyContent as Button;
}
private void btnWithName_Click(object sender, RoutedEventArgs e)
{
// this throws an exception, not so much
btnWithName.Tag=2;
}
}
}
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With