2010-07-01 52 views
0

我有一個對象模型是這樣的:數據綁定(WPF/Silverlight樣式)到集合中集合的屬性聚合的最佳方法?

class Car 
{ 
    public string Manufacturer; 
    public int Mileage; 
    public ObservableCollection<Part> Parts; 
} 

class Part 
{ 
    public string Name; 
    public int Price; 
} 

我想顯示「所有我的車」的總價格給用戶。我想用DataBinding(WPF/Silverlight/XAML)完成這個任務。這裏的代碼排序我想寫:

class MyWindow : Window 
{ 
    public MyWindow() 
    { 
     ObservableCollection<Car> myCars = CreateCars(); // Create a collection of cars 

     // Let the user edit the collection of Cars 
     this.DataGrid.DataContext = myCars; 

     // Bind to the grand total Price of all Parts in each Car. 
     // Should update text automatically if... 
     // 1) The Price of an individual Part changes 
     // 2) A Part is added or removed from a Car 
     // 3) A Car is added or removed from myCars collection 
     this.TotalPriceOfMyCarsLabel.Text = ??? // How? 
    } 
} 

的MVVM的做法似乎是分類模式,將是有用這裏,但我想不出來實現它的最好辦法。我的對象模型可以被修改,例如,添加INotifyPropertyChanged等。

我試圖寫的代碼本着ObservableCollection that also monitors changes on the elements in collection的精神,但代碼變得相當長,所有OnCollectionChanged和OnPropertyChanged事件處理程序都在各處飛行。

我真的想用DataBinding來處理總價格的計算和顯示,我該如何以一種乾淨的方式來做到這一點?

謝謝!

-Mike

回答

3

我強烈建議MVVM但是當涉及到分層模型這樣,你可以,如果你願意和能夠做一些簡單的修改模型類本身保存自己很多痛苦。

當您編寫使其他計算屬性「無效」的屬性和集合時,它會變得很複雜。在我的應用程序中,我使用了基於屬性的方法,該方法允許使用DependsOn屬性修飾屬性,以便每當相關屬性發生更改時,也會爲計算屬性引發屬性更改事件。在你的情況下,我認爲你不需要像這樣全力以赴。

但我認爲有一件事真的會派上用場,那就是ObservableCollection,因爲缺乏更好的術語,我現在打電話給ObservableCollectionEx。它添加的一件事是一個ItemPropertyChanged事件,它允許您將一個處理程序連接到它,並且只要集合中某個項目的屬性發生更改,就會引發該事件。這要求您每次添加或刪除項目時都連接到INotifyPropertyChanged。但是,一旦這個課程不受歡迎,級聯的財產變化就變得輕而易舉。

我建議你創建一個CarListViewModel(或者任何你想要調用它的)類似於下面的東西。請注意,最終結果是對象的層次結構,其中對讀/寫Part.Total屬性進行的任何修改都會導致PropertyChanged事件的連鎖反應,這些事件會冒泡到ViewModel。下面的代碼並不完全完整。您需要提供INotifyPropertyChanged的實現。但最後你會看到我提到的ObservableCollectionEx。

編輯:我剛纔點擊了原來的文章中的鏈接,並意識到你已經有了一個ObservableCollectionEx的實現。我選擇使用InsertItem,RemoveItem等方法而不是OnCollectionChanged來掛鉤/解除項目事件,因爲在其他實現中存在一個令人討厭的問題 - 如果清除該集合,則不再需要解除項目集合。

class CarListViewModel : INotifyPropertyChanged { 

    public CarListViewModel() { 

     Cars = new ObservableCollectionEx<Car>(); 
     Cars.CollectionChanged += (sender,e) => OnPropertyChanged("Total"); 
     Cars.ItemPropertyChanged += (sender,e) => { 
      if (e.PropertyName == "Total") { 
       OnPropertyChanged("Total"); 
      } 
     } 

    } 

    public ObservableCollectionEx<Car> Cars { 
     get; 
     private set; 
    } 

    public decimal Total { 
     get { 
      return Cars.Sum(x=>x.Total); 
     } 
    } 

} 

class Car : INotifyPropertyChanged { 

    public Car() { 
     Parts = new ObservableCollectionEx<Part>(); 
     Parts.CollectionChanged += (sender,e) => OnPropertyChanged("Total"); 
     Parts.ItemPropertyChanged += (sender,e) => { 
      if (e.PropertyName == "Total") { 
       OnPropertyChanged("Total"); 
      } 
     } 
    } 

    public ObservableCollectionEx<Part> Parts { 
     get; 
     private set; 
    } 

    public decimal Total { 
     get { 
      return Parts.Sum(x=>x.Total); 
     } 
    } 

} 

class Part : INotifyPropertyChanged { 

    private decimal _Total; 

    public decimal Total { 
     get { return _Total; } 
     set { 
      _Total = value; 
      OnPropertyChanged("Total"); 
     } 
    } 

} 

class ObservableCollectionEx<T> : ObservableCollection<T> 
    where T: INotifyPropertyChanged 
{ 

    protected override void InsertItem(int index, T item) { 
     base.InsertItem(index, item); 
     item.PropertyChanged += Item_PropertyChanged; 
    } 

    protected override void RemoveItem(int index) { 
     Items[index].PropertyChanged -= Item_PropertyChanged; 
     base.RemoveItem(index); 
    } 

    protected override void ClearItems() { 
     foreach (T item in Items) { 
      item.PropertyChanged -= Item_PropertyChanged; 
     } 
     base.ClearItems(); 
    } 

    protected override void SetItem(int index, T item) { 

     T oldItem = Items[index]; 
     T newItem = item; 

     oldItem.PropertyChanged -= Item_PropertyChanged; 
     newItem.PropertyChanged += Item_PropertyChanged; 

     base.SetItem(index, item); 

    } 

    private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { 
     var handler = ItemPropertyChanged; 
     if (handler != null) { handler(sender, e); } 
    } 

    public event PropertyChangedEventHandler ItemPropertyChanged; 

}