x:如果元素包含在 UserControl 的内容中,则名称不起作用 (Silverlight)
情况:
我有一个像这样的“包装面板”UserControl
(为了简洁起见,删除了命名空间和视觉细节):
<UserControl ...>
<Grid x:Name="LayoutRoot" Background="White">
<ContentPresenter x:Name="integratedPanelContent" Margin="5" />
</Grid>
</UserControl>
然后在代码隐藏中我注册了一个依赖属性
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;
}
现在我可以将任何内容包装到我的控件中:
<my:MyWrapperPanel x:Name="myWrap">
<my:MyWrapperPanel.PanelContent>
<TextBlock x:Name="tbxNothing" Text="Nothing" />
</my:MyWrapperPanel.PanelContent>
</my:MyWrapperPanel>
问题描述:
每当我尝试在代码隐藏中使用引用 tbxNothing 时,系统都会抛出 NullReferenceException
因为tbxNothing 虽然作为引用存在,但并不指向 XAML 中定义的 TextBlock,而是 null
。
可能(但不方便)的解决方法:
有一种解决方法,我从 TextBlock 中删除 x:Name
,然后明确定义名为 tbxNothing
的私有 TextBlock >。然后在 OnNavigedTo 事件处理程序中,我按以下方式分配值:
tbxNothing = myWrap.PanelContent as TextBlock;
这可以工作,但不是正确的方法,因为如果内容是包含所需控件的堆栈面板,我必须遍历树去寻找我需要的东西,极其不方便。
问题:
为什么文本块在包装在用户控件中时不再可见(按照描述的方式),以及如何通过代码隐藏中的 x:Name 获取它?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题是你的面板内容落在两个凳子之间。一方面,名称为“tbxNothing”的内容是在主页的名称范围中创建的。然而,此时它还没有添加到对象树中。另一方面,作为 UserControl 的 MyWrapperPanel 有其自己的名称范围,并且将名称为“tbxNothing”的项目添加到其下方的对象树中。主页上的
FindName
不会在 MyWrapperPanel 中找到任何内容,因为它有自己的名称范围,而 MyWrapperPanel 上的FindName
不会找到“tbxNothing”,因为它没有存在于其名称范围中(实际在主页中创建)。答案是不要使用
UserControl
作为MyWrapperPanel
的基础。相反,创建一个 Silverlight 模板控件。将其继承的基类修改为ContentControl
并调整其默认模板以包含ContentPresenter
。应该看起来像这样:-然后在 theme/generic.xaml 中,样式可以看起来像这样:-
你的主页 xaml 看起来像这样:-
请注意,从
ContentControl
派生给你一个Content
属性。ContentPresenter
自动神奇地连接到的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.
FindName
on the main page won't find anything inside the MyWrapperPanel because it has its own namescope andFindName
on 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
UserControl
as a basis forMyWrapperPanel
. Instead create a Silverlight Template Control. Modify the base class it inherits from toContentControl
and 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
ContentControl
gives you aContent
property which theContentPresenter
auto-magically wires to.