我正在编写一个简单的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;
}
}
答案 0 :(得分:4)
通常:
您的MVVM视图模型应仅包含数据和命令。当然,有很多例外情况,但是请记住,基本上,它只应包含与视图相关的命令和项目。
当事件处理程序应用于组件时,我对此感到非常困惑。事情是;当在视图模型中放置UI组件的事件处理程序时,视图模型将绑定到视图的实际实现(使用UI组件),而不是通常的“视图”。
根据经验;您应该能够复制过去的视图模型,并在另一个实现中使用它,并且应该对其进行编译。它不应包含对UI元素本身的引用,也不应包含通过事件处理等间接引用。
答案 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);
}
}
并执行逻辑。