Observed LinkedList

In my WPF application, I have an ItemsControl element whose element values ​​depend on the previous element displayed .

The ViewModel is an audio file divided into variable length parts, and I need to display it this way, with the DateTime value displayed on the right and what I need to calculate (I know only the length of each part, I need to calculate the actual start and end time, as well position in ItemsControl).

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

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

5-way multibinding, in which IMultiValueConverter I would calculate the return value and set the DataContext property for this value, because I knew only the previous element at runtime.

The previous item was sent using the binding on Relativesource.PreviousData .

Now my problem is that after setting the value from the converter (which is obviously bad) and actually making it work, the regular collection has no concept of order in its elements, therefore, when the road is further down, when I want to add an audio part to in the middle of the rest, the display is messed up.

In addition, when I use more business logic, I may need access to the beginning and end of the audio components that are calculated in this converter, and what if it is not already displayed ...?

So this approach was wrong on several levels.

What I started to search on Google and learned about LinkedList . Now I'm trying to create a class that is basically an Observable LinkedList (I don't need it to be shared):

 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 } 

And the crux of the problem is that I cannot override methods that modify the collection (Addfirst, AddLast, etc.), so I cannot correctly call OnNotifyCollectionChanged ...

So, I think I can do overloads for each of these methods, but that sounds pretty unpleasant ...

In short: I need some kind of collection in which each element knows the details of the previous one in order to calculate one of its properties.

Any clues? is this even a good solution?

Thanks!

Application, ViewModel looks like this:

 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 } 

EDIT: I think I will try to combine the answers of Thomas and Will: I will use composition (i.e. I save a LinkedList instance in my user object instead of inheriting from it) and redefine the methods to be used (AddAfter, AddFirst, etc.) e.), in which I simply call OnNotifyPropertychanged after calling the LinkedList method. This is a bit of work, but I think there will be no elegant solution to my problem ...

+4
source share
6 answers

Now I have created my own universal class that supports IEnumerable and is used as if it were a LinkedList<T> , with the only difference being that WPF receives notification of changes.

Please note that this solution only works for a fairly small collection, I only need about 30 max elements, so for me this is great, but every time you change this collection, it is considered a "Reset".

Here comes 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 mentioned in @AndrewS comments, LinkedListNode should be replaced with a special class that returns an ObservableLinkedList from its List property.

+4
source

LinkedList<T> not intended to be inherited: most of its methods are not virtual, so there is nothing to redefine. If you want to reuse its implementation and implement INotifyCollectionChanged , use composition rather than inheritance.

But in any case, it would be pointless to implement the observed linked list, because the linked list does not support random access by index, and CollectionChanged notifications are useful if you specify an index (unless you raise NotifyCollectionChangedAction.Reset , but then it is not very efficient)

+2
source

This is a good solution, you just need to create your own LinkedList implementation.

LinkedList<T> does not implement any linky listy interface, so you yourself relate to methods / properties. I believe that a good guide would be to duplicate the public methods and properties of LinkedList<T> . This will allow you to use the actual collection instance as backup storage.

+1
source

I think that ultimately a simpler solution would be to calculate the start and end times in advance and add them as properties in the ViewModel. Moreover, you say that you may need this value in your business logic.

0
source

It seems to me that you have two different problems. One of them controls the list of items to display, and the other allows the item to access its previous and subsequent items.

The way I approached it: add the properties of the Previous and Next class to the item class, set them when the collection is initially populated, and then update them when I insert and remove items from the list.

If you really want to go crazy and create a common solution, you can implement the ILinkedListNode interface, and then subclass ObservableCollection<T> where T : ILinkedListNode , overriding the various insertion and deletion methods to update the Previous and Next elements of the property. This is what I would do if I needed the solution to be reused.

But if not, I would simply create a view model class that wraps the collection, exposing it as the Items property, and then implements the insert and delete commands that the user interface can communicate with.

0
source

If you made your class as follows:

 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 have no problem redefining methods with a new keyword.
Note that the class itself has a TYPE specifier that matches the type of the linked list.

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

0
source

All Articles