WPF中System.Windows.Interactivity的使用
背景
在我们进行WPF开发应用程序的时候不可避免的要使用到事件,很多时候没有严格按照MVVM模式进行开发的时候习惯直接在xaml中定义事件,然后再在对应的.cs文件中直接写事件的处理过程,这种处理方式写起来非常简单而且不用过多地处理考虑代码之间是否符合规范,但是我们在写代码的时候如果完全按照WPF规范的MVVM模式进行开发的时候就应该将相应的事件处理写在ViewModel层,这样整个代码才更加符合规范而且层次也更加清楚,更加符合MVVM规范。
常规用法
1 引入命名空间
通过在代码中引入System.Windows.Interactivity.dll,引入了这个dll后我们就能够使用这个里面的方法来将事件映射到ViewModel层了,我们来看看具体的使用步骤,第一步就是引入命名控件
1 |
|
另外还可以通过另外一种方式来引入命名空间,其实这两者间都是对等的。
1 |
|
2 添加事件对应的Command
这里以TextBox的GetFocus和LostFocus为例来进行说明
<TextBox Text="CommandBinding"><i:Interaction.Triggers><i:EventTrigger EventName="LostFocus"><i:InvokeCommandAction Command="{Binding OnTextLostFocus}"CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type TextBox}}}"/></i:EventTrigger><i:EventTrigger EventName="GotFocus"><i:InvokeCommandAction Command="{Binding OnTextGotFocus}"CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type TextBox}}}"/></i:EventTrigger></i:Interaction.Triggers>
</TextBox>
这个里面我们重点来看看这个InvokeCommandAction的代码结构
namespace System.Windows.Interactivity
{public sealed class InvokeCommandAction : TriggerAction<DependencyObject>{public static readonly DependencyProperty CommandProperty;public static readonly DependencyProperty CommandParameterProperty;public InvokeCommandAction();public string CommandName { get; set; }public ICommand Command { get; set; }public object CommandParameter { get; set; }protected override void Invoke(object parameter);}
}
这里我们发现这里我们如果我们定义一个Command的话我们只能够在Command中获取到我们绑定的CommandParameter这个参数,但是有时候我们需要获取到触发这个事件的RoutedEventArgs的时候,通过这种方式就很难获取到了,这个时候我们就需要自己去扩展一个InvokeCommandAction了,这个时候我们应该怎么做呢?整个过程分成三步:
2.1 定义自己的CommandParameter
public class ExCommandParameter
{/// <summary> /// 事件触发源 /// </summary> public DependencyObject Sender { get; set; }/// <summary> /// 事件参数 /// </summary> public EventArgs EventArgs { get; set; }/// <summary> /// 额外参数 /// </summary> public object Parameter { get; set; }
}
这个对象除了封装我们常规的参数外还封装了我们需要的EventArgs属性,有了这个我们就能将当前的事件的EventArgs传递进来了。
2.2 重写自己的InvokeCommandAction
public class ExInvokeCommandAction : TriggerAction<DependencyObject>{private string commandName;public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(ExInvokeCommandAction), null);public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExInvokeCommandAction), null);/// <summary> /// 获得或设置此操作应调用的命令的名称。 /// </summary> /// <value>此操作应调用的命令的名称。</value> /// <remarks>如果设置了此属性和 Command 属性,则此属性将被后者所取代。</remarks> public string CommandName{get{base.ReadPreamble();return this.commandName;}set{if (this.CommandName != value){base.WritePreamble();this.commandName = value;base.WritePostscript();}}}/// <summary> /// 获取或设置此操作应调用的命令。这是依赖属性。 /// </summary> /// <value>要执行的命令。</value> /// <remarks>如果设置了此属性和 CommandName 属性,则此属性将优先于后者。</remarks> public ICommand Command{get{return (ICommand)base.GetValue(ExInvokeCommandAction.CommandProperty);}set{base.SetValue(ExInvokeCommandAction.CommandProperty, value);}}/// <summary> /// 获得或设置命令参数。这是依赖属性。 /// </summary> /// <value>命令参数。</value> /// <remarks>这是传递给 ICommand.CanExecute 和 ICommand.Execute 的值。</remarks> public object CommandParameter{get{return base.GetValue(ExInvokeCommandAction.CommandParameterProperty);}set{base.SetValue(ExInvokeCommandAction.CommandParameterProperty, value);}}/// <summary> /// 调用操作。 /// </summary> /// <param name="parameter">操作的参数。如果操作不需要参数,则可以将参数设置为空引用。</param> protected override void Invoke(object parameter){if (base.AssociatedObject != null){ICommand command = this.ResolveCommand();/** ★★★★★★★★★★★★★★★★★★★★★★★★* 注意这里添加了事件触发源和事件参数* ★★★★★★★★★★★★★★★★★★★★★★★★*/ExCommandParameter exParameter = new ExCommandParameter{Sender = base.AssociatedObject,Parameter = GetValue(CommandParameterProperty),EventArgs = parameter as EventArgs};if (command != null && command.CanExecute(exParameter)){/** ★★★★★★★★★★★★★★★★★★★★★★★★* 注意将扩展的参数传递到Execute方法中* ★★★★★★★★★★★★★★★★★★★★★★★★*/command.Execute(exParameter);}}}private ICommand ResolveCommand(){ICommand result = null;if (this.Command != null){result = this.Command;}else{if (base.AssociatedObject != null){Type type = base.AssociatedObject.GetType();PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);PropertyInfo[] array = properties;for (int i = 0; i < array.Length; i++){PropertyInfo propertyInfo = array[i];if (typeof(ICommand).IsAssignableFrom(propertyInfo.PropertyType) && string.Equals(propertyInfo.Name, this.CommandName, StringComparison.Ordinal)){result = (ICommand)propertyInfo.GetValue(base.AssociatedObject, null);}}}}return result;}}
这个里面的重点是要重写基类中的Invoke方法,将当前命令通过反射的方式来获取到,然后在执行command.Execute方法的时候将我们自定义的ExCommandParameter传递进去,这样我们就能够在最终绑定的命令中获取到特定的EventArgs对象了。
2.3 在代码中应用自定义InvokeCommandAction
<ListBox x:Name="lb_selecthistorymembers" SnapsToDevicePixels="true"ItemsSource="{Binding DataContext.SpecificHistoryMembers,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"HorizontalAlignment="Stretch"ScrollViewer.HorizontalScrollBarVisibility="Disabled"Background="#fff"BorderThickness="1"><i:Interaction.Triggers><i:EventTrigger EventName="SelectionChanged"><interactive:ExInvokeCommandAction Command="{Binding DataContext.OnSelectHistoryMembersListBoxSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}"></interactive:ExInvokeCommandAction></i:EventTrigger></i:Interaction.Triggers>
</ListBox>
注意这里需要首先引入自定义的interactive的命名空间,这个在使用的时候需要注意,另外在最终的Command订阅中EventArgs根据不同的事件有不同的表现形式,比如Loaded事件,那么最终获取到的EventArgs就是RoutedEventArgs对象,如果是TableControl的SelectionChanged事件,那么最终获取到的就是SelectionChangedEventArgs对象,这个在使用的时候需要加以区分。
3 使用当前程序集增加Behavior扩展
System.Windows.Interactivity.dll中一个重要的扩展就是对Behavior的扩展,这个Behavior到底该怎么用呢?我们来看下面的一个例子,我们需要给一个TextBlock和Button增加一个统一的DropShadowEffect,我们先来看看最终的效果,然后再就具体的代码进行分析。
代码分析
1 增加一个EffectBehavior
public class EffectBehavior : Behavior<FrameworkElement>{protected override void OnAttached(){base.OnAttached();AssociatedObject.MouseEnter += AssociatedObject_MouseEnter;AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;}private void AssociatedObject_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e){var element = sender as FrameworkElement;element.Effect = new DropShadowEffect() { Color = Colors.Transparent, ShadowDepth = 2 }; ;}private void AssociatedObject_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e){var element = sender as FrameworkElement;element.Effect = new DropShadowEffect() { Color = Colors.Red, ShadowDepth = 2 };}protected override void OnDetaching(){base.OnDetaching();AssociatedObject.MouseEnter -= AssociatedObject_MouseEnter;AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;}}
这里我们继承自System.Windows.Interactivity中的Behavior<T>这个泛型类,这里我们的泛型参数使用FrameworkElement,因为大部分的控件都是继承自这个对象,我们方便为其统一添加效果,在集成这个基类后我们需要重写基类的OnAttached和OnDetaching方法,这个里面AssociatedObject就是我们具体添加Effect的元素,在我们的示例中这个分别是TextBlock和Button对象。
2 在具体的控件中添加此效果
<Window x:Class="WpfBehavior.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfBehavior"xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Grid><StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"><TextBlock Text="测试文本" Margin="2" Height="30"><i:Interaction.Behaviors><local:EffectBehavior></local:EffectBehavior></i:Interaction.Behaviors></TextBlock><Button Content="测试" Width="80" Height="30" Margin="2"><i:Interaction.Behaviors><local:EffectBehavior></local:EffectBehavior></i:Interaction.Behaviors></Button></StackPanel></Grid>
</Window>
WPF中System.Windows.Interactivity的使用相关推荐
- c# WPF中System.Windows.Interactivity的使用
背景 在我们进行WPF开发应用程序的时候不可避免的要使用到事件,很多时候没有严格按照MVVM模式进行开发的时候习惯直接在xaml中定义事件,然后再在对应的.cs文件中直接写事件的处理过程,这种处理方式 ...
- 关于WPF 中 “System.Windows.Markup.XamlParseException”类型的未经处理的异常在 PresentationFramework.dll 中发生 异常的处理。
关于WPF 中 "System.Windows.Markup.XamlParseException"类型的未经处理的异常在 PresentationFramework.dll 中发 ...
- 【WPF/WAF】使用System.Windows.Interactivity交互事件
[WPF/WAF]使用System.Windows.Interactivity交互事件 原文:[WPF/WAF]使用System.Windows.Interactivity交互事件 下载System. ...
- 在WPF中处理Windows消息
在Winform中 处理Windows消息通过重写WndProc方法 在WPF中 使用的是System.Windows. Sytem.Windows.Controls等名字空间,没有WndProc函数 ...
- 正确在WPF中对Windows窗体背景进行设置
初次接触WPF,就被对窗体的背景图进行设置难住了.跟Winform区别很大, 如果没有正确设置的,会报错. 直接输入添加背景图片代码会弹出异常. <Window.Background> & ...
- WPF中使用Windows.Controls.Ribbon创建一个简单画板
Windows系统自带的画图工具很完善的,如下代码只是演示创建一个简易的画板,可以做教学使用. 整体效果如下: 1. 添加组件System.Windows.Controls.Ribbon 2. 界面设 ...
- 错误1未能加载文件或程序集“System.Windows.Interactivity, Version=4.0.5.0, Culture=neutral, PublicKeyToken=31bf3
如果是win7系统尝试一下用管理员权限打开vs.并且发现所有Usercontrol都无法添加.可能是文件夹权限问题.
- 为何在wpf中textbox的值有时不会实时的变化
TextBox绑定view model 中的一个属性,如果TextBox的值发生改变,但是焦点没有移出TextBox,此时VM中的属性值是没有发生改变的 其实可以在XAML里这么做 xmlns:i=& ...
- Windows Presentation Foundation (WPF)中的命令(Commands)简述
Windows Presentation Foundation (WPF)中的命令(Commands)简述 原文:Windows Presentation Foundation (WPF)中的命令(C ...
最新文章
- 如何用初中知识理解机器学习到底在干什么事情
- 选频放大电路对于150kHz导航信号进行放大检波
- PLSQL_SQL Loader的概念和用法(概念)
- LeetCode 快乐数(Happy Number)
- Jenkins持续集成环境之凭证管理
- 参会全攻略 | 倒计时 7 天!30+ 位重量级嘉宾“聊”什么?
- 数组的扩展-搜集自无忧脚本
- matlab-JDBC操作MYSQL数据库中文乱码解决
- 青蛙跳台(含变种)及汉诺塔递归,母牛生小牛
- epoll实现高并发聊天室
- php vim 补全,Vim 不使用 tags 文件补全 PHP 代码
- (转)KeyDown、KeyUp、KeyPress区别
- 平安夜福利,送3本《从0到1搭建自动化测试框架》
- 这本Python算法书有点火~
- Hive复杂数据类型 struct
- 新闻管理系统(增删改查+分页+阅读+发布评论+删除评论+数据库)
- NX/UG二次开发—装配—抑制的组件怎么获取原型
- python遍历列表中所有元素_python如何遍历列表所有元素?
- mysql 定时任务 每月15号执行
- python修改图片,Python之修改图片像素值的方法