Наблюдаемый LinkedList - PullRequest
       45

Наблюдаемый LinkedList

4 голосов
/ 09 августа 2011

В моем приложении WPF у меня есть ItemsControl, значения элементов которого зависят от предыдущего элемента , отображаемого .

ViewModel - это аудиофайл, разбитый на части переменной длины, и мне нужно отобразить его таким образом, с DateTime, отображаемым справа, и это то, что мне нужно рассчитать (я знаю только каждую частьдлина, мне нужно вычислить фактическое время, когда он начинается и заканчивается, и позицию в ItemsControl).

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

Мой первый подход заключался в использовании ObservableCollection<MyviewModel>, но вскоре произошли некоторые ужасы:

5-стороннее мультисвязывание, в котором IMultiValueConverter я бы рассчитал возвращаемое значение и установил бы свойство DataContext на это значение, потому что я знал только предыдущий элемент во время выполнения.

Предыдущийэлемент был отправлен с помощью привязки на Relativesource.PreviousData.

Теперь моя проблема заключается в том, что после установки значения из конвертера (что, очевидно, плохо) и фактического получения его на работу обычная коллекция не 'у него нет понятия порядка в его элементах, поэтому, когда я иду дальше, когда я хочу добавить аудио часть в середине остальных, на дисплее отображается моценил

Кроме того, когда я буду реализовывать больше бизнес-логики, мне может понадобиться доступ к началу и концу звуковых частей, которые рассчитаны в этом преобразователе, и что, если они еще не отображаются ...?

Так что этот подход был неправильным на нескольких уровнях.

Именно здесь я начал искать в Google и узнал о LinkedList.Сейчас я пытаюсь создать класс, который по сути является Observable 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 ...

Так что я думаю, что могу сделать перегрузки для каждого из этих методов, но это звучит довольно неприятно...

Короче говоря: мне нужна какая-то коллекция, в которой каждый элемент знает детали предыдущего, чтобы вычислить одно из своих собственных свойств.

Anyключи?это даже хорошее решение?

Спасибо!

Приложение, 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.Это немного работы, но я думаю, что не будет никакого элегантного решения моей проблемы ...

Ответы [ 6 ]

4 голосов
/ 09 августа 2011

Хорошо, теперь я создал собственный обобщенный класс, который поддерживает 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 следует заменить пользовательским классом, который возвращает ObservableLinkedList из своего свойства List.

2 голосов
/ 09 августа 2011

LinkedList<T> не предназначен для наследования: большинство его методов не являются виртуальными, поэтому нечего переопределять. Если вы хотите повторно использовать его реализацию и реализовать INotifyCollectionChanged, используйте композицию, а не наследование.

Но в любом случае не имеет смысла реализовывать наблюдаемый связанный список, потому что связанный список не поддерживает произвольный доступ по индексу, а CollectionChanged уведомления полезны только в том случае, если вы указываете индекс (если только вы не повышаете NotifyCollectionChangedAction.Reset уведомлений, но тогда это не очень эффективно)

1 голос
/ 09 августа 2011

Это хорошее решение, вам просто нужно создать собственную реализацию LinkedList.

LinkedList<T> не реализует никакого связного интерфейса со списками, так что вы сами по отношению к методам / свойствам.Полагаю, хорошим руководством будет дублирование открытых методов и свойств LinkedList<T>.Это позволит вам использовать фактический экземпляр коллекции в качестве резервного хранилища.

0 голосов
/ 27 августа 2015

Если вы сделали свой класс таким:

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

У вас не должно возникнуть проблем с переопределением методов новым ключевым словом.
Обратите внимание, что сам класс имеет спецификатор TYPE, соответствующий вашему связанному списку.Тип.

Я сделал это здесь общим, но вы можете поместить все, что вы хотите, между этими .Универсальный просто означает, что вы можете использовать эту вещь в гораздо большем количестве мест, чем 1, включая будущий проект.

0 голосов
/ 09 августа 2011

Звучит так, будто у вас есть две разные проблемы. Один из них управляет списком элементов для отображения, а другой позволяет элементу получать доступ к своим предыдущим и следующим элементам.

Вот как я бы подошел: добавьте свойства Previous и Next к классу элементов, установите их при первоначальном заполнении коллекции, а затем обновите их, когда я вставляю и удаляю элементы из списка.

Если вы действительно хотите сходить с ума и сделать общее решение, вы можете реализовать интерфейс ILinkedListNode, а затем создать подкласс ObservableCollection<T> where T : ILinkedListNode, переопределяя различные методы вставки и удаления для обновления элементов 'Previous и Next свойства. Это то, что я бы сделал, если бы мне нужно было многоразовое решение.

Но если нет, я бы просто создал класс модели представления, который обернул коллекцию, выставив ее как свойство Items, а затем реализовал команды вставки и удаления, с которыми пользователь может связываться.

0 голосов
/ 09 августа 2011

Я думаю, что в конечном итоге более простым решением было бы заранее рассчитать время начала и окончания и добавить их в качестве свойств в ViewModel. Тем более, что вы говорите, что вам может понадобиться это значение в вашей бизнес-логике.

...