事件绑定意义

一般事件的处理程序都放在界面后台,通过事件绑定可以把事件处理程序放在ViewModel中,实现界面和逻辑的解耦。

要使用事件绑定需要借助System.Windows.interactivity(安装了Blend就有),如果电脑上找不到,可以通过NuGet安装System.Windows.Interactivity.WPF。

需要引用以下命名空间:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
using System.Windows.Interactivity;

无参数的事件绑定

在Interaction.Triggers里面添加一个或多个EventTrigger并指定关注的的事件名称,在EventTrigger中通过InvokeCommandAction来绑定事件对应的命令,在事件触发后会调用绑定的命令对象的Execute方法执行命令。

命令的实现参考WPF之自定义委托命令,事件触发后是否能够真正执行绑定的命令也受到绑定的命令的CanExecute方法的影响。事件绑定过程如下:

<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

带EventArgs参数的事件绑定

InvokeCommandAction在未对CommandParameter绑定的情况下给Execute方法传递的参数为null,对CommandParameter绑定的情况下给Execute方法传递的参数为绑定值(不是EventArgs参数),Execute方法是由Invoke(object parameter)调用的。

其实,TiggerBase调用InvokeCommandAction的Invoke(object parameter)方法时有传入EventArgs参数,但Invoke调用Execute方法时一直使用的是CommandParameter参数。有一说一,这个程序逻辑有点反人类,这也是网上为什么有这么多重新实现InvokeCommandAction资料的原因。

如果需要从事件的EventArgs中获取数据,正常来说派生InvokeCommandAction然后“重写”Invoke方法即可。但是,InvokeCommandAction是密封类,我们只能参照源码重新实现一个EventCommandAction类,代码如下:

using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity; namespace WpfApp
{
public class EventCommandAction : TriggerAction<DependencyObject>
{
/// <summary>
/// 事件要绑定的命令
/// </summary>
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
} // Using a DependencyProperty as the backing store for MsgName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommandAction), new PropertyMetadata(null)); /// <summary>
/// 绑定命令的参数,保持为空就是事件的参数
/// </summary>
public object CommandParameter
{
get { return (object)GetValue(CommandParateterProperty); }
set { SetValue(CommandParateterProperty, value); }
} // Using a DependencyProperty as the backing store for CommandParateter. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandParateterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventCommandAction), new PropertyMetadata(null)); //执行事件
protected override void Invoke(object parameter)
{
if (CommandParameter != null)
parameter = CommandParameter;
var cmd = Command;
if (cmd != null&&cmd.CanExecute(parameter))
cmd.Execute(parameter);
}
}
}

使用事件绑定

创建一个MainViewModel,这里要使用之前的数据绑定基类BindableBase(参考WPF之数据绑定基类),代码如下:

class MainViewModel:BindableBase
{
public bool CanExecute { get; set; } private string tipText;
public string TipText
{
get { return tipText; }
set { SetProperty(ref tipText, value); }
} public DelegateCommand LoadedCommand { get; } public DelegateCommand<MouseEventArgs> MouseMoveCommand { get; } public MainViewModel()
{
LoadedCommand = new DelegateCommand(() => { MessageBox.Show("程序加载成功");}); MouseMoveCommand = new DelegateCommand<MouseEventArgs>(e =>
{
TipText = "鼠标当前位置:" + e.GetPosition(e.Device.Target).ToString();
},
e =>CanExecute);
}
}

界面的XAML代码如下:

<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseMove">
<local:EventCommandAction Command="{Binding MouseMoveCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<StackPanel>
<CheckBox Content="命令开关" IsChecked="{Binding CanExecute}"/>
<Label Content="{Binding TipText}" Margin="5"/>
</StackPanel>

在后台代码中添加DataContext:

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}

运行程序,效果如下:



扩展:基于InvokeCommandAction源码的实现(推荐)

可以在InvokeCommandAction源码基础改动一下Invoke方法,实现我们的需求,改动如下:

protected override void Invoke(object parameter)
{
if (base.AssociatedObject != null)
{
if (CommandParameter != null)
parameter = CommandParameter;
ICommand command = ResolveCommand();
if (command != null && command.CanExecute(parameter))
{
command.Execute(parameter);
}
}
}

重写的RewriteInvokeCommandAction类完全可以替代上面的EventCommandAction,完整版代码如下:

public sealed class RewriteInvokeCommandAction : TriggerAction<DependencyObject>
{
private string commandName; public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(RewriteInvokeCommandAction), null); public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(RewriteInvokeCommandAction), null); public string CommandName
{
get
{
ReadPreamble();
return commandName;
}
set
{
if (CommandName != value)
{
WritePreamble();
commandName = value;
WritePostscript();
}
}
} public ICommand Command
{
get
{
return (ICommand)GetValue(CommandProperty);
}
set
{
SetValue(CommandProperty, value);
}
} public object CommandParameter
{
get
{
return GetValue(CommandParameterProperty);
}
set
{
SetValue(CommandParameterProperty, value);
}
} protected override void Invoke(object parameter)
{
if (base.AssociatedObject != null)
{
if (CommandParameter != null)
parameter = CommandParameter;
ICommand command = ResolveCommand();
if (command != null && command.CanExecute(parameter))
{
command.Execute(parameter);
}
}
} private ICommand ResolveCommand()
{
ICommand result = null;
if (Command != null)
{
result = Command;
}
else if (base.AssociatedObject != null)
{
Type type = base.AssociatedObject.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo[] array = properties;
foreach (PropertyInfo propertyInfo in array)
{
if (typeof(ICommand).IsAssignableFrom(propertyInfo.PropertyType) && string.Equals(propertyInfo.Name, CommandName, StringComparison.Ordinal))
{
result = (ICommand)propertyInfo.GetValue(base.AssociatedObject, null);
}
}
}
return result;
}
}

参考资料

MVVM设计模式和在WPF中的实现(四)事件绑定

EventTrigger原理浅谈

WPF:MVVM:命令与CallMethodAction?

WPF之事件绑定命令的更多相关文章

  1. WPF中使用System.Windows.Interactivity实现事件绑定的替代方法

    一.问题描述 对于 Button 等控件,在 MVVM 中我们能通过 Command 绑定解决 Click 事件.具体如下所示: <Button Margin="10" He ...

  2. WPF 在事件中绑定命令(不可以在模版中绑定命令)

    其实这也不属于MVVMLight系列中的东东了,没兴趣的朋友可以跳过这篇文章,本文主要介绍如何在WPF中实现将命令绑定到事件中. 上一篇中我们介绍了MVVMLight中的命令的用法,那么仅仅知道命令是 ...

  3. WPF 在事件中绑定命令

    导航:MVVMLight系列文章目录:<关于 MVVMLight 设计模式系列> 其实这也不属于MVVMLight系列中的东东了,没兴趣的朋友可以跳过这篇文章,本文主要介绍如何在WPF中实 ...

  4. 整理:WPF中应用附加事件制作可以绑定命令的其他事件

    原文:整理:WPF中应用附加事件制作可以绑定命令的其他事件 目的:应用附加事件的方式定义可以绑定的事件,如MouseLeftButton.MouseDouble等等 一.定义属于Control的附加事 ...

  5. 整理:WPF用于绑定命令和触发路由事件的自定义控件写法

    原文:整理:WPF用于绑定命令和触发路由事件的自定义控件写法 目的:自定义一个控件,当点击按钮是触发到ViewModel(业务逻辑部分)和Xaml路由事件(页面逻辑部分) 自定义控件增加IComman ...

  6. MVVM设计模式和WPF中的实现(四)事件绑定

    MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  7. WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。

    做过WPF开发的人,都知道做MVVM架构,最麻烦的是Event的绑定,因为Event是不能被绑定的,同时现有的条件下,命令是无法替代Event.而在开发过程中无法避免Event事件,这样MVVM的架构 ...

  8. WPF采用MVVM模式(绑定:纯前台、命令:触发器绑定命令)

    MVVM绑定 view-viewModel-model,模型介绍省略,就是创建类,添加字段封装属性.注:控件的绑定只能绑定到属性上,不能绑定到字段上: 接下来就是代码 (view): <Wind ...

  9. MVVM设计模式和在WPF中的实现(四) 事件绑定

    系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中的实现(三)命令绑定 MVVM模式解析和在WPF中的 ...

随机推荐

  1. 从yield到yield from再到python协程

    yield 关键字 def fib(): a,b = 0,1 while 1: yield b a,b = b,a+b yield是在:PEP 255 -- Simple Generators 这个p ...

  2. ASP.NET Core扩展库之实体映射

    在分层设计模式中,各层之间的数据通常通过数据传输对象(DTO)来进行数据的传递,而大多数情况下,各层数据的定义结构大同小异,如何在这些定义结构中相互转换,之前我们通过使用AutoMapper库,但Au ...

  3. CRC校验原理和verilog实现方法(一)

    1.CRC简介 CRC全称循环冗余校验(Cyclic Redundancy Check, CRC),是通信领域数据传输技术中常用的检错方法,用于保证数据传输的可靠性.网上有关这方面的博客和资料很多,本 ...

  4. js数组reduce解析及使用示例

    reduce() 简单说,reduce()可以对数组中的每个元素执行一个由您提供的reducer函数(升序执行),函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后将其结果汇总为单个返 ...

  5. 清明|TcaplusDB持续为您保驾护航

    清明将至,又到一年休闲踏青,祭拜祖先的时机. 清明假期期间,TcaplusDB不停歇,我们将一如既往地守护您的数据,继续做您最坚实的后盾.  在未来,TcaplusDB还将以国产键值型数据库领航者的身 ...

  6. 第24 章 : Kubernetes API 编程利器:Operator 和 Operator Framework

    Kubernetes API 编程利器:Operator 和 Operator Framework 本节课程主要分享以下三方面的内容: operator 概述 operator framework 实 ...

  7. Shell十三问更新总结版 -- 什么叫做 Shell?-- Shell十三问<第一问>

    Shell十三问更新总结版 简介 ChinaUnix 论坛 Shell 版名为網中人的前辈于 2004 年发布的精华贴,最近回炉这块内容,觉得很多东西讲的实在透彻,非常感谢前辈網中人,同时我个人也对这 ...

  8. [图论]剑鱼行动:kruskal

    剑鱼行动 目录 剑鱼行动 Description Input Output Sample Input Sample Output 解析 难点 代码 Description 给出N个点的坐标,对它们建立 ...

  9. CPU发展史和相关品牌介绍

    CPU发展史和相关品牌介绍 CPU发展已经有40多年的历史了.我们通常将其分成 六个阶段. (1)第一阶段 (1971年-1973年) .这是4位和8位低档微处理器时代,代表产品是Intel 4004 ...

  10. 使用 EPPlus 封装的 excel 表格导入功能 (.net core c#)

    使用 EPPlus 封装的 excel 表格导入功能 前言 最近做系统的时候有很多 excel导入 的功能,以前我前后端都做的时候是在前端解析,然后再做个批量插入的接口 我觉着这样挺好的,后端部分可以 ...