在后面的代码中访问ViewModel是否总是违反MVVM模式?

我真正不确定的一件事是如何将鼠标事件正确传递给ViewModel。 有一种使用交互扩展来绑定触发器的方法,例如: 从View到ViewModel的WPF事件绑定?

但是这并没有将MouseEventArgs转发给我所知,而且这个解决方案对我来说似乎并不优雅。

那么什么是正确的解决方案? 一种方法是注册事件并在后面的代码中处理它,例如:

private void ListBox_PreviewMouseDown(object sender, System.Windows.Input.MouseEventArgs e) { var listbox = sender as ListBox; if (listbox == null) return; var vm = listbox.DataContext as MainViewModel; if (vm == null) return; // access the source of the listbox in viewmodel double x = e.GetPosition(listbox).X; double y = e.GetPosition(listbox).Y; vm.Nodes.Add(new Node(x, y)); } 

这里我假设列表框的ItemsSource绑定到vm.Nodes属性。 所以问题是:这是正确的做法吗? 还是有更好的?

我认为你的方法很好。 如果处理程序通过ViewModel工作,那些与View一起使用的事件可以在您的代码隐藏中。 但是,还有另一种用途GalaSoft.MvvmLight ( 链接下载 ),其中有EventToCommand ,支持参数PassEventArgsToCommand

使用示例:

  

我认为你可以使用这两种方法。 您的解决方案很简单,不需要使用任何框架但使用代码隐藏,在这种情况下它并不重要。 有一点是肯定的,建议不要保留ViewModel事件处理程序,使用命令或在View端存储这些处理程序。

Some new notes

我认为,你的方式不违反MVVM的原则,所有使用View事件处理程序都应该在View的一边,主要的事情 – 它的事件处理程序需要使用ViewModel并通过接口具有依赖性,但不直接与UI。

你破坏的唯一原则MVVM是“无代码”的口头禅,这不是MVVM的主要原则。 主要原则:

  • 拆分数据Model

  • 应用程序逻辑不应与UI相关联

  • 支持可测试性代码

一旦代码隐藏违反了这些原则中的至少一个,您必须已经看到了解决问题的替代方案。

此外,您可以在此链接上阅读有关它的意见:

WPF MVVM代码背后

很好的时机,我写了一些代码,大约两个小时前完成这个。 你确实可以传递参数,而且我个人觉得它很优雅,因为它允许你完全测试你的用户界面。 MVVM Lite允许您使用EventToCommand将事件绑定到命令,因此首先将相关的命名空间添加到控件/窗口:

 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:cmd ="http://www.galasoft.ch/mvvmlight" 

现在将事件触发器添加到要拦截其事件的子控件:

              

在我的特定情况下,我将一组项目渲染到canvas上,因此我使用了ItemsControl,但它可以在包括父窗口在内的任何东西上工作。 它也适用于击键(例如KeyDown),但是如果您的子控件不能聚焦,那么您必须将触发器添加到父级。 无论如何,剩下的就是将相关的处理程序添加到您的视图模型中:

 public class MyViewModel : ViewModelBase { public ICommand MouseDownCommand { get; set; } public ICommand MouseUpCommand { get; set; } public ICommand MouseMoveCommand { get; set; } public ICommand KeyDownCommand { get; set; } // I'm using a dependency injection framework which is why I'm // doing this here, otherwise you could do it in the constructor [InjectionMethod] public void Init() { this.MouseDownCommand = new RelayCommand(args => OnMouseDown(args)); this.MouseUpCommand = new RelayCommand(args => OnMouseUp(args)); this.MouseMoveCommand = new RelayCommand(args => OnMouseMove(args)); this.KeyDownCommand = new RelayCommand(args => OnKeyDown(args)); } private void OnMouseDown(MouseButtonEventArgs args) { // handle mouse press here } // OnMouseUp, OnMouseMove and OnKeyDown handlers go here } 

我要提到的最后一件事只是有点偏离主题,通常你需要回传给代码隐藏,例如当用户按下鼠标左键时你可能需要捕获鼠标,但这可以很容易通过附加行为来完成。 鼠标捕获行为很简单,只需将“MouseCaptured”布尔属性添加到视图模型中,将附加行为绑定到它并让其更改处理程序相应地响应。 对于任何更复杂的事情,您可能希望在视图模型中创建一个事件,然后您的附加行为可以订阅。 无论哪种方式,您的UI现在都完全可以进行unit testing,并且您的代码隐藏已经转移到通用行为中,以便在其他类中重复使用。