具有多个数据模板的 ItemsControl

ItemsControl with multiple DataTemplates

我有一个带有 Canvas 作为 ItemsPanelTemplate 和多个 DataTemplates 的 ItemsControl:

    <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>

两个视图模型位置 (X,Y) 绑定到 Canvas 左侧和顶部 属性。问题是我想以不同的方式将 viewModels 属性绑定到 Canvas。

例如 Object1ViewModel 使用 MultiBinding 转换器,returns 一个值取决于 ItemControl 的大小等因素,其中 Object2ViewModel 应该直接绑定到 Canvas Left/Top 如上所示的属性。

我尝试直接在 DataTemplate 中设置绑定,这样我就可以为不同的 DataTemplate 使用不同的绑定样式,但这不起作用。对象将无法按原样找到 Canvas在 ContentPresenter 中创建。

听起来你需要 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;
    }
}

或者类似的东西。

所以我根据 Clemens 的评论弄明白了。正如他提到的,我需要一个 ItemContainerStyleSelector,所以我制作了 class 可以给我合适的样式:

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;
    }
}

在 xaml 中,我将在资源中定义样式并将 ItemContainerStyleSelector 设置为我的新样式选择器:

<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>

执行此操作时,不得设置 属性 ItemContainerStyle,否则样式选择器将被忽略。