Wpf:通用集合依赖属性

Posted

技术标签:

【中文标题】Wpf:通用集合依赖属性【英文标题】:Wpf: Generic collection dependency property 【发布时间】: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,那么你在设置它时做错了,或者你没有设置它。没有一些示例代码很难说。 【参考方案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 但不幸的是它也不起作用。仍然很好,所以我会更新问题。

以上是关于Wpf:通用集合依赖属性的主要内容,如果未能解决你的问题,请参考以下文章

设置DataContext后WPF依赖属性两种方式绑定不起作用[重复]

WPF非依赖属性绑定的问题

WPF中的依赖属性

WPF 依赖属性概念

WPF依赖属性

WPF依赖属性1