我在WPF应用程序中使用MVVM。我对两者都很陌生。让我说明我不是MVVM模式中最纯粹的,我试图尽可能多地使用尽可能多的最佳实践,但我正在努力做出我认为合理的妥协,使其在我们的环境中运行。例如,我不想在我的View代码隐藏中实现0%的代码。
我对最佳做法有几个问题。
1)我理解我不希望我的VM了解附加的View,但是View对其VM的引用是否合理?
2)如果视图中的控件打开另一个视图(例如对话框),我应该在视图中处理吗?在VM中处理它似乎是错误的,因为VM知道特定的View。
答案 0 :(得分:13)
1)View绝对通过DataContext引用了ViewModel。并且您可以在视图中投射DataContext:
public class ShellView : Window
{
…
public ShellViewModel { get { return DataContext as ShellViewModel; } }
这不违反Model-View-ViewModel模式。
。
2)你是对的。 ViewModel不应该打开另一个View。更好的方法是使用控制器。他们负责应用程序的工作流程。
如果您对更详细的信息感兴趣,可以查看 WPF Application Framework (WAF) 。
答案 1 :(得分:3)
1)以下是View的“了解”ViewModel的两个简单实践。 View知道ViewModel(用于数据绑定)是合理的 - 但在您的情况下可能不需要它。看看这些方法中的任何一种是否有助于解决您的问还有其他方法,但这些方法应该足够简单:
public View(ViewModel vm)
{
View.DataContext = vm;
}
public Bootstrapper(View v, ViewModel vm)
{
v.DataContext = vm;
//or, if you want it to have no parameters
View v = new View();
ViewModel vm = new ViewModel();
v.DataContext = vm;
}
如果你有一个服务定位工具,第一个选项也不错,但有一种MVVM不喜欢View的Code-Behind中的任何代码。第二种选择也不错,应该足够简单,适合你的任务。
2。)这个问题在MVVM设计中可能有点棘手。如果我们谈论的是一般的Win32 MessageBox,我会经常将该逻辑分成另一个对象并将其放入VM中。这种方式往往更加清晰。 (例如,我在ListBox中选择了一个项目,我已经将删除ICommand附加到该操作,并且在我的ViewModel中执行此ICommand时,我将戳我的MessageBoxObject以询问用户是否“想要真正删除”这个项目)。更高级的“Dialogs”将为这些ViewModel使用额外的ViewModel和DataTemplates。我更喜欢Mediator方法。
答案 2 :(得分:2)
1)。视图需要在某个级别引用视图模型,因为视图模型将充当视图的datacontext。
2)处理此问题的一种方法是使用一个表示对话框的通用视图模型,该视图由主视图模型(用作视图datacontext的那个)拥有。
您可以使用命令来创建对话框视图模型的新实例,该实例将在您的资源中定义相应的数据窗口。此模板将设置为绑定到dialogviewmodel类型。
答案 3 :(得分:2)
很晚,但我觉得这很难得到很多不同的观点。
我知道我不希望我的VM了解附加的View,但是 View是否合理地引用其VM?
正如已经回答的那样,正确的View-ViewModel安排涉及将ViewModel指定为View的DataContext属性。这允许DataBindings从声明性XAML“自动”建立,或通过后面的代码进行微调。
有时候,你会想要在你的代码中写下这样的东西:
var dc = DataContext as CleverViewModel;
CleverViewModel.CleverProperty.Add(someValue); // just a simple example
我认为实现这类事情的正确方法不是转换DataContext,而是:
在View中有一些专用控件,例如ItemsControl及其ItemsSource双向数据绑定到viewmodel中的某些属性:
<ItemsSource x:Name="cleverControl" Visibility="Collapsed" ItemsSource="{Binding CleverProperty, Mode=TwoWay}"/>
在后面的代码中转换绑定属性而不是整个ViewModel:
var collection = (ObservableCollection<double>)cleverControl.ItemsSource;
collection.Add(someValue);
请注意重要的区别:此示例中的第二种方法不要求View知道ViewModel类型,它只需要一个名为CleverProperty
的{{1}}类型的属性。这允许我有多态甚至鸭型的ViewModels。
如果视图中的控件打开另一个视图(例如对话框),我应该 在View中处理这个?从那以后在VM中处理它似乎是错误的 然后,VM了解特定的视图。
这不应该发生在严格的MVVM中,并且使用DataTemplates也不难避免。 DataTemplates将给定类型的DataContext映射到给定类型的视图,因此只要ContentControl的datacontext发生更改,其显示也会发生变化,前提是您拥有该类型的DataTemplate:
视图中的控件可以向ViewModel发送命令,而ViewModel又会更新一些自己的属性,这些属性将被视图反映出来。
< / LI>在ViewModel知识之外,View可以包含另一个View。在这种情况下,后面的代码可以操作包含视图的datacontext。
还有更多细微之处,但我一直在使用这种方法,效果很好。希望这有助于某人。
答案 4 :(得分:1)
我发现Rob Eisenberg提出的方法非常有趣。
关键点:
这与ASP.NET MVC哲学非常相似。
我强烈建议观看视频。