可观察链表 [英] Observable LinkedList

查看:24
本文介绍了可观察链表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 WPF 应用程序中,我有一个 ItemsControl,其项目值取决于显示的前一项.

In my WPF app, I have an ItemsControl whose items values are dependant upon the previous item displayed.

ViewModel 是一个分割成可变长度部分的音频文件,我需要以这种方式显示它,右边显示一个 DateTime,这就是我需要计算的(我只知道每个部分的长度,我需要计算它的实际开始和结束时间,以及在ItemsControl上的位置).

The ViewModel is an audio file split into parts of variable length, and i need to display it in such manner, with a DateTime displayed on the right, and that's what i need to calculate (I only know each part's length, i need to calculate the actual time it starts and ends, and the position on the ItemsControl).

--
  ----
      ------------
                  --
                    --------------------

我的第一个方法是使用 ObservableCollection<MyviewModel> 但很快就发生了一些可怕的事情:

My first approach was to use an ObservableCollection<MyviewModel> but soon enough some horrors occured :

5 路多重绑定,其中 IMultiValueConverter 我会计算要返回的值并将 DataContext 的属性设置为该值,因为我在运行时只知道前一个元素.

5-way multibinding in which's IMultiValueConverter I'd calculate the value to return and set a property of the DataContext to that value, because I only knew the previous element at runtime.

前一个元素是使用 Relativesource.PreviousData 上的绑定发送的.

The previous element was sent using a binding on Relativesource.PreviousData.

现在我的问题是,在从 Converter 设置一个值(这显然是一件坏事)并实际让它工作后,常规 Collection 在其元素中没有顺序概念,所以当进一步向下时当我想在其余部分中间添加音频部分时,显示混乱.

Now my problem is that after setting a value from the Converter (which is obviously a bad thing), and actually getting it to work, a regular Collection doesn't have a notion of order in its elements, so when further down the road when i want to add an audio part in the middle of the rest, the display is messed up.

此外,当我实现更多业务逻辑时,我可能需要访问在此转换器中计算的音频部分的开始和结束,如果还没有显示怎么办...?

Furthermore, when I'll implement more business logic, I may need to access the audio parts's start and end that are calculated in this converter, and what if it's not displayed yet...?

所以这种方法在几个层面上都是错误的.

So that approach was wrong on several levels.

那是我开始使用谷歌搜索并发现 LinkedList 的地方.现在我正在尝试创建一个基本上是 Observable LinkedList 的类(我不需要它是通用的):

That's where i started googling and found out about LinkedList. Now I'm trying to make a class that is basically an Observable LinkedList (I don't need it to be generic):

public class ObservableSegmentLinkedList : LinkedList<MyViewModel>, INotifyCollectionChanged
    {
        //Overrides ???

        #region INotifyCollectionChanged Members

        public event NotifyCollectionChangedEventHandler CollectionChanged;
        public void OnNotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, e);
            }
        }

        #endregion
    }

问题的核心是我无法覆盖修改集合的方法(Addfirst、AddLast 等),所以我无法正确调用 OnNotifyCollectionChanged...

And the heart of the problem is that i can't override the methods that modify the collection (Addfirst, AddLast etc), so i can't call OnNotifyCollectionChanged properly...

所以我想我可以为这些方法中的每一个进行重载,但这听起来很讨厌......

So I'm thinking i could make overloads for each of these methods, but that sounds quite nasty...

简而言之:我需要某种集合,其中每个项目都知道前一个项目的详细信息,以便计算自己的一个属性.

有什么线索吗?这是一个很好的解决方案吗?

Any clues? is this even a good solution?

谢谢!

附录,ViewModel 长这样:

Appendix, the ViewModel looks like:

public class MyViewModel : INotifyPropertyChanged
    {
        private DateTime m_SegmentLength;
        public DateTime SegmentLength
        {
            get { return m_SegmentLength; }
            set
            {
                m_SegmentLength = value;
                NotifyPropertyChanged("SegmentLength");
            }
        }

        private DateTime m_SegmentAdvert;
        public DateTime SegmentAdvert
        {
            get { return m_SegmentAdvert; }
            set
            {
                m_SegmentAdvert = value;
                NotifyPropertyChanged("SegmentAdvert");
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String prop)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        #endregion
    }

我想我会尝试结合 Thomas 和 Will 的答案:我将使用组合(即我在我的自定义对象中保留 LinkedList 的一个实例,而不是从它继承)并重新定义要使用的方法(AddAfter、AddFirst 等),其中我将在调用实际的 LinkedList 方法后调用 OnNotifyPropertychanged.这有点工作,但我想我的问题不会有任何优雅的解决方案......

i think i will try to combine Thomas and Will's answers: I'll use composition (i.e I keep an instance of LinkedList in my custom object instead of inheriting from it) and redefine methods that are meant to be used (AddAfter, AddFirst etc) in which i'll just call OnNotifyPropertychanged after calling the actual LinkedList method. It's a bit of work but i guess there won't be any elegant solution to my problem...

推荐答案

现在,我创建了一个支持 IEnumerable 的自定义泛型类,并被用作 LinkedList;,唯一的区别是 WPF 会收到更改通知.

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.

请注意,此解决方案仅适用于相当小的集合,我最多只需要管理大约 30 个元素,因此对我来说很好,但是每次修改此集合时,它都会被视为重置".

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".

解决办法如下:

    /// <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
    }

正如@AndrewS 在评论中提到的,LinkedListNode 应该替换为自定义类,该类从其 List 属性返回 ObservableLinkedList.

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

这篇关于可观察链表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆