案例是:我有一个控件的事件,我希望我的ViewModel做出反应。目前我正在通过执行隐藏按钮的命令来执行此操作,如下例所示。
在View.xaml中:
<Control x:Name="SearchResultGrid" ... DataRefreshed="SearchResultRefreshed" />
<Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="{Binding SearchResultRefreshedCommand}" />
在View.xaml.cs中:
private void SearchResultRefreshed(object sender, EventArgs e)
{
if (SearchResultRefreshedButton.Command != null)
{
SearchResultRefreshedButton.Command.Execute(SearchResultGrid.ResultRowCount);
}
}
这很好用,但对我来说看起来像是黑客。我想知道是否有更好的(标准)方式这样做?我找不到任何例子,这就是我自己“发明”的。
答案 0 :(得分:8)
使用MVVM,处理事件的一般方法是将它们包装在Attached Properties中,或使用Attached Events。以下是使用附加属性中的PreviewKeyDown
事件的示例:
public static DependencyProperty PreviewKeyDownProperty = DependencyProperty.RegisterAttached("PreviewKeyDown", typeof(KeyEventHandler), typeof(TextBoxProperties), new UIPropertyMetadata(null, OnPreviewKeyDownChanged));
public static KeyEventHandler GetPreviewKeyDown(DependencyObject dependencyObject)
{
return (KeyEventHandler)dependencyObject.GetValue(PreviewKeyDownProperty);
}
public static void SetPreviewKeyDown(DependencyObject dependencyObject, KeyEventHandler value)
{
dependencyObject.SetValue(PreviewKeyDownProperty, value);
}
public static void OnPreviewKeyDownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = dependencyObject as TextBox;
if (e.OldValue == null && e.NewValue != null) textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
else if (e.OldValue != null && e.NewValue == null) textBox.PreviewKeyDown -= TextBox_PreviewKeyDown;
}
private static void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
KeyEventHandler eventHandler = GetPreviewKeyDown(textBox);
if (eventHandler != null) eventHandler(sender, e);
}
请注意,使用ICommand
代替实际KeyEventArgs
对象同样容易(也更好),该对象实际上不应该在视图模型中。只需创建ICommand
类型的附加属性,然后从此TextBox_PreviewKeyDown
处理程序调用该属性:
private static void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
ICommand command = PreviewKeyDownCommand(textBox);
if (command != null && command.CanExecute(textBox)) command.Execute(textBox);
}
无论哪种方式,都可以使用这样的东西:
<TextBox TextBoxProperties.PreviewKeyDown="SomeKeyEventHandler" />
或者,如果您使用首选的ICommand
方法:
<TextBox TextBoxProperties.PreviewKeyDownCommand="{Binding SomeCommand}" />
答案 1 :(得分:2)
就我个人而言,我从未需要使用附加属性来处理控件的事件。在你的例子中,一个控件想要知道何时“SearchResultRefreshed”,然后通过隐藏控件通知ViewModel ...为什么ViewModel不知道结果已被刷新?
如果结果首先来自ViewModel,并且绑定用于在您的控件中显示它们,那么搜索结果已刷新的知识应该由您的ViewModel驱动 - 而不是您的视图。
在少数情况下,我发现需要摆脱ICommands和数据绑定。
答案 2 :(得分:0)
您应该向控件添加依赖项属性DataRefreshed
以便绑定它
这里有一个如何做到的例子
public static readonly DependencyProperty DataRefreshedProperty = DependencyProperty.Register(
"DataRefreshed",
typeof(bool),
typeof("typeof yourcontrol here "),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnDataRefreshedChanged)
)
);
public bool DataRefreshed
{
get { return (bool)GetValue(DataRefreshedProperty); }
set { SetValue(DataRefreshedProperty, value); }
}
然后您可以像任何其他WPF属性一样操纵您的属性,例如在{ViewModel
中定义的SearchResultRefreshed
<Control x:Name="SearchResultGrid" ... DataRefreshed="{Binding SearchResultRefreshed}" />
<Button x:Name="SearchResultRefreshedButton" Visibility="Collapsed" Command="{Binding SearchResultRefreshedCommand}" />
查看以下教程以了解更多dependecyproperty and attachedproperty