zoukankan      html  css  js  c++  java
  • WPF之事件绑定命令

    事件绑定意义

    一般事件的处理程序都放在界面后台,通过事件绑定可以把事件处理程序放在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?

  • 相关阅读:
    8月工作杂记
    好用的MarkDown编辑器
    Windows下遍历某目录下的文件
    Visual Assist 试用期过期怎么办?
    网易有道面试
    Windows操作系统C盘占用空间过多
    如果有一天我当了面试官
    matlab进行三维重建
    HBase , Doris
    《Java程序设计》第6周学习总结
  • 原文地址:https://www.cnblogs.com/timefiles/p/WpfEventCommandAction.html
Copyright © 2011-2022 走看看