List firing Event on Change

前端 未结 5 672
说谎
说谎 2020-12-10 03:37

I created a Class EventList inheriting List which fires an Event each time something is Added, Inserted or Removed:

public          


        
相关标签:
5条回答
  • 2020-12-10 03:44

    I have a Solution for when someone calls the Generic method from IList.add(object). So that you also get notified.

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace YourNamespace
    {
        public class ObjectDoesNotMatchTargetBaseTypeException : Exception
        {
            public ObjectDoesNotMatchTargetBaseTypeException(Type targetType, object actualObject)
                : base(string.Format("Expected base type ({0}) does not match actual objects type ({1}).",
                    targetType, actualObject.GetType()))
            {
            }
        }
    
        /// <summary>
        /// Allows you to react, when items were added or removed to a generic List.
        /// </summary>
        public abstract class NoisyList<TItemType> : List<TItemType>, IList
        {
            #region Public Methods
            /******************************************/
            int IList.Add(object item)
            {
                CheckTargetType(item);
                Add((TItemType)item);
                return Count - 1;
            }
    
            void IList.Remove(object item)
            {
                CheckTargetType(item);
                Remove((TItemType)item);
            }
    
            public new void Add(TItemType item)
            {
                base.Add(item);
                OnItemAdded(item);
            }
    
            public new bool Remove(TItemType item)
            {
                var result = base.Remove(item);
                OnItemRemoved(item);
                return result;
            }
            #endregion
    
            # region Private Methods
            /******************************************/
            private static void CheckTargetType(object item)
            {
                var targetType = typeof(TItemType);
                if (item.GetType().IsSubclassOf(targetType))
                    throw new ObjectDoesNotMatchTargetBaseTypeException(targetType, item);
            }
            #endregion
    
            #region Abstract Methods
            /******************************************/
            protected abstract void OnItemAdded(TItemType addedItem);
    
            protected abstract void OnItemRemoved(TItemType removedItem);
            #endregion
        }
    }
    
    0 讨论(0)
  • 2020-12-10 03:45

    If an ObservableCollection is not the solution for you, you can try that:

    A) Implement a custom EventArgs that will contain the new Count attribute when an event will be fired.

    public class ChangeListCountEventArgs : EventArgs
    {
        public int NewCount
        {
            get;
            set;
        }
    
        public ChangeListCountEventArgs(int newCount)
        {
            NewCount = newCount;
        }
    }
    

    B) Implement a custom List that inherits from List and redefine the Count attribute and the constructors according to your needs:

    public class CustomList<T> : List<T>
    {
        public event EventHandler<ChangeListCountEventArgs> ListCountChanged;
    
        public new int Count
        {
            get
            {
                ListCountChanged?.Invoke(this, new ChangeListCountEventArgs(base.Count));
                return base.Count;
            }
        }
    
        public CustomList()
        { }
    
        public CustomList(List<T> list) : base(list)
        { }
    
        public CustomList(CustomList<T> list) : base(list)
        { }
    }
    

    C) Finally subscribe to your event:

    var myList = new CustomList<YourObject>();
    myList.ListCountChanged += (obj, e) => 
    {
        // get the count thanks to e.NewCount
    };
    
    0 讨论(0)
  • 2020-12-10 03:49

    If you do not want to or can not convert to an Observable Collection, try this:

    public class EventList<T> : IList<T> /* NOTE: Changed your List<T> to IList<T> */
    {
      private List<T> list; // initialize this in your constructor.
      public event ListChangedEventDelegate ListChanged;
      public delegate void ListChangedEventDelegate();
    
      private void notify()
      {
          if (ListChanged != null
              && ListChanged.GetInvocationList().Any())
          {
            ListChanged();
          }
      }
    
      public new void Add(T item)
      {
          list.Add(item);
          notify();
      }
    
      public List<T> Items {
        get { return list; } 
        set {
          list = value; 
          notify();
        }
      }
      ...
    }
    

    Now, for your property, you should be able to reduce your code to this:

    public EventList List
    {
      get { return m_List.Items; }
      set
      {
          //m_List.ListChanged -= List_ListChanged;
    
          m_List.Items = value;
    
          //m_List.ListChanged += List_ListChanged;
          //List_ListChanged();
      }
    }
    

    Why? Setting anything in the EventList.Items will call your private notify() routine.

    0 讨论(0)
  • 2020-12-10 03:54

    ObservableCollection is a List with a CollectionChanged event

    ObservableCollection.CollectionChanged Event

    For how to wire up the event handler see answer from Patrick. +1

    Not sure what you are looking for but I use this for a collection with one event that fires on add, remove, and change.

    public class ObservableCollection<T>: INotifyPropertyChanged
    {
        private BindingList<T> ts = new BindingList<T>();
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        // This method is called by the Set accessor of each property. 
        // The CallerMemberName attribute that is applied to the optional propertyName 
        // parameter causes the property name of the caller to be substituted as an argument. 
        private void NotifyPropertyChanged( String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
        public BindingList<T> Ts
        {
            get { return ts; }
            set
            {
                if (value != ts)
                {
                    Ts = value;
                    if (Ts != null)
                    {
                        ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
                        {
                            OnListChanged(this);
                        };
                    }
                    NotifyPropertyChanged("Ts");
                }
            }
        }
    
        private static void OnListChanged(ObservableCollection<T> vm)
        {
            // this will fire on add, remove, and change
            // if want to prevent an insert this in not the right spot for that 
            // the OPs use of word prevent is not clear 
            // -1 don't be a hater
            vm.NotifyPropertyChanged("Ts");
        }
    
        public ObservableCollection()
        {
            ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
            {
                OnListChanged(this);
            };
        }
    }
    
    0 讨论(0)
  • 2020-12-10 04:07

    You seldom create a new instance of a collection class in a class. Instantiate it once and clear it instead of creating a new list. (and use the ObservableCollection since it already has the INotifyCollectionChanged interface inherited)

    private readonly ObservableCollection<T> list;
    public ctor() {
        list = new ObservableCollection<T>();
        list.CollectionChanged += listChanged;
    }
    
    public ObservableCollection<T> List { get { return list; } }
    
    public void Clear() { list.Clear(); }
    
    private void listChanged(object sender, NotifyCollectionChangedEventArgs args) {
       // list changed
    }
    

    This way you only have to hook up events once, and can "reset it" by calling the clear method instead of checking for null or equality to the former list in the set accessor for the property.


    With the changes in C#6 you can assign a get property from a constructor without the backing field (the backing field is implicit)

    So the code above can be simplified to

    public ctor() {
        List = new ObservableCollection<T>();
        List.CollectionChanged += OnListChanged;
    }
    
    public ObservableCollection<T> List { get; }
    
    public void Clear()
    {
        List.Clear();
    }
    
    private void OnListChanged(object sender, NotifyCollectionChangedEventArgs args)
    {
       // react to list changed
    }
    
    0 讨论(0)
提交回复
热议问题