MVVM ObservableCollection Bind TwoWay

巧了我就是萌 提交于 2020-01-17 08:37:10

问题


I have been working with MVVM and wondering how to make use of ObservableCollection to bind to an ItemsSource in TwoWay?

For example, I have an Custom Drawing UserControl called SmartDraw, in which the ItemsSource refers to the list of Custom Graphics and it is bound to an ObservableCollection Graphics in the View Model.

If I add a CustomGraphic in the view model, the ItemsSource in the SmartDraw will know that there is an addition of CustomGraphic and then do some other function calls. It is normal.

However, the SmartDraw is also a Canvas which enable user to draw graphics on it using mouse. The number of CustomGraphic will change according to the user drawing. So, how could I know the ObservableCollection is changed by the UI(SmartDraw)?

Here is my code:

ViewModel:

public ObservableCollection<CustomGraphic> Graphics { get; set; }

Thank you so much.


回答1:


Not sure whether this answers your question ... but here is how you would track changes to an observable collection in general.

To detect changes to an observable collection (not changes to properties of the items within the collection) you subscribe to the CollectionChanged event of the ObservableCollection.

private ObservableCollection<ViewModel> _collection;
public ObservableCollection<ViewModel> Collection {
    get { return _collection; }
    set {
        if (_collection != value) {
            // de-register on collection changed
            if (_collection != null)
                _collection.CollectionChanged -= this.Collection_CollectionChanged;

            _collection = value;

            // re-register on collection changed
            if (_collection != null)
                _collection.CollectionChanged += this.Collection_CollectionChanged;
    }
}

private void Collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
    switch (e.Action) {
    case NotifyCollectionChangedAction.Add:
            // e.NewItems contains the added items
    break;
    case NotifyCollectionChangedAction.Remove:
            // e.OldItems contains the removed items
            break;
        case NotifyCollectionChangedAction.Replace:
            // e.NewItems contains the new items, 
            // e.OldItems contains the removed items
            break;
        case NotifyCollectionChangedAction.Reset:
            // the collection has been cleared
            break;
    }
}

If you need to track changes to the objects within the collection you have to build a extended ObservableCollection that subscribes to the PropertyChanged events of the items and then raises an event if one of these properties has changed.

public class ObservableCollectionEx<TValue> : ObservableCollectionAddRange<TValue>
{

   public ObservableCollectionEx() : base() { }
   public ObservableCollectionEx(IEnumerable<TValue> values)
      : base(values)
   {
      this.EnsureEventWiring();
   }
   public ObservableCollectionEx(List<TValue> list)
      : base(list)
   {
      this.EnsureEventWiring();
   }

   public event EventHandler<ItemChangedEventArgs> ItemChanged;

   protected override void InsertItem(int index, TValue item)
   {
      base.InsertItem(index, item);

      var npc = item as INotifyPropertyChanged;
      if (npc != null)
         npc.PropertyChanged += this.HandleItemPropertyChanged;
   }

   protected override void RemoveItem(int index)
   {
      var item = this[index];

      base.RemoveItem(index);

      var npc = item as INotifyPropertyChanged;
      if (npc != null)
         npc.PropertyChanged -= this.HandleItemPropertyChanged;
   }

   protected override void SetItem(int index, TValue item)
   {
      var oldItem = this[index];

      base.SetItem(index, item);

      var npcOld = item as INotifyPropertyChanged;
      if (npcOld != null)
         npcOld.PropertyChanged -= this.HandleItemPropertyChanged;

      var npcNew = item as INotifyPropertyChanged;
      if (npcNew != null)
         npcNew.PropertyChanged += this.HandleItemPropertyChanged;
   }

   protected override void ClearItems()
   {
      var items = this.Items.ToList();

      base.ClearItems();

      foreach (var npc in items.OfType<INotifyPropertyChanged>().Cast<INotifyPropertyChanged>())
         npc.PropertyChanged -= this.HandleItemPropertyChanged;
   }


   private void HandleItemPropertyChanged(object sender, PropertyChangedEventArgs args)
   {
      if (typeof(TValue).IsAssignableFrom(sender.GetType()))
      {
         var item = (TValue)sender;
         var pos = this.IndexOf(item);
         this.OnItemChanged(item, pos, args.PropertyName);
      }
   }

   protected virtual void OnItemChanged(TValue item, int index, string propertyName)
   {
      if (this.ItemChanged != null)
         this.ItemChanged(this, new ItemChangedEventArgs(item, index, propertyName));
   }

   private void EnsureEventWiring()
   {
      foreach (var npc in this.Items.OfType<INotifyPropertyChanged>().Cast<INotifyPropertyChanged>())
      {
         npc.PropertyChanged -= this.HandleItemPropertyChanged;
         npc.PropertyChanged += this.HandleItemPropertyChanged;
      }
   }

   public class ItemChangedEventArgs : EventArgs
   {
      public ItemChangedEventArgs(TValue item, int index, string propertyName)
      {
         this.Item = item;
         this.Index = index;
         this.PropertyName = propertyName;
      }

      public int Index { get; private set; }
      public TValue Item { get; private set; }
      public string PropertyName { get; private set; }
   }
}


来源:https://stackoverflow.com/questions/9224917/mvvm-observablecollection-bind-twoway

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!