I have a class CollectionOfThings
. As its name suggests its a simple collection of instances of the Thing
class. Thing
class has a public default constructor and two simple public get, set properties ID
and DisplayName
, both are string. CollectionOfThing
also has public default constructor.
In XAML I would like to use markup like this:-
<Grid.Resources>
<local:CollectionOfThings x:Key="Things">
<local:Thing ID="1" DisplayName="Hello" />
<local:Thing ID="2" DisplayName="World" />
<local:CollectionOfThings>
</Grid.Resources>
All is good as long as CollectionOfThings derives from a Collection type. However I want CollectionOfThings
to also be a DependencyObject
. I thought that's fine creating an implementation of ICollection<T>
, INotifyCollectionChanged
etc is not that hard. Then I can derive from DependencyObject
.
Unfortunately ICollection<T>
doesn't cut it for some reason. With ICollection<Thing>
I get 'CollectionOfThings does not support Thing as content'. Go back to Collection<Thing>
and everything works but leaves me without a DependencyObject
implementation.
Suggestions anyone?
XAML wants System.Collections.IList
(the non-generic one!) for collections. If you only implement generic IList<T>
interface, it won't cut it.
It also expects to see public Add
methods on the class, and derives suitable child types for the collection from arguments of those methods. So the typical approach is to explicitly implement IList
- so that its Add(object)
method isn't public, and thus isn't picked by XAML parser - and then implicitly implement IList<T>
for all child types that you want to support.
The reason why it works for most built-in collection types (List<T>
, Collection<T>
etc) is because they follow the pattern above and implement both generic and non-generic interfaces.
Collections are usually used in xaml so as to define a ContentPropertyAttribute("PropertyName")
on the custom class where PropertyName
is usually IList
or IList<T>
.
[ContentProperty(SomeProp)]
class SomeClass
{
//this is where subitems created in xaml would get added automatically
public IList<int> SomeProp { get; set; }
}
However, if a class does not provide a value for the ContentPropertyAttribute
, and implements any interface of type IList<T>
, IList
, ICollection<T>
or ICollection
, a property of name "Items" (if present, and if of appropriate type, e.g. IList
) is detected trough reflection automatically (for the purpose of population trough xaml), and that is the case with the Collection<T>
class.
You can even check the class in Reflector to make sure it has no content property defined.
The Collection<T>
class has this property
protected IList<T> Items
{
get
{
return _items;
}
}
and adding something like that to your collection type (even if the property is protected) is enough to have it behave as desired in xaml.
I suppose the behavior is implemented in that way for backward compatibility.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With