动态设置UserControl为Listbox数据模板体

本文关键字:数据 Listbox 设置 UserControl 动态 | 更新日期: 2023-09-27 18:05:54

我有以下设置:

<ListBox ItemSource="{Binding Targets}">
    <ListBox.ItemTemplate>
        <DataTemplate>
          <view:ViewName />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

我想要完成的是动态地决定在运行时使用哪个视图,基于ListBoxDataContext中的属性。简单来说,我想用返回正确视图的数据绑定替换<view:ViewName>

我使用MEF为我的应用程序提供插件,这些插件可能需要在适当的时候提供自定义视图来显示项目。在设计时,我不会知道所有可能的视图类型(它们可能从DLL动态加载),所以一个简单的DataTemplateSelector不会这样做。

我已经研究了解决方案,但一无所获。

动态设置UserControl为Listbox数据模板体

由于您希望根据绑定值更改模板,因此可以使用DataTrigger来确定ListBoxItemContentTemplate

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}"/>
    <Style.Triggers>
        <DataTrigger Property="{Binding SomeProperty}" Value="A">
            <Setter Property="ContentTemplate" Value="{StaticResource TemplateA}"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

我发现这比使用DataTemplateSelector更好,因为如果绑定属性改变,它会被重新评估,而DataTemplateSelector不会。

如果您想基于对象类型更改模板,您可以使用隐式DataTemplates。这些DataTemplates定义了DataType,但没有x:Key,它们将在WPF尝试绘制指定类型的对象时使用。

例如,如果您在<X.Resources>中某个地方定义了这个模板

<DataTemplate DataType="{x:Type models:ActionA}">
    <views:ActionAView />
</DataTemplate>

然后你可以直接将你的模型对象插入到UI中,WPF将使用你指定的模板绘制它

<ContentControl Content="{Binding SomeIActionObject}" />
<ItemsControl ItemsSource="{Binding CollectionOfIActionObjects}" />

您提到您将允许用户创建具有使用MEF导入的附加模板的模块,因此在这种情况下,您可能最好使用IValueConverter查找Application.Resources

中的匹配模板。

例如,如果绑定值等于"A",那么转换器可能会在Application.Resources中搜索名为"TemplateA"的模板,并将其返回给绑定

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="ContentTemplate" 
            Value="{Binding SomeProperty, 
                Converter={StaticResource MyTemplateConverter}}"/>
</Style>

使用这篇文章中的DataTemplateManager,你可以这样做:

DataTemplateManager.RegisterDataTemplate<ViewModelType1, ViewType1>();
DataTemplateManager.RegisterDataTemplate<ViewModelType2, ViewType2>();
DataTemplateManager.RegisterDataTemplate<ViewModelType3, ViewType3>();

那么你将从ListBox中移除ItemTemplate:

<ListBox ItemSource="{Binding Targets}"/>

在ListBox ViewModel中,你可以:

public void AddTargets()
{
    Targets.Add(new ViewModelType1());
    Targets.Add(new ViewModelType2());
    Targets.Add(new ViewModelType3());
}

然后,WPF将自动使用每个DataTemplate来渲染每个相应的ViewModel。

还请注意,您可以在显示ListBox之前的任何时间调用DataTemplateManager.RegisterDataTemplate(),因此理论上您可以在加载MEF部分时这样做。

编辑:

根据你的评论,你可以用一个ContentPresenter创建一个DataTemplate来根据ViewModel的属性显示选中的View:

<DataTemplate DataType="{x:Type local:TargetViewModel}">
    <ContentPresenter x:Name="MainContentPresenter" Content="{Binding}" ContentTemplate="{Binding YourProperty, Converter=SomeConverter}"/>

在someeconverter中,你应该使用与文章中演示的相同的技术来动态生成一个DataTemplate