【问题标题】:Best way to databind (WPF/Silverlight style) to an aggregation of a property on a collection inside a collection?将数据绑定(WPF/Silverlight 样式)到集合内集合上的属性聚合的最佳方法?
【发布时间】:2010-07-01 02:26:14
【问题描述】:

我有一个这样的对象模型:

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来处理总价的计算和显示,请问怎样才能干净利落?

谢谢!

-迈克

【问题讨论】:

    标签: c# wpf silverlight data-binding


    【解决方案1】:

    我强烈推荐 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;
    
    }
    

    【讨论】:

      猜你喜欢
      • 2012-06-14
      • 2011-07-11
      • 1970-01-01
      • 2014-10-05
      • 1970-01-01
      • 2011-01-02
      • 1970-01-01
      • 2010-12-10
      • 1970-01-01
      相关资源
      最近更新 更多