DependencyProperty of type ObservableCollection doesn't bind

1.7k views Asked by At

I am trying to make a custom control with a DependencyProperty. But I cannot bind my ObservableCollection to the control. When I use an Enumerable I have no problem. But I need to add items to the collection so my only option is ObservableCollection.

Creating Authorizations:

AuthorizationsDest = new ObservableCollection<Authorization>();
        AuthorizationsDest.Add(new Authorization() { Key = "Test1", Description = "Test1", ObjectState = ObjectState.UnModified });
    }

The custom control in xaml

<customControls:ListBoxEditLookup ItemsSource="{Binding Authorizations}" DisplayMember="Description" DestinationList="{Binding AuthorizationsDest}" />

The DependencyProperty:

[Description("Binded destination list"), Category("Data")]
    public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("DestinationList", typeof(ObservableCollection<HrdEntity>), typeof(ListBoxEditLookup), new UIPropertyMetadata(null));
    public ObservableCollection<HrdEntity> DestinationList
    {
        get
        {
            return GetValue(ItemsProperty) as ObservableCollection<HrdEntity>;
        }
        set { SetValue(ItemsProperty, value); }
    }
1

There are 1 answers

0
Xavier On BEST ANSWER

Based on the comment responses to your question, I think we have arrived at the realization that using a specific concrete collection type on your dependency property is causing issues, and you should consider using an interface such as IEnumerable instead. Read on for a more detailed explanation.


It is generally a good idea to use the IEnumerable interface as the type for collection dependency properties in a custom control. It is the base interface that every collection implements as it allows foreach loops to be run on them. When the dependency property is set, you can inspect the value to see if it implements other interfaces that you care about within your control.

For example, if your control wants to do things like add, remove and insert items and/or index into the collection, check to see if it implements IList. If you want to observe the collection for changes, check to see if it implements INotifyCollectionChanged.

Consider maintaining private references to the collection that are typed as the interfaces you need to access. For example:

private IList mItemsAsList;
private INotifyCollectionChanged mItemsAsObservable;

// Call when the value of ItemsProperty changes
private void OnItemsChanged(IEnumerable newValue)
{
    if (mItemsAsObservable != null)
    {
        mItemsAsObservable.CollectionChanged -= Items_CollectionChanged;
    }

    mItemsAsList = newValue as IList;
    mItemsAsObservable = newValue as INotifyCollectionChanged;

    if (mItemsAsObservable != null)
    {
        mItemsAsObservable.CollectionChanged += Items_CollectionChanged;
    }
}

private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // Do stuff in response to collection being changed
}

If there are certain things that are required by your control (not optional), you can always throw an ArgumentException in the property changed callback if those requirements are not met. For example, if you must be able to add new items to the collection:

mItemsAsList = newValue as IList;
if (newValue != null && (mItemsAsList == null || mItemsAsList.IsReadOnly || mItemsAsList.IsFixedSize))
{
    throw new ArgumentException("The supplied collection must implement IList, not be readonly, and have a variable size.", "newValue");
}

Once you have specialized references to the collection, you can limit your functionality based on which interfaces are implemented. For example, let's say you want to add a new item:

private void AddItem(object item)
{
    // Make sure to check IsFixedSize because some collections, such as Array,
    // implement IList but throw an exception if you try to call Add on them.
    if (mItemsAsList != null && !mItemsAsList.IsReadOnly && !mItemsAsList.IsFixedSize)
    {
        mItemsAsList.Add(item);
    }
}