我有一个像这样的对象模型:
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
答案 0 :(得分:3)
我强烈推荐使用MVVM,但是当谈到像这样的层次模型时,如果你愿意并且能够对模型类本身进行一些简单的修改,你可以省去很多悲伤。
当您编写“使其他计算属性无效”的属性和集合时,它会很快变得复杂。在我的应用程序中,我使用了一种基于属性的方法,它允许我使用DependsOn属性修饰属性,这样每当更改依赖属性时,也会为计算属性引发属性更改事件。在你的情况下,我认为你不需要全力以赴。
但我认为真正派上用场的一件事是一个源自ObservableCollection
的课程,由于目前缺少一个更好的术语,我会称之为ObservableCollectionEx
。它添加的一件事是ItemPropertyChanged
事件,它允许您将一个处理程序连接到它,并且只要集合中项目的属性发生更改,它就会被引发。这需要您在添加或删除项目时连接到INotifyPropertyChanged
。但是,一旦这个课程失控,级联的属性变化就变得轻而易举。
我建议您创建类似于以下内容的CarListViewModel(或任何您想要调用的内容)。请注意,最终结果是对象的层次结构,其中对读/写Part.Total属性的任何修改都会导致PropertyChanged事件的链式反应,这些事件会冒泡到ViewModel。以下代码并不完整。您需要提供INotifyPropertyChanged的实现。但最后你会看到我提到的ObservableCollectionEx。
编辑:我刚刚点击了原帖中的链接,发现你已经有了ObservableCollectionEx的实现。我选择使用InsertItem,RemoveItem等方法而不是OnCollectionChanged来挂钩/取消挂钩项事件,因为在其他实现中存在一个令人讨厌的问题 - 如果清除集合,则不再有取消挂钩的项目集合。 p>
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;
}