MVVM (with WPF) - 将多个视图绑定到同一个 ViewModel
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1988704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MVVM (with WPF) - Binding Multiple Views to the Same ViewModel
提问by Bimf
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):
我最近开始为即将到来的项目研究 WPF 的 MVVM 模式。我从Josh Smith 的 MSDN 文章开始。我有一个问题(很多,但让我们从一个开始):
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.
我有一个 IndividualViewModel,它公开了模型的属性。我需要两个视图“添加个人”和“编辑个人”,它们与您想象的非常相似。我目前所做的是有 2 个子类 AddIndividualViewModel 和 EditIndividualViewModel 分别公开 Add 和 Edit 命令。我还有 2 个类似的命名视图绑定到这些视图。
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:
现在这个方法起作用了,而且这些类无论如何都相当小,但我想知道我是否有可能只有一个视图模型,它公开两个命令。我仍然会有 2 个视图绑定到同一个视图模型,将适当的命令作为按钮公开。我不太确定如何做到这一点。在主窗口资源中,我有类似的东西:
<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?
使用这种绑定方法,您只能进行一对一绑定,即对于给定的视图模型始终显示相同的视图。有没有办法根据视图模型(例如 IndividualViewModel.Mode)上的属性自动切换视图。我应该考虑使用不同的方法吗?
Note that the main window has a collection of view models and shows each in tab.
请注意,主窗口有一组视图模型,并在选项卡中显示每个模型。
Thank you!
谢谢!
回答by wigy
So you need 2 different views based on a property value. One thing to consider is to refactor your presentation code, so instead of the values of a property you could have real subclasses. Then you can use 2 different DataTemplate
for each class.
因此,您需要基于属性值的 2 个不同视图。要考虑的一件事是重构您的表示代码,因此您可以拥有真正的子类,而不是属性的值。然后你可以DataTemplate
为每个班级使用 2 个不同的。
<DataTemplate DataType="{x:Type ViewModels:AddIndividualViewModel}">
<Views:AddIndividualView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:EditIndividualViewModel}">
<Views:EditIndividualView />
</DataTemplate>
If you think that is an overkill, you could use a trigger and wrap your specific views into a ContentPresenter
.
如果您认为这是一种矫枉过正,您可以使用触发器并将您的特定视图包装到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>
回答by Poya M.
Thanks for pointing me in the right direction! I am still new with WPF too and learning about all the different possibilities including binding methods. Anyway for anyone interested, here is the solution I arrived at for this particular case:
感谢您为我指明正确的方向!我也是 WPF 的新手,正在学习所有不同的可能性,包括绑定方法。无论如何,对于任何感兴趣的人,这是我针对此特定情况得出的解决方案:
I decided I wanted to keep the view models separated in two subclasses AddIndividualViewModel and EditIndividualViewModel which only expose commands, rather than trying to manage state in the one class. However I wanted one view so that I'm not duplicating the XAML. I ended up using two DataTemplates and DataTemplateSelector to switch out the action buttons depending on the view model:
我决定将视图模型保持在两个子类 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" />
and a content presenter at the bottom of the form:
以及表单底部的内容演示者:
<ContentPresenter Content="{Binding}"
ContentTemplateSelector="{StaticResource addEditTemplateSelector}" />
Here is the code for the template selector:
这是模板选择器的代码:
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;
}
}
This may or may not be how implement the final thing (given the requirements) but it's good to see I have this sort of option available.
这可能是也可能不是如何实施最终的事情(考虑到要求),但很高兴看到我有这种选择。
回答by msfanboy
For this task you do not need any DataTemplateSelector at all.
对于此任务,您根本不需要任何 DataTemplateSelector。
- Derive both EditIndividualVM and AddINdividualVM from IndividualVM.
- The Edit- and AddCommands route to a setter property in the IndividualVM.
- The setter VM = new AddIndividualVM or VM = new EditIndividualVM depending on which button is pressed.
In xaml you bind in the contentgrid to your VM property like this:
- 从 IndividualVM 派生 EditIndividualVM 和 AddINdividualVM。
- Edit- 和 AddCommands 路由到 IndividualVM 中的 setter 属性。
- 根据按下的按钮,setter VM = new AddIndividualVM 或 VM = new EditIndividualVM。
在 xaml 中,您将 contentgrid 绑定到您的 VM 属性,如下所示:
回答by Aviad P.
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
classthat'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.
作为参考,您可以查看Sacha Barber 的DataWrapper
类,它是他Cinch
框架的一部分(不直接适用于您的情况,但它是一个很好的起点),它以这种方式将数据字段包装在视图模型中,以支持在只读之间切换的标志(查看记录模式)和读写(编辑记录模式)。您可以应用类似的方法来区分添加和编辑。
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.
基本上,不是在视图模型中具有简单的属性,而是实例化一个包含Value
属性和IsAdding
属性的数据包装器类。在您的视图中,您可以使用绑定、触发器或模板选择器来根据该属性修改模板。