Mvvm 如何避免视图模型中的视图特定代码
我的应用程序有一个菜单选项,允许创建一个新帐户。菜单选项的命令绑定到我的ViewModel中的命令(NewAccountCommand)。当用户单击创建新帐户的选项时,应用程序将显示一个“新帐户”对话框,用户可以在其中输入名称、地址等数据。。。然后单击“确定”关闭对话框并创建新帐户 我知道ViewModel中的代码不正确,因为它创建了“新帐户”对话框并调用ShowDialog()。以下是虚拟机的一个片段:Mvvm 如何避免视图模型中的视图特定代码,mvvm,Mvvm,我的应用程序有一个菜单选项,允许创建一个新帐户。菜单选项的命令绑定到我的ViewModel中的命令(NewAccountCommand)。当用户单击创建新帐户的选项时,应用程序将显示一个“新帐户”对话框,用户可以在其中输入名称、地址等数据。。。然后单击“确定”关闭对话框并创建新帐户 我知道ViewModel中的代码不正确,因为它创建了“新帐户”对话框并调用ShowDialog()。以下是虚拟机的一个片段: var modelResult = newAccountDialog.ShowDialo
var modelResult = newAccountDialog.ShowDialog();
if (modelResult == true)
{
//Create the new account
}
如何避免在虚拟机中创建和显示对话框,以便对虚拟机进行单元测试?有不同的方法。一种常见的方法是使用某种形式的依赖项注入来注入对话服务,并使用该服务
这允许在运行时插入该服务的任何实现(即:一个不同的视图),并使您能够从ViewModel中解耦到视图。在类似的场景中,我通常使用事件。该模型可以引发一个事件来询问信息,任何人都可以对此作出响应。视图将侦听事件并显示对话框
public class MyModel
{
public void DoSomething()
{
var e = new SomeQuestionEventArgs();
OnSomeQuestion(e);
if (e.Handled)
mTheAnswer = e.TheAnswer;
}
private string mTheAnswer;
public string TheAnswer
{
get { return mTheAnswer; }
}
public delegate void SomeQuestionHandler(object sender, SomeQuestionEventArgs e);
public event SomeQuestionHandler SomeQuestion;
protected virtual void OnSomeQuestion(SomeQuestionEventArgs e)
{
if (SomeQuestion == null) return;
SomeQuestion(this, e);
}
}
public class SomeQuestionEventArgs
: EventArgs
{
private bool mHandled = false;
public bool Handled
{
get { return mHandled; }
set { mHandled = value; }
}
private string mTheAnswer;
public string TheAnswer
{
get { return mTheAnswer; }
set { mTheAnswer = value; }
}
}
public class MyView
{
private MyModel mModel;
public MyModel Model
{
get { return mModel; }
set
{
if (mModel != null)
mModel.SomeQuestion -= new MyModel.SomeQuestionHandler(mModel_SomeQuestion);
mModel = value;
if (mModel != null)
mModel.SomeQuestion += new MyModel.SomeQuestionHandler(mModel_SomeQuestion);
}
}
void mModel_SomeQuestion(object sender, SomeQuestionEventArgs e)
{
var dlg = new MyDlg();
if (dlg.ShowDialog() != DialogResult.OK) return;
e.Handled = true;
e.TheAnswer = dlg.TheAnswer;
}
}
我喜欢这篇codeproject文章中解释的方法: 它基本上创建了一个WPF对话框控件,该控件可以嵌入到另一个窗口或用户控件的可视树中 然后,它使用一个样式触发器,每当对话框中有内容时,该触发器就会使对话框打开
public class MyModel
{
public void DoSomething()
{
var e = new SomeQuestionEventArgs();
OnSomeQuestion(e);
if (e.Handled)
mTheAnswer = e.TheAnswer;
}
private string mTheAnswer;
public string TheAnswer
{
get { return mTheAnswer; }
}
public delegate void SomeQuestionHandler(object sender, SomeQuestionEventArgs e);
public event SomeQuestionHandler SomeQuestion;
protected virtual void OnSomeQuestion(SomeQuestionEventArgs e)
{
if (SomeQuestion == null) return;
SomeQuestion(this, e);
}
}
public class SomeQuestionEventArgs
: EventArgs
{
private bool mHandled = false;
public bool Handled
{
get { return mHandled; }
set { mHandled = value; }
}
private string mTheAnswer;
public string TheAnswer
{
get { return mTheAnswer; }
set { mTheAnswer = value; }
}
}
public class MyView
{
private MyModel mModel;
public MyModel Model
{
get { return mModel; }
set
{
if (mModel != null)
mModel.SomeQuestion -= new MyModel.SomeQuestionHandler(mModel_SomeQuestion);
mModel = value;
if (mModel != null)
mModel.SomeQuestion += new MyModel.SomeQuestionHandler(mModel_SomeQuestion);
}
}
void mModel_SomeQuestion(object sender, SomeQuestionEventArgs e)
{
var dlg = new MyDlg();
if (dlg.ShowDialog() != DialogResult.OK) return;
e.Handled = true;
e.TheAnswer = dlg.TheAnswer;
}
}
因此,在xaml中,您所要做的就是(其中DialogViewModel是ViewModel中的一个属性):
因此,在单元测试中,您需要做的就是:
MyViewModel model = new MyViewModel();
model.DialogViewModel = new MyDialogViewModel();
model.DialogViewModel.InputProperty = "Here's my input";
//Assert whatever you want...
我个人在ViewModel中创建了一个ICommand属性,用于设置DialogViewModel属性,以便用户可以按下按钮打开对话框
所以我的ViewModel从不调用对话框,它只是实例化一个属性。视图将对此进行解释并显示一个对话框。这背后的美妙之处在于,如果您决定更改视图,并且可能不显示对话框,那么您的ViewModel一点也不需要更改。它将所有用户交互代码推送到视图中它应该位于的位置。创建wpf控件允许我在需要时重复使用它
有很多方法可以做到这一点,我发现这是一个对我有好处的方法 展示了如何实现这一点的具体示例
ViewModel示例应用程序显示了一个电子邮件客户端,您可以在其中打开“电子邮件帐户设置”对话框。它使用依赖注入(MEF),因此您仍然能够对ViewModel进行单元测试
希望这有帮助
jbe这将要求您在视图usercontrol中编写代码,这不是亵渎,但如果可以避免的话,应该这样做。我喜欢这个想法。我想我会试试的。谢谢。这正是我发现MVVM模式更麻烦的地方之一。我不认为这一方面是从以前的技术(例如WinForms)演变而来的,编写数百行代码来提供简单的对话框功能对我来说并不合适。
MyViewModel model = new MyViewModel();
model.DialogViewModel = new MyDialogViewModel();
model.DialogViewModel.InputProperty = "Here's my input";
//Assert whatever you want...