Problem on binding ObservableCollection to 'SelectedObjects' of PropertyGrid

Aug 15, 2014 at 1:55 PM
Edited Aug 15, 2014 at 1:56 PM
I tried to bind observablecollection of viewmodel to 'SelectedObjects' of propertygrid, but it was failed.

Following codes were used.

-- xaml --
...
xmlns:p="clr-namespace:PropertyTools.Wpf;assembly=PropertyTools.Wpf"
...
<p:PropertyGrid SelectedObjects="{Binding SelectedModels}" />
...
-- viewmodel.cs --
...
SelectedModels = new ObservableCollection<object>();
...
public ObservableCollection<object> SelectedModels { get; private set; }
I think that it's because of not notifying collectionchanged event.

So, I modified "PropertyGrid.cs" like this, and it works now.

-- Original --
/// <summary>
/// Handles changes in SelectedObjects.
/// </summary>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs" /> instance containing the event data.</param>
private void SelectedObjectsChanged(DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue == null)
    {
        this.CurrentObject = null;
    }
    else
    {
        var enumerable = e.NewValue as IEnumerable;
        if (enumerable != null)
        {
            var list = enumerable.Cast<object>().ToList();
            if (list.Count == 0)
            {
                this.CurrentObject = null;
            }
            else if (list.Count == 1)
            {
                this.CurrentObject = list[0];
            }
            else
            {
                this.CurrentObject = new ItemsBag(list);
            }
        }
        else
        {
            this.CurrentObject = null;
        }
    }

    this.UpdateControls();
}
-- Modified --
/// <summary>
/// Handles changes in SelectedObjects.
/// </summary>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs" /> instance containing the event data.</param>
private void SelectedObjectsChanged(DependencyPropertyChangedEventArgs e)
{
    if (e.OldValue != null)
    {
        System.Collections.Specialized.INotifyCollectionChanged collection = e.OldValue as System.Collections.Specialized.INotifyCollectionChanged;
        if (collection != null)
            collection.CollectionChanged -= new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged);

        System.Collections.Specialized.NotifyCollectionChangedEventArgs ev = new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, e.OldValue as IList);
        collection_CollectionChanged(e.NewValue, ev);
    }

    if (e.NewValue != null)
    {
        System.Collections.Specialized.INotifyCollectionChanged collection = e.NewValue as System.Collections.Specialized.INotifyCollectionChanged;
        if (collection != null)
            collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged);

        System.Collections.Specialized.NotifyCollectionChangedEventArgs ev = new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, e.NewValue as IList);
        collection_CollectionChanged(e.NewValue, ev);
    }
    else
        this.CurrentObject = null;            
}

/// <summary>
/// Temporal items for CurrentObjects
/// </summary>
List<object> __items = new List<object>();

/// <summary>
/// Fire when connected collection changed.
/// </summary>
void collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    switch (e.Action)
    {
        case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
            foreach (var item in e.NewItems)
            {
                if (!__items.Contains(item))
                    __items.Add(item);
            }
            break;

        case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
            foreach (var item in e.OldItems)
            {
                if (__items.Contains(item))
                    __items.Remove(item);
            }
            break;

        default:
            break;
    }

    if (__items.Count == 0)
        this.CurrentObject = null;
    else if (__items.Count == 1)
        this.CurrentObject = __items[0];
    else
        this.CurrentObject = new ItemsBag(__items);
    
    this.UpdateControls();
}
Does anyone has better idea?
Aug 17, 2014 at 5:38 AM
Edited Aug 17, 2014 at 5:38 AM
I modified my codes.
/// <summary>
/// Handles changes in SelectedObjects.
/// </summary>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs" /> instance containing the event data.</param>
private void SelectedObjectsChanged(DependencyPropertyChangedEventArgs e)
{
    if (e.OldValue != null)
    {
        INotifyCollectionChanged collection = e.OldValue as INotifyCollectionChanged;
        if (collection != null)
            collection.CollectionChanged -= new NotifyCollectionChangedEventHandler(SelectedObjects_CollectionChanged);

        NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, e.OldValue as IList);
        SelectedObjects_CollectionChanged(e.NewValue, args);
    }

    if (e.NewValue != null)
    {
        INotifyCollectionChanged collection = e.NewValue as INotifyCollectionChanged;
        if (collection != null)
            collection.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedObjects_CollectionChanged);

        NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, e.NewValue as IList);
        SelectedObjects_CollectionChanged(e.NewValue, args);
    }
    else
        this.CurrentObject = null; 
}

List<object> selectedObjects = new List<object>();
void SelectedObjects_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    switch (e.Action)
    {
        case NotifyCollectionChangedAction.Add:
            foreach (var item in e.NewItems)
            {
                if (!selectedObjects.Contains(item))
                    selectedObjects.Add(item);
            }
            break;

        case NotifyCollectionChangedAction.Remove:
            foreach (var item in e.OldItems)
            {
                if (selectedObjects.Contains(item))
                    selectedObjects.Remove(item);
            }
            break;

        case NotifyCollectionChangedAction.Reset:
            selectedObjects.Clear();
            break;

        default:
            break;
    }

    if (selectedObjects.Count == 0)
        this.CurrentObject = null;
    else if (selectedObjects.Count == 1)
        this.CurrentObject = selectedObjects[0];
    else
        this.CurrentObject = new ItemsBag(selectedObjects);

    this.UpdateControls();
}
I'm not sure that this is the best solution, but it works well in my case.