Instantiate and reuse instances of objects in XAML

eriksmith200 picture eriksmith200 · Oct 1, 2010 · Viewed 17.2k times · Source

I want to instantiate objects in XAML, and reuse these instances. I think it should be simple but I'm stuck, I'm probably missing something obvious.

Say I want to add Cats to different Rooms (Room has an ObservableCollection containing objects of type Cat). In the UserControl.Resources I create ObjectDataProviders:

<ObjectDataProvider x:Key="Cat1" ObjectType="{x:Type local:Cat}">
    <ObjectDataProvider.ConstructorParameters>
        <System:String>Tom</System:String>
    </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="Cat2" ObjectType="{x:Type local:Cat}">
    <ObjectDataProvider.ConstructorParameters>
        <System:String>Garfield</System:String>
    </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="Cat3" ObjectType="{x:Type local:Cat}">
    <ObjectDataProvider.ConstructorParameters>
        <System:String>Furball</System:String>
    </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>

In my UserControl I want to add the Cats to the Rooms:

<local:Room x:Name="Room1">
    <local:Room.Cats>

    </local:Room.Cats>
<local:Room>
<local:Room x:Name="Room2">
    <local:Room.Cats>

    </local:Room.Cats>
<local:Room>

What is the syntax for adding the Cat instances to the ObservableCollection Room.Cats? For instance I want to add Cat1 and Cat2 to Room1, and Cat2 and Cat3 to Room2. Am I completely on the wrong track?

Answer

Robert Rossney picture Robert Rossney · Oct 1, 2010

Reusing individual instances the way you're trying to do it is tricky. This is because the way that you generally reference single objects in XAML is with the StaticResource markup extension, and you can only use that markup extension to set a property value.

So you can easily set a property of type Cat to an instance of a Cat:

<Room Cat="{StaticResource Cat1}"/>

but you can't populate a collection by setting a property.

The answer, surprisingly, is to instantiate your objects directly in XAML instead of wrapping them in ObjectDataProviders. You still use the ObjectDataProvider, but differently:

<Window x:Class="ObjectDataProviderDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:local="clr-namespace:ObjectDataProviderDemo" 
        xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" 
        Title="MainWindow" 
        Height="350" 
        Width="525">
    <Window.Resources>
        <local:Cat x:Key="Tom" Name="Tom"/>
        <local:Cat x:Key="Garfield" Name="Garfield"/>
        <local:Cat x:Key="Furball" Name="Furball"/>
        <Collections:ArrayList x:Key="CatList1">
            <ObjectDataProvider ObjectInstance="{StaticResource Tom}" />
            <ObjectDataProvider ObjectInstance="{StaticResource Garfield}" />
            <ObjectDataProvider ObjectInstance="{StaticResource Furball}" />
        </Collections:ArrayList>
        <Collections:ArrayList x:Key="CatList2">
            <ObjectDataProvider ObjectInstance="{StaticResource Tom}" />
            <ObjectDataProvider ObjectInstance="{StaticResource Furball}" />
        </Collections:ArrayList>
        <DataTemplate x:Key="CatTemplate">
            <TextBlock Text="{Binding Name}" />
        </DataTemplate>
    </Window.Resources>
    <StackPanel>
        <ListBox ItemsSource="{StaticResource CatList1}" 
                 ItemTemplate="{StaticResource CatTemplate}"/>
        <ListBox ItemsSource="{StaticResource CatList2}"
                 ItemTemplate="{StaticResource CatTemplate}" />
    </StackPanel>
</Window>