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

时间:2010-01-01 14:15:11

标签: wpf mvvm binding viewmodel

我最近开始使用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)。我应该考虑采用不同的方法吗?

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

谢谢!

4 个答案:

答案 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。

  1. 从IndividualVM派生EditIndividualVM和AddINdividualVM。
  2. Edit-和AddCommands路由到IndividualVM中的setter属性。
  3. setter VM = new AddIndividualVM或VM = new EditIndividualVM,具体取决于按下的按钮。
  4. 在xaml中,您将contentgrid绑定到VM属性,如下所示: