可观察到的LinkedList



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

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

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

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

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

前一个元素是通过绑定Relativesource.PreviousData发送的。

现在我的问题是,从转换器设置一个值(这显然是一件坏事),实际上让它工作后,一个常规的集合没有在其元素的顺序的概念,所以当我想在其余的中间添加一个音频部分时,显示是混乱的。

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

所以这种方法在很多层面上都是错误的。

这就是我开始谷歌和发现关于LinkedList。现在我想做一个类,基本上是一个可观察的LinkedList(我不需要它是通用的):

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正确…

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

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

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

谢谢!

附录,ViewModel看起来像:

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
    }

编辑:我想我会尝试结合托马斯和威尔的答案:我将使用组合(即我保持LinkedList的实例在我的自定义对象,而不是从它继承)和重新定义的方法是要使用(AddAfter, AddFirst等),我将调用OnNotifyPropertychanged后调用实际的LinkedList方法。这是一点工作,但我想不会有任何优雅的解决方案来解决我的问题…

现在,我制作了一个支持IEnumerable的自定义泛型类,并将其用作LinkedList<T>,唯一的区别是WPF会收到更改通知。

请注意,这个解决方案只适用于一个相当小的集合,我只需要管理大约30个元素的最大值,所以这对我来说很好,但每次你修改这个集合,它被认为是"重置"。

解决方案如下:

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

LinkedList<T>不是为继承而设计的:它的大多数方法不是虚拟的,所以没有什么可以重写的。如果您想重用它的实现并实现INotifyCollectionChanged,请使用组合,而不是继承。

但无论如何,实现一个可观察的链表是没有意义的,因为链表不支持通过索引随机访问,CollectionChanged通知只有在指定索引时才有用(除非你只提出NotifyCollectionChangedAction.Reset通知,但这不是很有效)

这是一个很好的解决方案,你只需要创建你自己的LinkedList实现。

LinkedList<T>没有实现任何链接列表接口,因此您可以自行选择方法/属性。我认为复制LinkedList<T>的公共方法和属性是一个很好的指南。这将允许您使用集合的实际实例作为后备存储。

我认为最终更简单的解决方案是提前计算开始和结束时间,并将它们作为属性添加到ViewModel上。特别是因为您说您可能在业务逻辑中需要此值。

听起来你有两个不同的问题。一个是管理要显示的项目列表,另一个是允许一个项目访问它的前一个和后一个项目。

这就是我如何处理它:将PreviousNext属性添加到项目类,在最初填充集合时设置它们,然后在我从列表中插入和删除项目时更新它们。

如果你真的想发疯,做一个通用的解决方案,你可以实现一个ILinkedListNode接口,然后子类ObservableCollection<T> where T : ILinkedListNode,覆盖各种插入和删除方法来更新项目的PreviousNext属性。如果我需要可重用的解决方案,我就会这样做。

但如果没有,我只是做一个视图模型类包装集合,将其暴露为Items属性,然后实现插入和删除命令,UI可以绑定到。

如果您创建了这样的类:

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

用new关键字重写方法应该没有任何问题。
注意,类本身有一个与链表类型匹配的TYPE说明符。

在这里是通用的但是你可以在。泛型只是意味着你可以在更多的地方使用这个东西,包括未来的项目。

相关内容

  • 没有找到相关文章

最新更新