Observable LinkedList

大憨熊 提交于 2019-12-01 18:33:53

Ok now, I made a custom generic class that supports IEnumerable and is used as if it was a LinkedList<T>, with the only difference that WPF gets notified of the changes.

Please note that this solution only works for a reasonably small collection, I only have to manage around 30 elements max, so it's fine for me, but everytime you modify this collection, it is considered "Reset".

Here goes the solution:

    /// <summary>
    /// This class is a LinkedList that can be used in a WPF MVVM scenario. Composition was used instead of inheritance,
    /// because inheriting from LinkedList does not allow overriding its methods.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ObservableLinkedList<T> : INotifyCollectionChanged, IEnumerable
    {
        private LinkedList<T> m_UnderLyingLinkedList;

        #region Variables accessors
        public int Count
        {
            get { return m_UnderLyingLinkedList.Count; }
        }

        public LinkedListNode<T> First
        {
            get { return m_UnderLyingLinkedList.First; }
        }

        public LinkedListNode<T> Last
        {
            get { return m_UnderLyingLinkedList.Last; }
        }
        #endregion

        #region Constructors
        public ObservableLinkedList()
        {
            m_UnderLyingLinkedList = new LinkedList<T>();
        }

        public ObservableLinkedList(IEnumerable<T> collection)
        {
            m_UnderLyingLinkedList = new LinkedList<T>(collection);
        }
        #endregion

        #region LinkedList<T> Composition
        public LinkedListNode<T> AddAfter(LinkedListNode<T> prevNode, T value)
        {
            LinkedListNode<T> ret = m_UnderLyingLinkedList.AddAfter(prevNode, value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)
        {
            m_UnderLyingLinkedList.AddAfter(node, newNode);
            OnNotifyCollectionChanged();
        }

        public LinkedListNode<T> AddBefore(LinkedListNode<T> node, T value)
        {
            LinkedListNode<T> ret = m_UnderLyingLinkedList.AddBefore(node, value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
        {
            m_UnderLyingLinkedList.AddBefore(node, newNode);
            OnNotifyCollectionChanged();
        }

        public LinkedListNode<T> AddFirst(T value)
        {
            LinkedListNode<T> ret = m_UnderLyingLinkedList.AddFirst(value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void AddFirst(LinkedListNode<T> node)
        {
            m_UnderLyingLinkedList.AddFirst(node);
            OnNotifyCollectionChanged();
        }

        public LinkedListNode<T> AddLast(T value)
        {
            LinkedListNode<T> ret = m_UnderLyingLinkedList.AddLast(value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void AddLast(LinkedListNode<T> node)
        {
            m_UnderLyingLinkedList.AddLast(node);
            OnNotifyCollectionChanged();
        }

        public void Clear()
        {
            m_UnderLyingLinkedList.Clear();
            OnNotifyCollectionChanged();
        }

        public bool Contains(T value)
        {
            return m_UnderLyingLinkedList.Contains(value);
        }

        public void CopyTo(T[] array, int index)
        {
            m_UnderLyingLinkedList.CopyTo(array, index);
        }

        public bool LinkedListEquals(object obj)
        {
            return m_UnderLyingLinkedList.Equals(obj);
        }

        public LinkedListNode<T> Find(T value)
        {
            return m_UnderLyingLinkedList.Find(value);
        }

        public LinkedListNode<T> FindLast(T value)
        {
            return m_UnderLyingLinkedList.FindLast(value);
        }

        public Type GetLinkedListType()
        {
            return m_UnderLyingLinkedList.GetType();
        }

        public bool Remove(T value)
        {
            bool ret = m_UnderLyingLinkedList.Remove(value);
            OnNotifyCollectionChanged();
            return ret;
        }

        public void Remove(LinkedListNode<T> node)
        {
            m_UnderLyingLinkedList.Remove(node);
            OnNotifyCollectionChanged();
        }

        public void RemoveFirst()
        {
            m_UnderLyingLinkedList.RemoveFirst();
            OnNotifyCollectionChanged();
        }

        public void RemoveLast()
        {
            m_UnderLyingLinkedList.RemoveLast();
            OnNotifyCollectionChanged();
        }
        #endregion

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public void OnNotifyCollectionChanged()
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (m_UnderLyingLinkedList as IEnumerable).GetEnumerator();
        }

        #endregion
    }

As mentionned by @AndrewS in the comments, LinkedListNode should be replaced with a custom class that returns an ObservableLinkedList from its List property.

LinkedList<T> isn't designed for inheritance: most of its methods are not virtual, so there is nothing to override. If you want to reuse its implementation and implement INotifyCollectionChanged, use composition, not inheritance.

But anyway, it wouldn't make sense to implement an observable linked list, because a linked list doesn't support random access by index, and CollectionChanged notifications are only useful if you specify an index (unless you only raise NotifyCollectionChangedAction.Reset notifications, but then it's not very efficient)

It is a good solution, you'll just have to create your own implementation of a LinkedList.

LinkedList<T> does not implement any linky listy interface, so you are on your own as to the methods/properties. I suppose a good guide would be to duplicate the public methods and properties of LinkedList<T>. This would allow you to use an actual instance of the collection as your backing store.

I think that eventually the simpler solution would be to calculate the start and end time beforehand and add them as properties on the ViewModel. Especially since you say that you may need this value in your business logic.

It sounds to me like you have two different issues. One is managing a list of items for display, and the other is allowing an item to access its preceding and following items.

That's how I'd approach it: add Previous and Next properties to the item class, set them when initially populating the collection, and then updating them when I insert and remove items from the list.

If you really want to go nuts and make a generic solution, you could implement an ILinkedListNode interface, and then subclass ObservableCollection<T> where T : ILinkedListNode, overriding the various insert and remove methods to update the items' Previous and Next properties. That's what I'd do if I needed the solution to be reusable.

But if not, I'd just make a view model class that wrapped the collection, exposing it as an Items property, and then implemented insert and remove commands that the UI can bind to.

If you had made your class like this:

public class ObservableSegmentLinkedList<T> : LinkedList<T>, INotifyCollectionChanged 
{
...
public new void AddFirst(T value)
{
.. do something to make it your own - you can still call the base method so in effect you override it with the new keyword.
}
}

You should not have any problem overriding the methods with new keyword.
Notice the Class itself has a TYPE Specifier that matches your Linked List Type.

I made it generic here but you can put whatever you want between those < MyType > . Generic just means you can use this thing in a lot more places than 1 including a future project.

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