1

ViewModel 打开附加对话真的有什么问题吗?假设我有一个 MainView 和一个 MainViewModel。MainViewModel 是 MainView 的数据上下文,实际上并不知道或对 mainview 本身有任何依赖。

但是,在某些情况下,主视图需要打开会影响 ViewModel 数据的对话。例如,我可能想要显示一个对话并显示一些项目以允许用户从中进行选择。所以,我决定的是:

在我的 ViewModel 中,我有以下方法:AddItemEditItemDeleteItem。但是,为了提供要添加或编辑的项目,我需要在一些对话框中显示一个列表供用户选择。现在我让 ViewModel 这样做只是因为我不想为这些简单的任务实现额外的抽象级别。让 ViewModel 这样做意味着它可以向用户提供要显示的列表,并且当用户完成编辑或选择时,它可以轻松更新其成员集合/属性。

我应该被枪杀以解决这种方法吗?

4

5 回答 5

4

射击?不,但有充分的理由不这样做。

首先,它会破坏 ViewModel 的可测试性,因为现在有一个可视化组件。当您尝试针对它编写自动化单元测试时,您仍然必须与它进行交互。您可以将其模拟出来,但是当您调用 UI 方法时,这样做会变得更加困难。

其次,您的视图模型不应该关心显示的内容。当你开始组合这些东西时,会有一个真正的“关注点分离”问题。

第三,它只是有一种“代码味道”。

您可以采取一些措施来规避此问题。我建议的第一件事是不要使用 dialogs。对话框有它们的位置,但程序员倾向于过度使用它们。重新考虑您的设计,并尝试弄清楚如何在不打扰用户的情况下完成工作。

其次,考虑使用消息传递框架在视图模型和视图之间发送消息以导航到对话框(如果您绝对必须使用它们)。消息很容易模拟和/或编写单元测试。

于 2012-05-24T19:30:55.367 回答
1

做到这一点的简单方法:使用对话服务- 易于使用,易于单元测试!

看到这个

于 2012-05-25T06:37:52.917 回答
0

我总是使用 Seelctor 服务(只是一个基本的对话服务)来执行此操作 - 它是可测试的和可模拟的,并且使代码非常可靠。

class ViewModel
{
    public ICommand ShowListSelectorForCounterparties { get; set; }

    public IListSelectorService ListSelector { get; set; }

    public void OnExecuteShowCounterpartySelector()
    {
        this.Counterparty = this.ListSelector.Select<Counterparty>();
    }
}

其中 IListSelectorService 可以在运行时实例化您的对话框,显示您的列表并返回所选项目。以这种方式运行它的主要好处是您的单元测试可以模拟 IListSelectorService。

于 2012-05-25T09:19:19.253 回答
0

我不确定您是否仍在寻求任何帮助,但我在对话框方面采取的方法是让视图模型引发视图可以处理的事件。视图现在可以做任何它想要将数据获取到视图模型的操作,因此您可以毫无问题地在视图中显示对话框。您将对话框中的响应传递给事件的 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方法。我希望这有帮助 :)

于 2012-08-10T13:03:40.607 回答
0

我认为 ViewModel 之间的通信没有任何问题。问题是他们是否开始访问视图或其他对话框,因为这会影响系统的可测试性。

如果您真的想要一个更松散耦合的系统,您可以使用某种消息传递系统进行通信,但我怀疑您在这里需要它:-)

于 2012-05-24T19:29:02.137 回答