XAML 集合填充创建重复项

XAML collection filling creates duplicates

我创建了一个 UserControl,它通过其 ContentProperty 属性接收对象集合,并通过为每个对象创建一个 ContentControl 将它们布置在屏幕上。在此示例中,这些对象是字符串以使其简单,但我的实际应用程序使用更复杂的对象和一些逻辑来选择足够的 DataTemplates;布局也更复杂。

此屏幕截图显示了我的目标:

但我得到的是这个:

问题是 MyControl 的每个实例都被赋予了分配给它们的所有字符串,而不是每个实例只获得专门分配给它的字符串。

Window 代码:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:local="clr-namespace:WpfApp1">
    <StackPanel Orientation="Horizontal">
        <local:MyControl Margin="10">
            <sys:String>AAA</sys:String>
            <sys:String>BBB</sys:String>
            <sys:String>CCC</sys:String>
        </local:MyControl>
        <local:MyControl Margin="10">
            <sys:String>DDD</sys:String>
            <sys:String>EEE</sys:String>
            <sys:String>FFF</sys:String>
        </local:MyControl>
        <local:MyControl Margin="10">
            <sys:String>GGG</sys:String>
            <sys:String>HHH</sys:String>
            <sys:String>III</sys:String>
        </local:MyControl>
    </StackPanel>
</Window>

用户控件代码:

[ContentProperty(nameof(MyProperty))]
public class MyControl : UserControl, IAddChild
{
   public static readonly DependencyProperty MyPropertyProperty =
      DependencyProperty.Register(nameof(MyProperty), typeof(IList), typeof(MyControl), new PropertyMetadata(new List<object>()));
   public IList MyProperty
   {
      get { return (IList)GetValue(MyPropertyProperty); }
      set { SetValue(MyPropertyProperty, value); }
   }

   public MyControl()
   {
      var sp = new StackPanel();
      Content = sp;

      Loaded += (_, __) =>
      {
         foreach (string e in MyProperty)
            sp.Children.Add(new ContentControl { Content = e });
      };
   }
}

您的 UserControl 重用在 PropertyMetadata 中作为默认值创建的 相同 列表实例。来自集合类型依赖属性的documentation

If your property is a reference type, the default value specified in dependency property metadata is not a default value per instance; instead it is a default value that applies to all instances of the type.

To correct this problem, you must reset the collection dependency property value to a unique instance, as part of the class constructor call.

要在您的代码中执行此操作,请像这样在构造函数中创建并设置一个新的列表实例。

public MyControl()
{
   MyProperty = new List<object>();

   // .. rest of the constructor.
}

额外答案:如果您使用的集合类型 属性 不应从控件外部写入,您可以考虑像下面这样创建依赖关系 属性 read-only。

[ContentProperty(nameof(MyProperty))]
public class MyControl : UserControl, IAddChild
{
   private static readonly DependencyPropertyKey MyPropertyPropertyKey =
      DependencyProperty.RegisterReadOnly(nameof(MyProperty), typeof(IList),
         typeof(MyControl), new PropertyMetadata(new List<object>()));

   public static readonly DependencyProperty MyPropertyProperty =
      MyPropertyPropertyKey.DependencyProperty;

   public IList MyProperty => (IList)GetValue(MyPropertyProperty);

   public MyControl()
   {
      SetValue(MyPropertyPropertyKey, new List<object>());

      var sp = new StackPanel();
      Content = sp;

      Loaded += (_, __) =>
      {
         foreach (string e in MyProperty)
            sp.Children.Add(new ContentControl { Content = e });
      };
   }
}