x:如果元素包含在 UserControl 的内容中,则名称不起作用 (Silverlight)

发布于 2024-11-15 15:32:33 字数 1934 浏览 4 评论 0原文

情况:

我有一个像这样的“包装面板”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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

黯然 2024-11-22 15:32:33

问题是你的面板内容落在两个凳子之间。一方面,名称为“tbxNothing”的内容是在主页的名称范围中创建的。然而,此时它还没有添加到对象树中。另一方面,作为 UserControl 的 MyWrapperPanel 有其自己的名称范围,并且将名称为“tbxNothing”的项目添加到其下方的对象树中。主页上的 FindName 不会在 MyWrapperPanel 中找到任何内容,因为它有自己的名称范围,而 MyWrapperPanel 上的 FindName 不会找到“tbxNothing”,因为它没有存在于其名称范围中(实际在主页中创建)。

答案是不要使用 UserControl 作为 MyWrapperPanel 的基础。相反,创建一个 Silverlight 模板控件。将其继承的基类修改为 ContentControl 并调整其默认模板以包含 ContentPresenter。应该看起来像这样:-

public class MyWrapperPanel : ContentControl
{
    public MyWrapperPanel ()
    {
        this.DefaultStyleKey = typeof(MyWrapperPanel );
    }
}

然后在 theme/generic.xaml 中,样式可以看起来像这样:-

<Style TargetType="local:MyWrapperPanel">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MyWrapperPanel">
                <Grid>
                    <ContentPresenter />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

你的主页 xaml 看起来像这样:-

<my:MyWrapperPanel x:Name="myWrap">
    <TextBlock x:Name="tbxNothing" Text="Nothing" />
</my:MyWrapperPanel>

请注意,从 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 and FindName 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 for MyWrapperPanel. Instead create a Silverlight Template Control. Modify the base class it inherits from to ContentControl and tweak its default template to include a ContentPresenter. Should look something like this:-

public class MyWrapperPanel : ContentControl
{
    public MyWrapperPanel ()
    {
        this.DefaultStyleKey = typeof(MyWrapperPanel );
    }
}

then in themes/generic.xaml the style can look like this:-

<Style TargetType="local:MyWrapperPanel">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MyWrapperPanel">
                <Grid>
                    <ContentPresenter />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Your main page xaml would look like:-

<my:MyWrapperPanel x:Name="myWrap">
    <TextBlock x:Name="tbxNothing" Text="Nothing" />
</my:MyWrapperPanel>

Note that deriving from ContentControl gives you a Content property which the ContentPresenter auto-magically wires to.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文