Situation:
I have a “wrapper panel” UserControl like this (namespaces and visual details removed for brevity):
<UserControl ...>
<Grid x:Name="LayoutRoot" Background="White">
<ContentPresenter x:Name="integratedPanelContent" Margin="5" />
</Grid>
</UserControl>
Then in the Code-behind I have registered a dependency property
public FrameworkElement PanelContent
{
get { return (FrameworkElement)GetValue(PanelContentProperty); }
set { SetValue(PanelContentProperty, value); }
}
public static readonly DependencyProperty PanelContentProperty =
DependencyProperty.Register("PanelContent", typeof(FrameworkElement), typeof(MyWrapperPanel),
new PropertyMetadata(null, OnPanelContentChanged));
private static void OnPanelContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyWrapperPanel)d).OnSetContentChanged(e);
}
protected virtual void OnSetContentChanged(DependencyPropertyChangedEventArgs e)
{
if (PanelContent != null)
integratedPanelContent.Content = PanelContent;
}
Now I can wrap any content into my control:
<my:MyWrapperPanel x:Name="myWrap">
<my:MyWrapperPanel.PanelContent>
<TextBlock x:Name="tbxNothing" Text="Nothing" />
</my:MyWrapperPanel.PanelContent>
</my:MyWrapperPanel>
Description of the problem:
Whenever I try to use the reference tbxNothing in codebehind, the system throws NullReferenceException because tbxNothing, although as a reference exists, does not point to the TextBlock defined in XAML, but is null.
Possible (but inconvenient) workaround:
There is a workaround where I remove x:Name from the TextBlock, and then I explicitely define private TextBlock called tbxNothing. Then in the OnNavigatedTo event handler I assign the value the following way:
tbxNothing = myWrap.PanelContent as TextBlock;
This works but is not a right way to do it, because if a content is a stackpanel that contains wanted controls, I’d have to traverse the tree to find what I need, which is extremely inconvenient.
Question:
Why is the textblock no longer visible when wrapped in a User control (the way described), and how to get it by its x:Name in code-behind?
The problem is your panel content is falling between two stools. On the one hand the content with the name “tbxNothing” is create in the namescope of the main page. However its not added to the object tree at that point. On the other hand the MyWrapperPanel being a UserControl has its own namescope and its into the object tree below this that the item with then name “tbxNothing” is added.
FindNameon the main page won’t find anything inside the MyWrapperPanel because it has its own namescope andFindNameon the MyWrapperPanel won’t find “tbxNothing” because it doesn’t exist in its namescope (being actually created in the main page).The answer is don’t use a
UserControlas a basis forMyWrapperPanel. Instead create a Silverlight Template Control. Modify the base class it inherits from toContentControland tweak its default template to include aContentPresenter. Should look something like this:-then in themes/generic.xaml the style can look like this:-
Your main page xaml would look like:-
Note that deriving from
ContentControlgives you aContentproperty which theContentPresenterauto-magically wires to.