动态设置UserControl为Listbox数据模板体
本文关键字:数据 Listbox 设置 UserControl 动态 | 更新日期: 2023-09-27 18:05:54
我有以下设置:
<ListBox ItemSource="{Binding Targets}">
<ListBox.ItemTemplate>
<DataTemplate>
<view:ViewName />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我想要完成的是动态地决定在运行时使用哪个视图,基于ListBox
的DataContext
中的属性。简单来说,我想用返回正确视图的数据绑定替换<view:ViewName>
。
我使用MEF为我的应用程序提供插件,这些插件可能需要在适当的时候提供自定义视图来显示项目。在设计时,我不会知道所有可能的视图类型(它们可能从DLL动态加载),所以一个简单的DataTemplateSelector
不会这样做。
我已经研究了解决方案,但一无所获。
由于您希望根据绑定值更改模板,因此可以使用DataTrigger
来确定ListBoxItem
的ContentTemplate
<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}"/>