MVVM事件处理程序在哪里

时间:2018-07-15 06:32:31

标签: c# wpf mvvm event-handling

我正在编写一个简单的MVVM应用程序,以研究正确的代码设计。完成它已经花了一段时间,但进展顺利。

关于如何处理事件以及代码是否应该放在ViewModel或隐藏代码中,我有一个疑问。

首先,有两种绑定事件的技术,一种是使用Blend Interactivity DLL绑定到命令,另一种是使用MethodBindingExtension类。

使用交互DLL,它允许使用EventArgs转换器将事件args转换为仅包含我们所需数据的与UI无关的类型。我不认为MethodBindingExtension可以做到这一点,但是它要灵活得多。但是,当您需要设置我想设置的事件args值时,此事件args转换器将无济于事? (或者它可能允许将值转换回,尚未检查那些类)

我喜欢使用MethodBindingExtension,现在我的ViewModel中有这段代码。我不喜欢的是我正在使用特定的UI类型,目前这不是什么大问题,但从理论上讲,也许可以改进。

该怎么办?将其移入代码隐藏区?留给ViewModel并使用args转换器吗?留下这样吗?

public void Window_DropFile(DragEventArgs e) {
    if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
        string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
        foreach (string file in files) {
            ReadScriptFile(file);
        }
    }
}

public void Window_PreviewDragOver(DragEventArgs e) {
    e.Effects = DragDropEffects.All;
    e.Handled = true;
}

public void Header_PreviewLeftMouseButtonDown(IScriptViewModel sender, MouseButtonEventArgs e) {
    if (sender == SelectedItem && sender.CanEditHeader && !sender.IsEditingHeader) {
        sender.IsEditingHeader = true;
        e.Handled = true;
    }
}

3 个答案:

答案 0 :(得分:4)

通常:

您的MVVM视图模型应仅包含数据和命令。当然,有很多例外情况,但是请记住,基本上,它只应包含与视图相关的命令和项目。

当事件处理程序应用于组件时,我对此感到非常困惑。事情是;当在视图模型中放置UI组件的事件处理程序时,视图模型将绑定到视图的实际实现(使用UI组件),而不是通常的“视图”。

根据经验;您应该能够复制过去的视图模型,并在另一个实现中使用它,并且应该对其进行编译。它不应包含对UI元素本身的引用,也不应包含通过事件处理等间接引用。


所以,是的,您应该将这些事件处理程序放在代码背后。一种更实用的方法是:将它们放在最有效的地方,并使代码最易读/可维护的地方。如果您了解MVVM的概念,则可以将这些混合模型的发生率降至最低,这通常就足够了。

答案 1 :(得分:2)

添加了事件处理程序只是为了便于移植旧代码,在纯MVVM中,您根本不需要使用它们。您的MVVM平台将可以将事件定向到视图模型中的命令处理程序,在MVVM Lite中,您可以使用EventToCommand,这是我为在画布上拖动项目而编写的一些代码的摘录:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd ="http://www.galasoft.ch/mvvmlight"
.
.
.
<i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseDown">
        <cmd:EventToCommand Command="{Binding MouseDownCommand}" PassEventArgsToCommand="True" />
    </i:EventTrigger>
    <i:EventTrigger EventName="MouseUp">
        <cmd:EventToCommand Command="{Binding MouseUpCommand}" PassEventArgsToCommand="True" />
    </i:EventTrigger>
    <i:EventTrigger EventName="MouseMove">
        <cmd:EventToCommand Command="{Binding MouseMoveCommand}" PassEventArgsToCommand="True" />
    </i:EventTrigger>
</i:Interaction.Triggers>

请注意,事件参数也已传递到命令处理程序中,这允许视图模型在拖动过程中捕获鼠标。这段代码是在我成为MVVM纯粹主义者之前写的,如今,我还设置了一个转换器(通过EventArgsConverter / EventArgsConverterParameter),以将事件参数类型转换为自定义类型,从而避免从视图模型代码中引用Windows库。这意味着拖动操作也可以进行单元测试。

答案 2 :(得分:0)

我会用附加属性来做这样的事情。例如,对于FileDrop,我将实现一个类似于以下内容的附加属性:

library(dplyr)
library(tidyr)

 dat %>% separate(var2,paste0("var2_",1:4)) %>% 
         gather(variable,var2,starts_with("var2")) %>% group_by(ID, var1) %>% 
         arrange(ID) %>% mutate(N=1:n(), Flag=!is.na(var2) | is.na(var2) & N==1) %>%    
         filter(Flag) %>% select(-variable, -N, -Flag) %>% ungroup()

  # A tibble: 6 x 3
     ID var1  var2 
   <int> <fct> <chr>
1     1 A     NA   
2     2 B     100  
3     2 B     101  
4     2 B     102  
5     3 A     105  
6     4 B     108  

因此,您可以在视图的export上进行设置,并将其链接到视图模型中的ENV CHROME_BIN=/usr/bin/google-chrome 。该命令采用public static class WindowExtensions { public static readonly DependencyProperty ReadScriptFilesCommandProperty = DependencyProperty.RegisterAttached( "ReadScriptFilesCommand", typeof(ICommand), typeof(WindowExtensions), new PropertyMetadata(default(ICommand), OnReadScriptFilesCommandChanged)); private static void OnReadScriptFilesCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Window window = d as Window; if (window == null) return; if (e.NewValue is ICommand) { window.Drop += WindowOnDrop; } if (e.OldValue != null) { window.Drop -= WindowOnDrop; } } private static void WindowOnDrop(object sender, DragEventArgs e) { Window window = sender as Window; if (window == null) return; if (e.Data.GetDataPresent(DataFormats.FileDrop)) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); ICommand readScriptFilesCommand = GetReadScriptFilesCommand(window); readScriptFilesCommand.Execute(files); } } public static void SetReadScriptFilesCommand(DependencyObject element, ICommand value) { element.SetValue(ReadScriptFilesCommandProperty, value); } public static ICommand GetReadScriptFilesCommand(DependencyObject element) { return (ICommand)element.GetValue(ReadScriptFilesCommandProperty); } } 并执行逻辑。