我最近开始使用WPF为即将开展的项目调查MVVM模式。我从Josh Smith's MSDN article.开始,我有一个问题(很多,但让我们从一开始):
我有一个IndividualViewModel,它公开了模型的属性。我需要两个视图“添加个人”和“编辑个人”,这些视图与您想象的非常相似。我目前所做的是拥有2个子类AddIndividualViewModel和EditIndividualViewModel,它们分别显示Add和Edit命令。我还有两个相似的命名视图绑定到这些视图。
现在这个方法有效,而且这些类无论如何都相当小,但我想知道是否有可能只有一个视图模型,它暴露了两个命令。我仍然会有2个视图绑定到同一个视图模型,将相应的命令作为按钮公开。我不太清楚如何做到这一点。在主窗口资源中我有类似的东西:
<DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
<Views:AddIndividualView />
</DataTemplate>
使用这种绑定方法,您只能拥有一对一的绑定,即对于给定的视图模型始终显示相同的视图。有没有办法根据视图模型上的属性自动切换视图(例如IndividualViewModel.Mode)。我应该考虑采用不同的方法吗?
请注意,主窗口包含一组视图模型,并在选项卡中显示每个视图。
谢谢!
答案 0 :(得分:5)
感谢您指点我正确的方向!我仍然是WPF的新手,并且了解所有不同的可能性,包括绑定方法。无论如何,对于任何感兴趣的人来说,这是我为这个特殊情况得出的解决方案:
我决定将视图模型分为两个子类AddIndividualViewModel和EditIndividualViewModel,它们只显示命令,而不是试图管理一个类中的状态。但是我想要一个视图,这样我就不会复制XAML了。我最终使用两个DataTemplates和DataTemplateSelector来根据视图模型切换操作按钮:
<DataTemplate x:Key="addTemplate">
<Button Command="{Binding Path=AddCommand}">Add</Button>
</DataTemplate>
<DataTemplate x:Key="editTemplate">
<Button Command="{Binding Path=UpdateCommand}">Update</Button>
</DataTemplate>
<TemplateSelectors:AddEditTemplateSelector
AddTemplate="{StaticResource addTemplate}"
EditTemplate="{StaticResource editTemplate}"
x:Key="addEditTemplateSelector" />
以及表单底部的内容演示者:
<ContentPresenter Content="{Binding}"
ContentTemplateSelector="{StaticResource addEditTemplateSelector}" />
以下是模板选择器的代码:
class AddEditTemplateSelector : DataTemplateSelector
{
public DataTemplate AddTemplate { get; set; }
public DataTemplate EditTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is AddIndividualViewModel)
{
return AddTemplate;
}
else if (item is EditIndividualViewModel)
{
return EditTemplate;
}
return null;
}
}
这可能是也可能不是如何实现最终的东西(给定要求),但很高兴看到我有这种选择。
答案 1 :(得分:4)
因此,您需要基于属性值的2个不同视图。要考虑的一件事是refactor your presentation code,因此您可以拥有真正的子类,而不是属性的值。然后,您可以为每个班级使用2个不同的DataTemplate
。
<DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
<Views:AddIndividualView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:EditIndividualViewModel}">
<Views:EditIndividualView />
</DataTemplate>
如果您认为这是一种矫枉过正,您可以使用触发器将特定视图包装到ContentPresenter
。
<DataTemplate x:Key="AddIndividualTemplate" DataType="{x:Type ViewModels:IndividualViewModel}">
<Views:AddIndividualView />
</DataTemplate>
<DataTemplate x:Key="EditIndividualTemplate" DataType="{x:Type ViewModels:IndividualViewModel}">
<Views:EditIndividualView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:IndividualViewModel}">
<ContentPresenter Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="ContentPresenter">
<Setter Property="ContentTemplate" Value="{StaticResource AddIndividualTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Mode}" Value="{x:Static ViewModels:IndividualMode.Edit}">
<Setter Property="ContentTemplate" Value="{StaticResource EditIndividualTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</DataTemplate>
答案 2 :(得分:0)
没有理由你不能实现这一目标。一种方法是在视图模型中提供一些标志,说明您是处于添加模式还是处于编辑模式,并使用简单的绑定,触发器或模板选择器基于该标志设置视图样式。
作为参考,您可以查看Sacha Barber's DataWrapper
class这是Cinch
框架的一部分(不直接适用于您的情况,但它是一个很好的起点),它将视图模型中的数据字段包装在这样的支持标志在只读(查看记录模式)和读写(编辑记录模式)之间切换的方法。您可以应用类似的方法来区分添加和编辑。
基本上,不是在视图模型中使用简单属性,而是实例化包含Value
属性和IsAdding
属性的数据包装类。在您的视图中,您可以使用绑定,触发器或模板选择器来基于该属性修改模板。
答案 3 :(得分:0)
对于此任务,您根本不需要任何DataTemplateSelector。
在xaml中,您将contentgrid绑定到VM属性,如下所示: