在UserControl中实现集合类型属性的标准方法

时间:2016-05-10 05:31:59

标签: c# wpf generics user-controls ienumerable

在开发具有集合类型属性的自己的UserControl时,Select CompanyName From tblCompany left join tblInvoice ON tblCompany.CompanyID = tblInvoice.CompanyID Where tblInvoice.CompanyID IS NULL 似乎非常合适,因为它会通知控件有关项目级别的更改。但是,基础ViewModel属性的类型为ObservableCollection<T>,虽然可以使用List<T>构建ObservableCollection<T>,但我不确定双向绑定在该方案中的工作方式。

我查看List<T>作为参考,但看起来MS家伙并不认为ItemsControl.ItemSource是出于他们的目的,而是生活在穷人的ObservableCollection上。我不确定IEnumerable如何支持项目级更改,因为ItemsControl没有提供此类通知。

我应该将IEnumerableIEnumerableList<T>用于我的收藏属性吗?我应该如何进行双向绑定和收集更改通知?

修改

我的VM包含名为ObservableCollection<T>的{​​{1}}类型的属性(List<Point>是一个复杂的模型级类型,但出于我们的目的,您可以将其视为X,Y坐标) 。 VM在启动时从模型中读取所有点,然后通过Points属性将它们公开给View层。

我的UserControl显示这些点,并允许用户添加新点或删除现有点。由于UserControl通常不直接绑定到VM,而是提供View可以绑定到底层VM属性的公共属性,因此我在控件中添加了一个名为Point的属性。在UserControl中Points后,我相应地更新MyPoints,但是也需要在底层VM中反映这些更改。

1 个答案:

答案 0 :(得分:1)

理想情况下,您希望VM拥有ObservableCollection - 这可以在每次需要更新时告诉视图。假设您有能力修改VM,我建议您这样做。如果您的VM未包含ObservableCollection,则您将无法接收项目级更新。

要启用双向绑定,您需要在View中使用依赖项属性(只有依赖项属性(和特别设计的类)才能接受绑定)

在您的视图中(假设它被称为MyControl):

// Provide a dependency property to enable binding
public static readonly DependencyProperty MyPointsProperty = DependencyProperty.Register( "MyPoints", typeof(ObservableCollection<Points>) ,new FrameworkPropertyMetadata( null, FrameworkPopertyMetatdataOptions.None, MyPointsPropertyChangedHandler ) );

// Provide a CLR property for convenience - this will not get called by the binding engine!
public ObservableCollection<Points> MyPoints {
    get { return (ObservableCollection<Points>) GetValue( MyPointsProperty ); } 
    set { SetValue( MyPointsProperty, value ); }
}

//Listen for changes to the dependency property (note this is a static method)
public static void MyPointsPropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) {
    MyControl me = obj as MyControl;
    if( me != null ) {
        // Call a non-static method on the object whose property has changed
        me.OnMyPointsChanged( (ObservableCollection<Points>) e.OldValue, (ObservableCollection<Points>) e.NewValue );
    }
}
// ...

// Listen for changes to the property (non-static) to register CollectionChanged handlers
protected virtual void OnMyPointsChanged(ObservableCollection<Points> oldValue, ObservableCollection<Points> newValue) {
    if(oldValue!=null){
        oldValue.CollectionChanged -= MyCollectionChangedHandler;
    }
    if( newValue!=null ) {
        newValue.CollectionChanged += MyCollectionChangedHandler;
    }
}

有了这个,你现在可以做到:

<ns:MyControl MyPoints="{Binding vmPointsCollection}" />

此代码不会按原样运行,但应作为实现提供绑定所需的必要功能的指南。

作为旁注,ItemsControl.ItemsSourceItemsControl.Items一起使用以产生整体效果,Items为ItemCollecton,其中包含CollectionChanged个事件。当您设置ItemsSourceItems被告知此情况时,请开始关注ItemsSource中的更改并通过其CollectionChanged事件报告。

编辑:这个控件的更好实现会有一个IEnumerable作为MyPoints集合的类型,当在处理程序中找到它时,检查它是否实现了INotifyCollectionChanged并适当地附加处理程序