“你确定吗?”提示。 ViewModel的一部分还是纯粹的视图?
我一直在玩弄“你确定吗? 在我的MVVM WPF应用程序中键入提示。
我倾向于认为这些纯粹是观点的一部分。 如果ViewModel暴露了一个DeleteCommand
,那么我希望这个命令立即删除。
要将这样的提示集成到ViewModel中,必须公开一个单独的RequestDeleteCommand
,一个用于绑定提示的DeletePromptItem
属性,还可以将其作为触发器来显示提示。
即使这样,也没有任何东西停止直接调用DeleteCommand
的unit testing,除非在ViewModel中放置特定的逻辑来要求DeletePromptItem
将作为参数提供的项目与DeleteCommand
相匹配。
但是,这一切似乎像ViewModel对我来说是噪音。 提示是更多的用户界面问题,以防止错误等。对我来说,这表明它应该在视图中确认提示调用DeleteCommand。
有什么想法吗?
这些提示绝对不应该是ViewModel的一部分,但这并不意味着最好的解决scheme就是在View中对它们进行硬编码(即使这是一个非常合理的第一种方法)。
我知道有两种方法可以减lessView和ViewModel之间的耦合:使用交互服务和触发交互请求。 这两个在这里解释得非常好; 你可能想看看。
总的思想是,你可以抽象asynchronous交互是如何完成的,并且可以处理更类似于基于事件的逻辑, 同时允许ViewModel表示它想要作为操作的一部分与用户进行交互 ; 最终的结果是你可以logging这个交互,并unit testing它。
编辑:我应该补充一点,我已经探索了在原型项目中使用Prism 4与交互请求,我对结果感到非常满意 (有了一些框架代码,你甚至可以完全指定在特定的交互请求上会发生什么XAML!)。 我很想给一些片段,但它将不得不等待明天。
但是,这一切似乎像ViewModel对我来说是噪音。 提示是更多的用户界面问题,以防止错误等。对我来说,这表明它应该在视图中确认提示调用DeleteCommand。
我同意; 这样的提示应该在视图中进行处理,因为最终视图是用户正在看到并与之交互的视图,而不是视图模型。 一旦你的视图获得了用户的确认,应该调用DeleteCommand
,那么继续在视图模型中调用它。
我看到它的方式,unit testing与用户交互没有任何关系,除非您正在testing视图本身。
在我看来,提示用户由两部分组成:
- 决定是否显示提示符的逻辑以及应该如何处理结果
- 实际显示提示的代码
第2部分显然不属于ViewModel。
但是第一部分确实属于那里。
为了使这种分离成为可能,我使用了一个ViewModel可以使用的服务,并且可以提供一个特定于我所处环境(WPF,Silverlight,WP7)的实现。
这导致这样的代码:
interface IMessageBoxManager { MessageBoxResult ShowMessageBox(string text, string title, MessageBoxButtons buttons); } class MyViewModel { IMessageBoxManager _messageBoxManager; // ... public void Close() { if(HasUnsavedChanges) { var result = _messageBoxManager.ShowMessageBox( "Unsaved changes, save them before close?", "Confirmation", MessageBoxButtons.YesNoCancel); if(result == MessageBoxResult.Yes) Save(); else if(result == MessageBoxResult.Cancel) return; // <- Don't close window else if(result == MessageBoxResult.No) RevertUnsavedChanges(); } TryClose(); // <- Infrastructure method from Caliburn Micro } }
这个方法不仅可以用来显示一个消息框,而且可以显示其他窗口,就像这个答案中所解释的那样。
我build议通过pipe理模式窗口的服务来做到这一点。 不久前我也遇到过这个问题。 这个博客帮了我很多。
尽pipe这是一个silverlight的post,但与wpf相比,它不应该有太大的差别。
我使用EventAggregator
模式来解决这类问题。
你可以看到它在这里解释
我认为这取决于提示,但总的来说,无论如何,需要提示用户的代码逻辑经常在视图模型中,例如用户按下了一个button来删除一个列表项,一个命令在VM中被触发,逻辑运行,很明显,这可能会影响另一个实体,用户必须select他们想要做的事情,在这一点上,你不应该要求视图提示用户,所以我看不到任何其他select,只能处理它在VM中。 这是我一直感到不安的,但我只是写了一个确认方法在我的基地虚拟机调用对话服务dsiplay提示并返回true或false:
/// <summary> /// A method to ask a confirmation question. /// </summary> /// <param name="messageText">The text to you the user.</param> /// <param name="showAreYouSureText">Optional Parameter which determines whether to prefix the message /// text with "Are you sure you want to {0}?".</param> /// <returns>True if the user selected "Yes", otherwise false.</returns> public Boolean Confirm(String messageText, Boolean? showAreYouSureText = false) { String message; if (showAreYouSureText.HasValue && showAreYouSureText.Value) message = String.Format(Resources.AreYouSureMessage, messageText); else message = messageText; return DialogService.ShowMessageBox(this, message, MessageBoxType.Question) == MessageBoxResult.Yes; }
对我来说,这是我有时在MVVM中无法得到确切答案的那些灰色交叉领域之一,所以我对其他方法感兴趣。
看看这个:
MVVM和确认对话框
我在视图模型中使用了类似的技术,因为我相信它是视图模型的一部分,可以询问是否继续删除或不删除任何视觉对象或视图。 使用所描述的技术,您的模型不会引用任何我不喜欢的视觉引用,而是指某种调用确认对话框或消息框或其他任何types的服务。
我想“你确定吗?”提示属于视图模型,因为它的应用程序逻辑,而不是纯粹的东西像animation等等。
所以最好的select是在delete命令的execute方法中调用“Are you sure” 服务对话框 。
编辑:ViewModel代码
IMessageBox _dialogService;//come to the viewmodel with DI public ICommand DeleteCommand { get { return this._cmdDelete ?? (this._cmdDelete = new DelegateCommand(this.DeleteCommandExecute, this.CanDeleteCommandExecute)); } }
把逻辑放在execute方法中
private void DeleteCommandExecute() { if (!this.CanDeleteCommandExecute()) return; var result = this.dialogService.ShowDialog("Are you sure prompt window?", YesNo); //check result //go on with delete when yes }
对话服务可以是任何你想要的,但在删除之前检查的应用程序逻辑是在你的viewmodel。
我个人认为这只是视图的一部分,因为没有数据
我过去处理它的方式是在ViewModel中放置一个事件,当需要显示对话框时触发这个事件。 视图挂钩到事件中,并处理显示确认对话框,并通过它的EventArgs将结果返回给调用者。