为什么ItemsControl容器的Parent属性返回null而不是其所在的Panel?(涉及IT技术)

13

这个问题让我感到困惑。我们有一个自定义的ItemsControl,它使用自定义的容器和自定义面板作为其ItemsHost。 现在,该面板具有一些度量标准,容器需要用于呈现目的。 由于它们是视觉树中面板的直接子项,因此您可能认为容器的Parent属性将返回面板,但实际上不是这样!

我还使用Snoop在标准ListBox上确认了这一点,因此这并不是我们代码专属的问题,而是所有ItemsControls的容器都存在同样的情况。

现在我知道可以使用VisualTreeHelper获取视觉父级(这正是我所需要的),但是为什么Parent属性不是面板呢?

如果论点是面板只是视觉树的一部分,而Parent保留给逻辑树,那么父级不应该是ItemsControl吗?

如果论点是容器也是ItemsControl的视觉树的一部分而不是逻辑树的一部分,那么为什么托管在容器中的内容会将容器作为其Parent属性返回?

这意味着如果从数据项遍历逻辑树,则停止在容器处,这可能解释为什么我们从容器到面板的绑定未按预期工作。 (我认为绑定基于逻辑层次结构而不是视觉层次结构,但我需要进行测试才能确定。)


1
好问题,我也一直想知道。只是顺便提一下:您还可以使用ItemsControl.GetItemsOwner获取ItemsControl - Niki
真的,但是在绑定中你不能这样做,除非你明确地编写一个转换器。使用RelativeSource向上搜索树似乎失败了;我猜测原因正是我指出的...容器的父级为空。 - Mark A. Donohoe
这确实是一个好问题。我相信在某个地方读到过,listboxitem 有一个非公共成员来获取其父级。但是,如果您使用自定义容器,则需要在创建时设置父级。 - Michael
2个回答

8
我之前没注意到这一点,这引起了我的好奇心。在查找.NET Framework的线索后,我发现Parent属性似乎确实是手动设置的:
这需要几个步骤,但我发现更改父属性的唯一方法是调用这些方法:
如果例如分析FrameworkElement.AddLogicalChild方法,我发现这些方法正在使用它:
这证实了parent属性应该指向逻辑树。我尝试创建自己的自定义控件:
[ContentProperty("CustomContent")]
public class CustomControl1 : Control
{
    static CustomControl1()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
    }

    public object CustomContent
    {
        get { return GetValue(CustomContentProperty); }
        set { SetValue(CustomContentProperty, value); }
    }

    public static readonly DependencyProperty CustomContentProperty = DependencyProperty.Register("CustomContent", typeof(object), typeof(CustomControl1));
}

使用此模板:

<ControlTemplate TargetType="{x:Type local:CustomControl1}">
     <ContentPresenter ContentSource="CustomContent" />
</ControlTemplate>

我是这样使用它的:
<WpfApplication1:CustomControl1 Width="50" Height="50">
    <Rectangle Fill="Red" />
</WpfApplication1:CustomControl1>

...这是如此起作用(像个魅力 :-)):

自定义控件截图

...猜猜看... 矩形的父级未设置 :-)

我现在没有时间继续调查,但关于ItemsControl,我想也许ItemContainerGenerator不知道插入itemsContainers的逻辑父级,这可能解释为什么在这种情况下父属性未设置...但需要证明...


很棒的研究!(抱歉我没早点看到。)而且细节也很好!更多的SO答案应该像这样。我会给你答案投票,因为这应该足以回答原始问题,并为那些想深入了解的人提供方向。 - Mark A. Donohoe
你的图片好像不见了。有没有可能直接上传它们?没有它们,答案就不如以前清晰了。 - Mark A. Donohoe
谢谢您的关注。但是就我所见,图片仍然被显示。 - Charles HETIER
我的错。在工作中有代理问题,某种原因导致他们阻止了 SO 上托管的图像。实际上,这些图像仍然存在,并且已经存在了两年。 - Mark A. Donohoe

5
FrameworkElement.Parent属性的文档指出,它可能为空,例如用于数据模板中创建的项。在这种情况下,建议使用FrameworkElement.TemplatedParent:

对于模板,模板的父对象最后将为空。为了超越这一点并进入应用实际模板的逻辑树,请使用TemplatedParent。

也许这是你遇到的情况?它对我有所帮助(如果Parent为空,则使用TemplateParent作为备选方案)。

虽然答案晚了,但它可能会帮助其他遇到与我相同错误的人。


如果这是真的,你刚刚帮我省了大量的工作!空值合并操作符来拯救了!! - Mark A. Donohoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接