【问题标题】:Wpf: Generic collection dependency propertyWpf:通用集合依赖属性
【发布时间】:2021-02-04 06:30:25
【问题描述】:

我创建了具有通用集合依赖属性的自定义控件。 但是,每当此属性从 xaml 更改时,setter 中的视图模型中的值为 null。

自定义控件:

   public IList A       
   {
            get { return (IList)GetValue(AProperty); }
            set { SetValue(AProperty, value); }
   }

   public static readonly DependencyProperty AProperty =
            DependencyProperty.Register(nameof(A), typeof(IList), typeof(CustomControl), new PropertyMetadata(new List<object>()));

视图模型:

  List<B> collectionB;
  public List<B> CollectionB
        {
            get { return collectionB; }
            set
            {
                if (collectionB == value) return;
                collectionB = value;
            }
        }
        

如果我将 CollectionB 的类型更改为 List 它工作正常。

【问题讨论】:

  • 您还没有向我们展示“从 xaml 更改”的确切含义。有没有像A="{Binding CollectionB, Mode=TwoWay}" 这样的绑定?以及控件是如何改变集合的?
  • 绑定是 A="{Binding CollectionB, Mode=OneWayToSource}"
  • 默认值(请参阅我在答案中的备注)类型为List&lt;object&gt;。这不能分配给List&lt;B&gt; 类型的源属性。 OneWayToSource 在这里不是一个非常适合的方法。根本不要设置默认值,而是使用双向绑定。
  • @AdamStawarek:当然,您可以将IList 属性设置为List&lt;A&gt;。如果属性仍然是null,那么你在设置它时做错了,或者你没有设置它。没有一些示例代码很难说。

标签: c# wpf dependency-properties


【解决方案1】:

集合属性的最通用类型是IEnumerable - 例如,参见ItemsControl.ItemsSource 属性。

还要注意,为集合类型依赖属性设置非空默认值是有问题的,因为默认情况下,所属类的所有实例都将在同一个集合实例上运行。将元素添加到一个控件的 A 属性会更改所有其他控件实例的集合。

你应该像这样声明属性:

public IEnumerable A       
{
    get { return (IEnumerable)GetValue(AProperty); }
    set { SetValue(AProperty, value); }
}

public static readonly DependencyProperty AProperty =
    DependencyProperty.Register(
        nameof(A), typeof(IEnumerable), typeof(CustomControl));

如果您确实需要一个非空默认值,请将其添加到控件的构造函数中:

SetCurrentValue(AProperty, new List<object>());

更新:使用 OneWayToSource 绑定集合类型属性,如

<local:CustomControl A="{Binding CollectionB, Mode=OneWayToSource}" />

只有在 A 的默认值与 Binding 的源属性赋值兼容时才能工作,这不适用于 List&lt;object&gt;List&lt;B&gt;

您根本不应该设置默认值,而是使用双向绑定

<local:CustomControl A="{Binding CollectionB, Mode=TwoWay}" />

private List<B> collectionB = new List<B>();

public List<B> CollectionB
{
    get { return collectionB; }
    set { collectionB = value; }
}

或者只是

public List<B> CollectionB { get; set; } = new List<B>();

【讨论】:

  • 不幸的是,使用 IEnumerable 代替 IList 并不能解决此问题,并且 value 仍然为 null。
【解决方案2】:

这是因为IList&lt;T&gt; 没有实现IList,所以从一种类型转换到另一种类型会失败。

如果您想要与A 绑定,那么您必须将其绑定到同样实现IList 的东西

【讨论】:

  • 我已将 IList 更改为 List 作为第二个实现 IList 但不幸的是它也不起作用。仍然很好,所以我会更新问题。
猜你喜欢
  • 1970-01-01
  • 2013-02-07
  • 1970-01-01
  • 2014-05-23
  • 1970-01-01
  • 2013-01-27
  • 1970-01-01
相关资源
最近更新 更多