视图模型打开对话
本文关键字:对话 模型 视图 | 更新日期: 2023-09-27 18:35:11
ViewModel 打开附加对话真的有什么问题吗?假设我有一个MainView和一个MainViewModel。MainViewModel 是 MainView 的数据上下文,实际上并不知道或依赖于 MainView 本身。
但是,在某些情况下,主视图需要打开将影响视图模型数据的对话框。例如,我可能想要显示一个对话框并播放一些项目以允许用户从中进行选择。所以,我决定的是:
在我的视图模型中,我有以下方法:AddItem
、EditItem
和 DeleteItem
。但是,为了提供要添加或编辑的项目,我需要在某些对话框中显示一个列表供用户选择。 现在我让 ViewModel 这样做只是因为我不想为这些简单的任务实现额外的抽象级别。让 ViewModel 执行此操作意味着它可以提供要向用户显示的列表,并且在用户完成编辑或选择时,它可以轻松更新其成员集合/属性。
我应该因为这种方法而受到枪杀吗?
射击? 不。 但是有充分的理由不这样做。
首先,它扼杀了 ViewModel 的可测试性,因为现在有一个可视组件。 当您尝试针对它编写自动化单元测试时,您仍然需要与它进行交互。 你可以模拟它,但在调用 UI 方法时,这样做变得更加困难。
其次,您的视图模型不应该关心显示的内容。 当你开始组合这些东西时,有一个真正的"关注点分离"问题。
第三,它只是有一种"代码气味"。
您可以采取一些措施来规避此问题。 我建议的第一件事是不要使用对话框。 对话框有它们的位置,但程序员倾向于过度使用它们。 重新思考你的设计,并尝试弄清楚如何在不打断用户的情况下完成工作。
其次,考虑使用消息传递框架在视图模型和视图之间发送消息,以导航到对话框(如果绝对必须使用它们)。 消息很容易模拟和/或编写单元测试。
方法:使用对话服务 - 易于使用,易于单元测试!
看到这里。
我认为ViewModels相互通信没有任何问题。问题是如果他们开始访问视图或其他对话框,因为这会影响系统的可测试性。
如果你真的想要一个更松散耦合的系统,你可以使用某种消息传递系统进行通信,但我怀疑你在这里需要它:-)
我总是使用 Seelctor 服务(只是一个基本的对话框服务)来执行此操作 - 它是可测试和可模拟的,并保持代码非常可靠。
class ViewModel
{
public ICommand ShowListSelectorForCounterparties { get; set; }
public IListSelectorService ListSelector { get; set; }
public void OnExecuteShowCounterpartySelector()
{
this.Counterparty = this.ListSelector.Select<Counterparty>();
}
}
其中 IListSelectorService 可以在运行时实例化您的对话框、显示您的列表并返回所选项目。以这种方式运行它的主要好处是,你的单元测试可以模拟IListSelectorService。
我不确定您是否仍在寻求任何帮助,但是在对话框方面,我采取的方法是让视图模型引发一个事件,然后视图可以处理该事件。视图现在可以执行任何它想要将数据获取到视图模型的操作,因此您可以毫无问题地在视图中分发对话框。将对话框的响应传递给事件的 EventArgs,以便视图模型具有要查找的数据,以便继续。
例如:
Public Class View
Private WithEvents _VM AS new ViewModel()
Private Sub _VM_AddingItem(Sender AS Object, E AS ViewModel.ItemEventArgs)
Dim Dialog As new SomeDialog()
If Dialog.ShowDialog then
E.Item = Dialog.Item
Else
E.Cancel = True
End If
End Sub
End Class
Public Class ViewModel
Public Sub AddItem(Item AS Object)
Do Some Work here
End Sub
Private Sub _AddItem()
Dim Args AS New ItemEventArgs()
OnAddingItem(Args)
If not Args.Cancel Then AddItem(Args.Item)
End Sub
Protected Sub OnAddingItem()
RaiseEvent AddingItem(me, ItemEventArgs)
End Sub
Public Event AddingItem(Sender AS Object, E As ItemEventArgs)
Public Class ItemEventArgs
Public Property Item AS Object
Public Property Cancel AS Boolean = false
End Class
End Class
然后,只需将您的命令连接到私有_AddItem
方法,该方法仅引发事件以收集AddItem
方法所需的数据。我希望这对:)有所帮助