0

I have a list of controls, where each control has ZIndex property:

class WizardControl : INotifyPropertyChanged
{
    public int ZIndex { get; set; /* set emits PropertyChanged event */}
}

class WizardStep
{
    ObservableCollection<WizardControl> Controls { get; set; }
}

class Wizard
{
    ObservableCollection<WizardStep> Steps { get; set; }
}

I also have a TreeView using HierarchicalDataTemplate, where each WizardStep has a tree-node with all WizardControl as tree leaves.

Now I would like to sort the controls by their ZIndex. I found a solution using custom Converter (http://stackoverflow.com/a/5730402/69868), which works fine as long as ZIndex does not change.

When the ZIndex changes, the sorted CollectionView does not emit CollectionChanged event and the GUI does not pick the change of order.

My question: how to create a sorted collection that will emit correct events when items are reordered due to change in sort-by values?

Miroslav Bajtoš
  • 10,667
  • 1
  • 41
  • 99

3 Answers3

0

For this you must use CollectionView wrapped around your ObservableCollection with CollectionView.SortDescriptions.Add(new SortDescription(ZIndex)).

This way whenever the ZIndex of any item inside the observable collection changes, it would acquire the correct sort position on the GUI automatically.

WPF-it
  • 19,625
  • 8
  • 55
  • 71
0

I guess you could just Implement the INotifyCollectionChanged interface yourself in a collection, that collection can listen to the property changed event args of WizardControl giving you complete control of how things are done. I've included a small sample of how it could be done.

WizardControl.cs

    public class WizardControl : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        int zIndex;
        PropertyChangedEventArgs zIndexArgs = new PropertyChangedEventArgs("ZIndex");

        public int ZIndex
        {
            get { return zIndex; }
            set
            {
                if (zIndex != value)
                {
                    zIndex = value;
                    PropertyChangedEventHandler temp = PropertyChanged;
                    if (temp != null)
                        temp(this, zIndexArgs);
                }
            }
        }

        public override string ToString()
        {
            return zIndex.ToString();      

        }
}

WizardCollection.cs

public class WizardCollection : INotifyCollectionChanged, IEnumerable<WizardControl>
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    NotifyCollectionChangedEventArgs collectionChangedMoveArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);

    List<WizardControl> items = new List<WizardControl>();

    public WizardControl this[int index]
    {
        get { return items[index]; }
    }

    public void Add(WizardControl item)
    {
        if (items == null) items = new List<WizardControl>();
        items.Add(item);
        item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
        NotifyCollectionChangedEventHandler temp = CollectionChanged;
        if (temp != null)
            temp(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
    }

    public void Remove(WizardControl item)
    {
        item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
        NotifyCollectionChangedEventHandler temp = CollectionChanged;
        if (temp != null)
            temp(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "ZIndex")
        {
            items = items.OrderBy(x => x.ZIndex).ToList();
            NotifyCollectionChangedEventHandler temp = CollectionChanged;
            if (temp != null)
                temp(this, collectionChangedMoveArgs);
        }
    }

    public IEnumerator<WizardControl> GetEnumerator()
    {
        return items.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return items.GetEnumerator();
    }
}
Andy
  • 6,366
  • 1
  • 32
  • 37
0

ObservableCollection only raises a PropertyChange notification when the collection changes, not when objects inside the collection change.

If you want that kind of behavior you have to add it yourself. Typically I add it in the CollectionChanged event.

public MyViewModel()
{
    MyCollection.CollectionChanged += MyCollection_CollectionChanged;
}

void MyCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        foreach(MyItem item in e.NewItems)
            item.PropertyChanged += MyItem_PropertyChanged;

    if (e.OldItems != null)
        foreach(MyItem item in e.OldItems)
            item.PropertyChanged -= MyItem_PropertyChanged;
}

void MyItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Some Property")
    {
        // Do work
        RaisePropertyChanged("MyCollection");
    }
}

Since you are using a Converter on the Collection, simply raising the PropertyChanged event for the collection should work to rerun the converter

Rachel
  • 130,264
  • 66
  • 304
  • 490