views:

26

answers:

1

I have a custom control Workspace that inherits from Control and within it is a DependencyProperty that I need to contain a user-specified IEnumerable<IFoo> (I have also tried making it an non-generic IEnumerable).

Public Shared ReadOnly FoosProperty As DependencyProperty = DependencyProperty.Register("Foos", GetType(IEnumerable(Of IFoo)), GetType(Workspace), New FrameworkPropertyMetadata())
Public Property Foos() As IEnumerable(Of IFoo)
    Get
        Return CType(Me.GetValue(FoosProperty), IEnumerable(Of IFoo))
    End Get
    Set(ByVal value As IEnumerable(Of IFoo))
        Me.SetValue(FoosProperty, CType(value, IEnumerable(Of IFoo)))
    End Set
End Property

Everything works perfectly when I create and set an array of IFoo in code but when I try to add them in XAML I get errors. If I add a single IFoo I get the error

  1. "'FooItem' is not a valid value for property 'Foos'."

at run time. If I try to add multiple IFoo items I get three errors at compile time

  1. The object 'Workspace' already has a child and cannot add 'FooItem'. 'Workspace' can accept only one child.
  2. Property 'Foos' does not support values of type 'FooItem'.
  3. The property 'Foos' is set more than once.

I read the errors to mean that WPF isn't converting the xaml to an array of items like it normally would. Here is how I'm trying to add the items in XAML

<Workspace>
    <Workspace.Foos>
        <FooItem />
        <FooItem />
    </Workspace.Foos>
</Workspace>

I have created similar DependencyProperties in the past and never had a problem so I'm guessing I'm missing something simple.

Thanks for any help!

+2  A: 

To be able to add multiple elements the collection property has to be IList or IDictionary. When it is IEnumerable XAML parser tries to assign first value to the property itself (instead of calling Add like with lists) and gets confused about consecutive items. This is the source of your errors.

Also, if you want to populate collection from XAML, make sure that your list is instantiated and not null to begin with, since XAML will not instantiate a list for you, it'll just call Add on it. So to avoid NullReferenceException, get rid of setter on your IList property and instantiate list from constructor.

It doesn't have to be dependency property:

private readonly ObservableCollection<FooItem> _foos = new ObservableCollection<FooItem>();

public ObservableCollection<FooItem> Foos
{
    get { return _foos; }
}
repka
I get the same errors when I use IList as well. Even when I instantiate the list in the constructor. IEnumerable should work though since it is the type for the ItemsSource property in ItemsControl.
Bryan Anderson
`ItemsSource` type is `object` and you cannot add multiple items there. You're confusing it with `Items` property of `ItemsCotnrol` which is `IList`. Are you sure you've tried making your `Foos` a read-only (no setter) property of `IList` (or its derivative) type?
repka
Yes I have. http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemssource.aspx
Bryan Anderson
Bryan, please also note that `IList<T>` is not `IList`, i.e. generic interface does not implement non-generic one. Unfortunately your property type have to implement non-generic `IList`.Since you normally want to be notified when collection is changed, it's a good idea to use `ObservableCollection<FooItem>`. Property can be exposed as `ObservableCollection<FooItem>` as well, since it implements non-generic `IList`.
repka
You were right, I had changed it to IList<IFoo>, making it an IList worked just fine. Thanks for the help.
Bryan Anderson
I added sample. Also once again, `ItemsControl.ItemsSource` is not an example you want to look at. Unless you're instantiating list or array explicitly in XAML to be your `ItemsSource` it wont work. If you want to add items in `ItemControl` of XAML, you should use `Items` property.
repka