【问题标题】:A DependencyProperty of Type ObservableCollection<T> set in XAML contains more items than there are declared. c#XAML 中设置的 ObservableCollection<T> 类型的 DependencyProperty 包含的项目比声明的项目多。 C#
【发布时间】:2017-04-18 17:37:41
【问题描述】:

我有一个 UWP-App,我在其中创建了一个模板化控件“括号”,其中包含一个声明如下的 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>

(注意:“Bracket”和“TextBlock”类都派生自“BaseControl”)
但是,当我尝试像这样在括号中添加括号时:

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

我得到一个异常:'元素已经是另一个元素的子元素'
经过一些调试,我发现在两个 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) 中,据说这是过时的,并且“收集行为现在是 XAML 类型系统的组成部分”。因此,我使用了一个集合,但它产生了不同的结果。
我该如何解决这个问题?或者我应该使用不同的方法来实现这个功能(也许通过使用 IAddChild 接口?

【问题讨论】:

  • addChildren() 是什么,它在做什么?我认为它根本不应该存在。
  • addChildren() 将其他一些 UIControls(不是 Content 中的那些)添加到 Control(呈现内容的开始和结束括号)

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


【解决方案1】:

该属性的每个实例的默认值,对于控件的每个实例,都是相同的 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 一起使用,否则这是习惯做法,但并非绝对必要。鉴于该属性通常会在不接触该设置器的情况下进行设置,但是,您永远不应该在其中放置任何额外的代码。当属性值更改时应该发生的任何事情都应该在依赖属性的PropertyChanged 处理程序中完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 2016-08-28
    相关资源
    最近更新 更多