16

I have recently started investigating the MVVM pattern with WPF for an upcoming project. I started with Josh Smith's MSDN article. I have a question (well many, but let's start with one):

I have an IndividualViewModel which exposes the properties of the model. I need two views "Add Individual" and "Edit Individual" which are very similar as you can imagine. What I have done currently is to have 2 subclasses AddIndividualViewModel and EditIndividualViewModel which expose the Add and Edit commands respectively. I also have 2 similary named views that bind to these.

Now this method works and these classes are fairly small anyway, but I'm wondering if it is possible for me to have just the one view model, which exposes both commands. I would still have 2 views which would bind to this same view model, exposing the appropriate command as a button. I'm not quite sure how to do this. In the main window resources I have something like:

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

With this method of binding you can only have a one-to-one binding, i.e. the same view is always shown for a given view model. Is there a way to automatically switch the view depending on a property on the view model (e.g. IndividualViewModel.Mode). Is there a different approach I should be considering?

Note that the main window has a collection of view models and shows each in tab.

Thank you!

4

4 回答 4

6

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

<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>
于 2015-03-11T13:08:10.550 回答
4

感谢您为我指明正确的方向!我对 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;
    }
}

这可能是也可能不是最终实现的方式(考虑到要求),但很高兴看到我有这种选项可用。

于 2010-01-03T13:29:56.290 回答
0

There's no reason why you shouldn't be able to achieve that. One way of doing this is to provide some flag in your view model stating whether you're in add mode or in edit mode, and styling your view based on that flag using simple bindings, triggers or template selectors.

For reference you may look at Sacha Barber's DataWrapper class that's part of his Cinch framework (not directly applicable to your case, but it's a good starting point) which wraps data fields in the view model in such a way to support a flag to toggle between read only (view record mode), and read-write (edit record mode). You could apply a similar approach to make the distinction between add and edit.

Basically, instead of having simple properties in your view model, instantiate a data wrapper class which includes a Value property, and a IsAdding property. In your view, you can use bindings, triggers or template selectors to modify templates based on that property.

于 2010-01-01T14:23:52.073 回答
0

对于此任务,您根本不需要任何 DataTemplateSelector。

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

于 2010-03-21T17:52:09.463 回答