Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/274.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WPF中干净优雅的视图模型_C#_.net_Wpf_Mvvm - Fatal编程技术网

C# WPF中干净优雅的视图模型

C# WPF中干净优雅的视图模型,c#,.net,wpf,mvvm,C#,.net,Wpf,Mvvm,一旦我发现了MVVM,我真的很喜欢它。绑定、将视图与逻辑分离、可测试性等的整个概念非常令人鼓舞。这是一个很好的选择,为混乱,永无止境的代码背后。后来我了解到,有些命令可以绑定,我一开始也很喜欢 虽然我已经使用MVVM编写了几个控件,但我发现我的视图模型开始或多或少地像代码隐藏。充满了几乎完全按照事件处理程序以前在代码隐藏中所做的操作执行的命令 让我举几个例子 有一个带有按钮“Details”的控件,它打开另一个窗口 [方法1] 您可以做的第一件(也是最糟糕的)事情是在命令中调用如下内容: new

一旦我发现了MVVM,我真的很喜欢它。绑定、将视图与逻辑分离、可测试性等的整个概念非常令人鼓舞。这是一个很好的选择,为混乱,永无止境的代码背后。后来我了解到,有些命令可以绑定,我一开始也很喜欢

虽然我已经使用MVVM编写了几个控件,但我发现我的视图模型开始或多或少地像代码隐藏。充满了几乎完全按照事件处理程序以前在代码隐藏中所做的操作执行的命令

让我举几个例子

有一个带有按钮“Details”的控件,它打开另一个窗口

[方法1] 您可以做的第一件(也是最糟糕的)事情是在命令中调用如下内容:

new DetailsWindow().ShowDialog();
这使得视图模型强烈引用表示层-丑陋

[方法2] 让我们使用弱引用修复此问题,并创建类似IDialogService的内容。我们可以注入一个简单的实现来创建和打开窗口。现在我们去掉了对表示层的强引用,命令可以如下所示:

_dialogService.ShowDetailsWindow();
我还是不喜欢这种方法。对我来说,视图模型并不是决定是否显示窗口的因素。它应该服务和处理数据

[方法3] 将视图模型与表示层完全分离的优雅方法是注入命令本身。视图模型将不知道表示层。它只会执行注入的操作——不管它是什么

问题1:

哪种方法最好?我想3号是赢家

问题2:

这是否应该成为视图模型的一部分?我认为不应该,因为这似乎是表示层所关心的。也许最好将其放在代码隐藏中的简单事件处理程序中

第二个例子更复杂。在同一个控件中,我们有一个“删除”按钮。它应该打开一个对话框,要求用户确认,如果他说“是”,它应该删除一些内容。在这种情况下,将其放在视图模型中更有意义,因为它确实会影响数据

问题3

这个案子对我来说是最棘手的。我不能使用我最喜欢的方法编号3,因为我必须显示一个对话框,这是表示层的工作,但是我还必须根据对话框的结果执行一些逻辑,另一方面,这是查看模型的工作。这里最好的方法是什么

请记住,我真的不想使用方法12。我希望视图模型是干净的,并且不知道与表示层相关的任何内容,即使是弱引用

我想到的一件事是将视图模型层分为两层。比如:

视图-->表示视图模型-->逻辑视图模型

  • 表示视图模型
    • 用作控件的上下文
    • 包含逻辑视图模型作为直接绑定的公共属性
    • 使用方法编号2-现在可以接受,因为整个班级都要执行与演示相关的动作
  • 逻辑视图模型
    • 这是“免费演示”
    • 参考专用逻辑服务
    • 有些命令可以直接绑定到视图
    • 某些命令可以由拥有它的表示视图模型执行
也许这是正确的选择

[编辑]

针对评论中关于使用框架的建议:


使用框架肯定会使处理windows变得更容易,但这并不是问题的解决方案。我根本不想让“逻辑视图模型”来处理windows,即使有框架的帮助也不行。参考我在最后建议的方法,我仍然将其放在“演示视图模型”中。

您的视图模型应该通过触发一个简单事件通知所有订阅者该命令已执行。视图应该订阅该事件,并通过显示新窗口来处理该事件

视图模型:

public event EventHandler<NotificationEventArgs<string>> DisplayDetailsNotice;

private DelegateCommand displayDetailsCommand;
public DelegateCommand DisplayDetailsCommand
{
    get { return displayDetailsCommand ?? (displayDetailsCommand = new DelegateCommand(DisplayDetails)); }
    }

public void DisplayDetailsWindow()
{
    //
    Notify(DisplayDetailsNotice, new NotificationEventArgs<string("DisplayDetails"));
}
公共事件事件处理程序DisplayDetailsNotice;
私有DelegateCommand显示详细信息命令;
公共DelegateCommand DisplayDetails命令
{
获取{return displayDetailsCommand??(displayDetailsCommand=new DelegateCommand(DisplayDetails));}
}
public void displaytailswindow()
{
//

Notify(DisplayDetailsNotice,new NotificationEventArg)这不是对问题的直接回答,但您应该研究如何使用MVVM框架(Caliburn.Micro,MVVM Light),因为他们已经为您完成了很多繁琐的工作,比如对话框或导航。例如,Caliburn.Micro有
IWindowManager
界面,可以将指定的视图模型显示为常规窗口或模式窗口。这意味着您只能在视图模型上操作。如果不使用第三方框架,我会使用<代码>\u navigationService.Show(SomeDialogViewModel)
,其中
SomeDialogViewModel
包含dialog需要显示的所有数据,可能还有一些标志来标识它应该显示为对话框。然后
\u navigationService
的工作是确定如何以及在何处显示视图模型,以响应@Patryk所说的,我找到了Caliburn。Micro work这很好。它按照约定解析适当的窗口或用户控件,然后允许您在对话框关闭后检查ViewModel实例以确定其状态。您可以滚动自己的实现;主要的挑战是解析正确的视图。有多种方法可以做到这一点(基于约定、属性、注册映射的方法调用等)IMO最优雅的解决方案是
private readonly MyViewModel mvm;

//Get reference to VM and subscribe to VM event(s) in Constructor

mvm = DataContext as MyViewModel;
if (mvm == null) return;
mvm.DisplayDetailsNotice += DisplayDetails;

private void DisplayDetails(object sender, NotificationEventArgs<string> e)
{
    DetailsWindow = new DetailsWindow { Owner = this };
    DetailsWindow.ShowDialog();
}