WPF实现MVVM的事件绑定

<i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseLeftButtonDown">
                  <i:InvokeCommandAction Command="{Binding DataContext.ImgClickCommand, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"  
                CommandParameter="{Binding ElementName=gd_img}"/>
            </i:EventTrigger>
</i:Interaction.Triggers>

这是调用原有的方法来实现事件命令的绑定,该代码部分把MouseLeftButtonDown事件绑定到了ImgClickCommand命令下,并把gd_img这个Grid传递过去。一般情况下传递一个参数或者不传可以满足大多情况,但针对业务较复杂的需求一个参数的传递已经不足以满足我们的需求。下面提供两个实现方案来解决这一问题。

第一种方式:重写InvokeCommandAction来扩充返回的参数

    public class EventToCommand : TriggerAction<DependencyObject>
    {
        private string commandName;
        public readonly static DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommand), null);
        public readonly static DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventToCommand), new PropertyMetadata(null, (DependencyObject s, DependencyPropertyChangedEventArgs e) =>
        {
            EventToCommand sender = s as EventToCommand;
            if (sender == null)
            {
                return;
            }
            if (sender.AssociatedObject == null)
            {
                return;
            }
        }));

        /// <summary>
        /// 获取或设置此操作应调用的命令。这是依赖属性。
        /// </summary>
        /// <value>要执行的命令。</value>
        /// <remarks>如果设置了此属性和 CommandName 属性,则此属性将优先于后者。</remarks>
        public ICommand Command
        {
            get
            {
                return (ICommand)base.GetValue(EventToCommand.CommandProperty);
            }
            set
            {
                base.SetValue(EventToCommand.CommandProperty, value);
            }
        }

        /// <summary>
        /// 获得或设置命令参数。这是依赖属性。
        /// </summary>
        /// <value>命令参数。</value>
        /// <remarks>这是传递给 ICommand.CanExecute 和 ICommand.Execute 的值。</remarks>
        public object CommandParameter
        {
            get
            {
                return base.GetValue(EventToCommand.CommandParameterProperty);
            }
            set
            {
                base.SetValue(EventToCommand.CommandParameterProperty, value);
            }
        }

        /// <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>
        /// <param name="parameter">操作的参数。如果操作不需要参数,则可以将参数设置为空引用。</param>
        protected override void Invoke(object parameter)
        {
            if (base.AssociatedObject == null)
                return;
            ICommand command = this.ResolveCommand();

            /*
             * ★★★★★★★★★★★★★★★★★★★★★★★★
             * 注意这里添加了事件触发源和事件参数
             * ★★★★★★★★★★★★★★★★★★★★★★★★
             */
            ExCommandParameter exParameter = new ExCommandParameter
            {
                Sender = base.AssociatedObject,
                //Parameter = GetValue(CommandParameterProperty),
                Parameter = this.CommandParameter,
                EventArgs = parameter as EventArgs
            };

            if (command != null && command.CanExecute(exParameter))
            {
                /*
                 * ★★★★★★★★★★★★★★★★★★★★★★★★
                 * 注意将扩展的参数传递到Execute方法中
                 * ★★★★★★★★★★★★★★★★★★★★★★★★
                 */
                command.Execute(exParameter);
            }
        }

        private ICommand ResolveCommand()
        {
            if (this.Command != null)
                return this.Command;
            if (base.AssociatedObject == null)
                return null;
            ICommand result = null;
            Type type = base.AssociatedObject.GetType();
            PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
            for (int i = 0; i < properties.Length; i++)
            {
                PropertyInfo propertyInfo = properties[i];
                if (typeof(ICommand).IsAssignableFrom(propertyInfo.PropertyType) && string.Equals(propertyInfo.Name, this.CommandName, StringComparison.Ordinal))
                {
                    result = (ICommand)propertyInfo.GetValue(base.AssociatedObject, null);
                    break;
                }
            }
            return result;
        }
    }

 EventToCommand 类是我参考大神的博客【1】以及反编译系统类仿照着来写的,基本变化不大,改动体现在invoke方法里,对返回的参数进行了打包(把数据封装成了类)

 public class ExCommandParameter
    {
        /// <summary>
		/// 事件触发源
		/// </summary>
		public DependencyObject Sender { get; set; }
        /// <summary>
        /// 事件参数
        /// </summary>
        public EventArgs EventArgs { get; set; }
        /// <summary>
        /// 额外参数
        /// </summary>
        public object Parameter { get; set; }

    }

ExCommandParameter 类就是我们扩充的数据类

界面上的调用方式为:

首先要引入命名空间,如:

xmlns:loc="clr-namespace:WpfProgect.Base"

然后我们就可以为所欲为的调用了,如下: 

<i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <!--★★★扩展的InvokeCommandAction★★★-->
                    <loc:EventToCommand Command="{Binding StretchSelectionChangedCommand}" 
CommandParameter ="{Binding ElementName=sampleViewBox}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>

后台使用方法:

首先需要在ViewModel里进行命令绑定的初始化,如:

StretchSelectionChangedCommand = new DelegateCommand() 
{ ExecuteActionObj = new Action<object>(StretchSelectionChanged) };

当然,具体实现方式要根据自己编写的DelegateCommand类来决定。

绑定的StretchSelectionChanged方法实现如下:

private void StretchSelectionChanged(object obj)
        {
            ComboBox cbStretch = ((ExCommandParameter)obj).Sender as ComboBox;
            Viewbox sampleViewBox = ((ExCommandParameter)obj).Parameter as Viewbox;
            if (cbStretch.SelectedItem != null)
            {
                sampleViewBox.Stretch = uiModel.StretchMode;
            }
        }

第二种方式:运用Behavior来实现事件,再运用视图树VisualTree来找所需的父控件或者子控件(控件到手了,就可以取到所需的参数),或者通过写扩展属性的方式来获取控件,以下Demo是通过写扩展属性来实现的。

同样的我们需要引入命名空间来使用我们的SliderBehavior

xaml调用方式如下:

<Slider x:Name="HSlider" Minimum="0" Maximum="100"  Height="24" Margin="79,0,91,42" VerticalAlignment="Bottom" Width="150">
            <i:Interaction.Behaviors>
                <behav:SliderBehavior TargetGrid="{Binding ElementName=theContainer}" 
TargetViewBox="{Binding ElementName=sampleViewBox}"/>
            </i:Interaction.Behaviors>
        </Slider>

SliderBehavior类如下:

 class SliderBehavior : Behavior<Slider>
    {
        public readonly static DependencyProperty TargetGridProperty = DependencyProperty.Register("TargetGrid", typeof(Grid), typeof(SliderBehavior), null);
        public readonly static DependencyProperty TargetViewBoxProperty = DependencyProperty.Register("TargetViewBox", typeof(Viewbox), typeof(SliderBehavior), null);
        /// <summary>
        /// 获得或设置命令参数。这是依赖属性。
        /// </summary>
        /// <value>命令参数。</value>
        /// <remarks>这是传递给 ICommand.CanExecute 和 ICommand.Execute 的值。</remarks>
        public Grid TargetGrid
        {
            get
            {
                return (Grid)base.GetValue(SliderBehavior.TargetGridProperty);
            }
            set
            {
                base.SetValue(SliderBehavior.TargetGridProperty, value);
            }
        }
        public Viewbox TargetViewBox
        {
            get
            {
                return (Viewbox)base.GetValue(SliderBehavior.TargetViewBoxProperty);
            }
            set
            {
                base.SetValue(SliderBehavior.TargetViewBoxProperty, value);
            }
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.ValueChanged += new RoutedPropertyChangedEventHandler<double>(HSlider_ValueChanged);
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.ValueChanged -= HSlider_ValueChanged;
        }

        void HSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (this.AssociatedObject.Name == "HSlider")
                TargetViewBox.Width = TargetGrid.ActualWidth * this.AssociatedObject.Value / 100.0;
            else TargetViewBox.Height = TargetGrid.ActualHeight * this.AssociatedObject.Value / 100.0;
        }
    }

Ps:看的时候可以忽略事件的实现内容,具体看下实现流程与调用方式。

参考博客:【1】https://blog.csdn.net/qing2005/article/details/6680047

 

Demo下载地址:https://download.csdn.net/download/iqqqqu/10924283

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值