Daltons
Daltons

Reputation: 2711

ItemsControl with multiple DataTemplates

I have a ItemsControl with a Canvas as ItemsPanelTemplate and multiple DataTemplates like this:

    <ItemsControl ItemsSource="{Binding Path=DisplayObjects}">

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.Resources>
            <DataTemplate DataType="{x:Type viewModels:Object1ViewModel}">
                <views:Object1UIElement/>
            </DataTemplate>

            <DataTemplate DataType="{x:Type viewModels:Object2ViewModel}">
                <viewModels:Object2UIElement/>
            </DataTemplate>
        </ItemsControl.Resources >

        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding Path=X"/>
                <Setter Property="Canvas.Top" Value="{Binding Path=Y"/>
                <!-- Serveral more properties that are either attached to the Canvas or UIElement -->
            </Style>
        </ItemsControl.ItemContainerStyle>

    </ItemsControl>

The two viewmodels location (X,Y) is bonded to the Canvas Left and Top property. The problem is that I want to bind the viewModels properties to the Canvas in different ways.

For example Object1ViewModel use a MultiBinding converter that returns a value depending on factors like the size of the ItemControl, where Object2ViewModel should bind directly to the Canvas Left/Top properties like shown above.

I have tried to set the binding directly in the DataTemplate so I can have different binding styles for different DataTemplates but that does not work.. The objects will not be able to find the Canvas as they are created in a ContentPresenter.

Upvotes: 1

Views: 2900

Answers (2)

Daltons
Daltons

Reputation: 2711

So I figured it out with the comment from Clemens. As he mentioned I needed a ItemContainerStyleSelector so I made the class that would give me the appropriate style:

public class MyItemContainerStyleSelector : StyleSelector
{
    public override Style SelectStyle(object item, DependencyObject container)
    {
        var element = container as FrameworkElement; // this will be a ContentPresenter
        if (element == null) return null;
        var viewModel = element.DataContext;
        if (viewModel is Object1ViewModel)
        {
            return element.FindResource("Object1Style") as Style;
        }
        if (viewModel is Object2ViewModel)
        {
            return element.FindResource("Object2Style") as Style;
        }
        return null;
    }
}

In xaml I would define the styles in the resources and set ItemContainerStyleSelector to my new style selector:

<UserControl.Resources>
    <utilities:MyItemContainerStyleSelector x:Key="MyStyleSelector"/>

    <Style x:Key="Object1Style" TargetType="ContentPresenter">
        ...
    </Style>

    <Style x:Key="Object2Style" TargetType="ContentPresenter">
        ...
    </Style>
</UserControl.Resources>

<ItemsControl
    ...
    ItemContainerStyleSelector="{StaticResource MyStyleSelector}" >
</ItemsControl>

When doing this the property ItemContainerStyle must not be set or the style selector will be ignored.

Upvotes: 2

Joe
Joe

Reputation: 7004

Sounds like you need a DataTemplateSelector?

public class MyWonderfulTemplateSelector : DataTemplateSelector
{
    public override DataTemplate
        SelectTemplate(object item, DependencyObject container)
    {
        FrameworkElement element = container as FrameworkElement;

        if (element != Object1ViewModel)
        {
            return element.FindResource("DataTemplate1") as DataTemplate;
        } else if (element != Object2ViewModel) {
            return element.FindResource("DataTemplate2") as DataTemplate;
        }

        return null;
    }
}

Or something along those lines.

Upvotes: 0

Related Questions