在XAML中设置的类型ObservableCollection <t>的DependencyProperty包含的项目多于声明的项目。 C#

时间:2017-04-18 17:37:41

标签: c# .net xaml uwp uwp-xaml

我有一个UWP-App,我在其中创建了一个模板控件&#39; Bracket&#39;它包含一个声明如下的DependencyProperty:

public ObservableCollection<BaseControl> Content
{
   get { return (ObservableCollection<BaseControl>)GetValue(ContentProperty); }
   set
   {
       SetValue(ContentProperty, value);
       addChildren();
   }
}

public static readonly DependencyProperty ContentProperty =
    DependencyProperty.Register(nameof(Content), typeof(ObservableCollection<BaseControl>), 
        typeof(Bracket), new PropertyMetadata(new ObservableCollection<BaseControl>()));

在XAML中使用像这样的控件时,一切正常:

<Math:Bracket>
    <Math:Bracket.Content>
         <Math:TextBlock Text="4" />
         <Math:TextBlock Text="x" />
    </Math:Bracket.Content>
</Math:Bracket>

(注意:两个类&#39; Bracket&#39;&#39; TextBlock&#39;都来自&#39; BaseControl&#39;)
但是,当我尝试在这样的支架中添加一个支架时:

<Math:Bracket>
    <Math:Bracket>
        <Math:Bracket.Content>
            <Math:TextBlock Text="4" />
            <Math:TextBlock Text="x" />
        </Math:Bracket.Content>
    </Math:Bracket>
</Math:Bracket>

我得到一个例外:&#39;元素已经是另一个元素的孩子&#39;
经过一些调试后我发现,在两个Bracket对象的每一个中,属性Content 包含3个元素:TextBlocks和内部Bracket。 我期望和我想要的是,外部支架的内容仅包含内部支架,内部支架的内容仅包含两个文本块。
自小组以来,例如。一个StackPanel有一个非常相似的函数(它包含子节点,其中一个子节点也可以是StackPanel类型)所以我查找了Panel类的代码。但是,似乎使用了IAddChild接口 在msdn文档(https://msdn.microsoft.com/de-de/library/system.windows.markup.iaddchild(v=vs.110).aspx)中,据说这是过时的,并且&#34; Collection行为现在是XAML类型系统的一部分&#34;。因此我使用了一个集合,但它产生了不同的结果 我该如何解决这个问题?或者我应该使用不同的方法来实现此功能(可能使用IAddChild接口?

1 个答案:

答案 0 :(得分:0)

对于每个控件实例,此属性的每个实例的默认值是相同的ObservableCollection

public static readonly DependencyProperty ContentProperty =
    DependencyProperty.Register(nameof(Content), typeof(ObservableCollection<BaseControl>), 
        typeof(Bracket), new PropertyMetadata(new ObservableCollection<BaseControl>()));

对于每个实例,XAML解析器/将其子项添加到其Content。但它总是相同的集合!所以第一个得到它的孩子,这导致第二个得到那些孩子。然后它添加了第二个孩子的孩子,并且他们两个都引用了同一个集合。

您不能为PrpertyMetadata提供引用类型的默认非空值,除非它像System.String这样是不可变的。您可以共享相同的字符串值。

使用null作为默认值,并在构造函数中使用新集合进行初始化,因此Bracket的每个实例都有自己的子集合。

public Bracket()
{
    Content = new ObservableCollection<BaseControl>();
}


public static readonly DependencyProperty ContentProperty =
    DependencyProperty.Register(nameof(Content), typeof(ObservableCollection<BaseControl>), 
        typeof(Bracket), new PropertyMetadata(null));

顺便说一句,你的setter中的addChildren();看起来非常可疑,但是setter可能永远不会被调用。 XAML不会打电话给它。框架直接调用DependencyObject.SetValue()。放入断点看看。除非您自己的代码想要将该传统Content属性与get和set一起使用,否则这是惯例,但并非严格必要。鉴于该属性通常会设置而不触及该setter ,但是,您永远不应该在其中添加任何额外的代码。属性值更改时应该发生的任何事情都应该在依赖项属性的PropertyChanged处理程序中完成。