MVVM(使用WPF) - 将多个视图绑定到同一ViewModel

Bim*_*imf 14 wpf binding mvvm viewmodel

我最近开始用WPF调查MVVM模式,用于即将开展的项目.我从Josh Smith的MSDN文章开始.我有一个问题(很多,但让我们从一开始):

我有一个IndividualViewModel,它公开了模型的属性.我需要两个视图"添加个人"和"编辑个人",这些视图与您想象的非常相似.我目前所做的是有2个子类AddIndividualViewModel和EditIndividualViewModel分别公开Add和Edit命令.我还有2个类似的命名视图绑定到这些视图.

现在这个方法有效,而且这些类无论如何都相当小,但我想知道是否有可能只有一个视图模型,它暴露了两个命令.我仍然会有2个视图绑定到同一个视图模型,将相应的命令作为按钮公开.我不太清楚如何做到这一点.在主窗口资源中我有类似的东西:

        <DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
            <Views:AddIndividualView />
        </DataTemplate>
Run Code Online (Sandbox Code Playgroud)

使用这种绑定方法,您只能具有一对一的绑定,即对于给定的视图模型始终显示相同的视图.有没有办法根据视图模型上的属性自动切换视图(例如IndividualViewModel.Mode).我应该考虑采用不同的方法吗?

请注意,主窗口包含一组视图模型,并在选项卡中显示每个模型.

谢谢!

wig*_*igy 6

因此,您需要基于属性值的 2 个不同视图。要考虑的一件事是重构您的表示代码,因此您可以拥有真正的子类,而不是属性的值。然后你可以DataTemplate为每个班级使用 2 个不同的。

<DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
  <Views:AddIndividualView />
</DataTemplate>

<DataTemplate DataType="{x:Type ViewModels:EditIndividualViewModel}">
  <Views:EditIndividualView />
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

如果您认为这是一种矫枉过正,您可以使用触发器并将您的特定视图包装到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>
Run Code Online (Sandbox Code Playgroud)


小智 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" />
Run Code Online (Sandbox Code Playgroud)

表单底部的内容演示者:

        <ContentPresenter Content="{Binding}"
                          ContentTemplateSelector="{StaticResource addEditTemplateSelector}" />
Run Code Online (Sandbox Code Playgroud)

这是模板选择器的代码:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这可能是(也可能不是)如何实现最终目标(根据需求),但是很高兴看到我有这种可用的选项。


Avi*_* P. 0

您没有理由不能实现这一目标。实现此目的的一种方法是在视图模型中提供一些标志,说明您是处于添加模式还是编辑模式,并使用简单的绑定、触发器或模板选择器根据该标志设置视图样式。

作为参考,您可以查看Sacha Barber 的DataWrapper,它是他的Cinch框架的一部分(不直接适用于您的情况,但这是一个很好的起点),它以支持在只读之间切换的标志的方式包装视图模型中的数据字段(查看记录模式)和读写(编辑记录模式)。您可以应用类似的方法来区分添加和编辑。

基本上,不是在视图模型中包含简单的属性,而是实例化一个包含一个Value属性和一个IsAdding属性的数据包装器类。在您看来,您可以使用绑定、触发器或模板选择器来根据该属性修改模板。