zoukankan      html  css  js  c++  java
  • 【UWP】使用Action代替Command

    在Xaml中,说到绑定,我们用的最多的应该就是ICommand了,通过Command实现ViewModel到View之间的命令处理,例如Button默认就提供了Command支持,如下

    Xaml:

        <Button Content="TestWithCommand" Command="{Binding TestCommand}" />

    ViewModel

        /// <summary>Provides a base implementation of the <see cref="ICommand"/> interface. </summary>
        public abstract class CommandBase : ICommand
        {
            /// <summary>Gets a value indicating whether the command can execute in its current state. </summary>
            public abstract bool CanExecute { get; }
    
            /// <summary>Defines the method to be called when the command is invoked. </summary>
            protected abstract void Execute();
    
            /// <summary>Tries to execute the command by checking the <see cref="CanExecute"/> property 
            /// and executes the command only when it can be executed. </summary>
            /// <returns>True if command has been executed; false otherwise. </returns>
            public bool TryExecute()
            {
                if (!CanExecute)
                    return false;
                Execute();
                return true;
            }
    
            /// <summary>Occurs when changes occur that affect whether or not the command should execute. </summary>
            public event EventHandler CanExecuteChanged;
    
            void ICommand.Execute(object parameter)
            {
                Execute();
            }
    
            bool ICommand.CanExecute(object parameter)
            {
                return CanExecute;
            }
        }
    
        /// <summary>Provides an implementation of the <see cref="ICommand"/> interface. </summary>
        public class RelayCommand : CommandBase
        {
            private readonly Action _execute;
            private readonly Func<bool> _canExecute;
    
            /// <summary>Initializes a new instance of the <see cref="RelayCommand"/> class. </summary>
            /// <param name="execute">The action to execute. </param>
            public RelayCommand(Action execute)
                : this(execute, null)
            { }
    
            /// <summary>Initializes a new instance of the <see cref="RelayCommand"/> class. </summary>
            /// <param name="execute">The action to execute. </param>
            /// <param name="canExecute">The predicate to check whether the function can be executed. </param>
            public RelayCommand(Action execute, Func<bool> canExecute)
            {
                if (execute == null)
                    throw new ArgumentNullException(nameof(execute));
    
                _execute = execute;
                _canExecute = canExecute;
            }
    
            /// <summary>Defines the method to be called when the command is invoked. </summary>
            protected override void Execute()
            {
                _execute();
            }
    
            /// <summary>Gets a value indicating whether the command can execute in its current state. </summary>
            public override bool CanExecute => _canExecute == null || _canExecute();
        }
    RelayCommand
        public class MainPageViewModel
        {
            public ICommand TestCommand { get; private set; }
            public MainPageViewModel()
            {
                TestCommand = new RelayCommand(TestCmd);
            }
    
            public void TestCmd()
            {
                Debug.WriteLine("TestCmd");
            }
        }

    上面只是一个最简单的例子,但是如果需要绑定的方法很多的时候,就会有一大堆的ICommand属性定义,并且也需要初始化,代码看起来特别臃肿

    下面我们使用Behavior代替Command完成方法的绑定

    在WinRT中,Behavior是以组件的方法安装到VS上的,而在UWP上,官方并没有提供对应的组件,我们可以通过Nuget添加UWP版本的Behavior组件:

      http://www.nuget.org/packages/Microsoft.Xaml.Behaviors.Uwp.Managed/

    使用如下

    Xaml

        <Button Content="TestWithAction">
            <interactivity:Interaction.Behaviors>
                <core:EventTriggerBehavior EventName="Click">
                    <core:CallMethodAction TargetObject="{Binding}" MethodName="Test"/>
                </core:EventTriggerBehavior>
            </interactivity:Interaction.Behaviors>
        </Button>

    ViewModel

        public class MainPageViewModel
        {
            public void Test()
            {
                Debug.WriteLine("test");
            }
        }

      官方提供的Behavior更加灵活,可以自己配置事件名,和对应的方法名称,并且我们在ViewModel中不需要写ICommand等代码了,看起来更简洁明了,但是还有个问题,

    有时候我们需要用到事件的参数,有时候我们需要用到触发事件的控件,有时候我们还需要控件对应的DataContext,而官方提供的库中并不提供带参数的方法,下面我们对其进行修改一下,让其支持参数传递,并且支持多参数

    自定义一个支持参数Action

        public class Parameter : DependencyObject
        {
            public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
                "Value", typeof (object), typeof (Parameter), new PropertyMetadata(default(object)));
    
            public object Value
            {
                get { return (object) GetValue(ValueProperty); }
                set { SetValue(ValueProperty, value); }
            }
        }
    Parameter
        /// <summary>
        /// 带参数的Action
        /// </summary>
        [ContentProperty(Name = "Parameters")]
        public sealed class WdCallMethodAction : DependencyObject, IAction
        {
            public static readonly DependencyProperty MethodNameProperty = DependencyProperty.Register("MethodName",
                typeof (string), typeof (WdCallMethodAction), new PropertyMetadata(null));
    
            public static readonly DependencyProperty TargetObjectProperty = DependencyProperty.Register("TargetObject",
                typeof (object), typeof (WdCallMethodAction), new PropertyMetadata(null));
    
            public static readonly DependencyProperty ParametersProperty = DependencyProperty.Register(
                "Parameters", typeof (ICollection<Parameter>), typeof (WdCallMethodAction),
                new PropertyMetadata(new List<Parameter>()));
            
            /// <summary>
            /// 方法名:参数有?,eventArgs,sender,dataContext
            ///     eg:Test
            ///     eg:Test(?,?)
            ///     eg:Test(sender,?,?)
            /// </summary>
            public string MethodName
            {
                get { return (string) GetValue(MethodNameProperty); }
                set { SetValue(MethodNameProperty, value); }
            }
    
            public ICollection<Parameter> Parameters
            {
                get { return (ICollection<Parameter>) GetValue(ParametersProperty); }
                set { SetValue(ParametersProperty, value); }
            }
    
            public object TargetObject
            {
                get { return GetValue(TargetObjectProperty); }
                set { SetValue(TargetObjectProperty, value); }
            }
    
            public object Execute(object sender, object parameter)
            {
                InvokeMethod(MethodName, sender, parameter, TargetObject, Parameters);
                return true;
            }
    
            public void InvokeMethod(string methodName, object sender, object eventArgs, object dataContext,
                ICollection<Parameter> parameters)
            {
                var start = methodName.IndexOf('(');
                var end = methodName.IndexOf(')');
    
                MethodInfo methodInfo;
    
                if (start >= 0 && end >= 0)
                {
                    var paras = methodName.Substring(start + 1, end - start - 1)
                        .Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);
    
                    methodName = MethodName.Substring(0, start);
    
                    var allParameter = new List<object>();
                    var enumerator = parameters.GetEnumerator();
                    foreach (var para in paras)
                    {
                        switch (para)
                        {
                            case "?":
                                enumerator.MoveNext();
                                allParameter.Add(enumerator.Current);
                                break;
                            case "eventArgs":
                                allParameter.Add(eventArgs);
                                break;
                            case "sender":
                                allParameter.Add(sender);
                                break;
                            case "dataContext":
                                allParameter.Add(dataContext);
                                break;
                            default:
                                throw new NotImplementedException(string.Format("没有实现该参数:{0}", para));
    
                        }
                    }
    
                    var paramCount = paras.Length;
                    methodInfo = TargetObject.GetType().GetRuntimeMethods()
                        .FirstOrDefault(m => Equals(m.Name, methodName) && m.GetParameters().Count() == paramCount);
    
                    methodInfo.Invoke(TargetObject, allParameter.ToArray());
                }
                else
                {
                    methodInfo = TargetObject.GetType().GetRuntimeMethod(methodName, new Type[0]);
                    methodInfo.Invoke(TargetObject, null);
                }
    
            }
        }
    WdCallMethodAction

    Xaml

        <Button x:Name="btn" Content="TestWithActionAndParameter">
            <interactivity:Interaction.Behaviors>
                <core:EventTriggerBehavior EventName="Click">
                    <command:WdCallMethodAction TargetObject="{Binding}" MethodName="Test(eventArgs, sender, dataContext, ?,?)">
                        <command:Parameter Value="32"/>
                        <!-- 参数暂不支持绑定(下面方式为空)-->
                        <command:Parameter Value="{Binding ElementName=btn}"/>
                    </command:WdCallMethodAction>
                </core:EventTriggerBehavior>
            </interactivity:Interaction.Behaviors>
        </Button>

    ViewModel

        public void Test(RoutedEventArgs eventArgs, FrameworkElement sender, MainPageViewModel dataContext, Parameter param1, object param2)
        {
            Debug.WriteLine("sender:{0}", sender);
            Debug.WriteLine("eventArgs:{0}", eventArgs);
    Debug.WriteLine(
    "dataContext:{0}", dataContext); Debug.WriteLine("param1:{0}", param1); Debug.WriteLine("param2:{0}", param2); }

    注:目前Parameter暂不支持绑定

     demo

      http://files.cnblogs.com/files/bomo/CommandInvokeDemo.zip

  • 相关阅读:
    完爆!用边缘容器,竟能秒级实现团队七八人一周的工作量
    手把手教你使用 cert-manager 签发免费证书
    手把手教你使用 Nginx Ingress 实现金丝雀发布
    Codeforces 534B Covered Path 贪心
    Codeforces 534A Exam 水
    Topcoder open 2015 Round 1A 250 Similars 枚举 + 状压
    Topcoder SRM 654 DIV1 500 FoldingPaper2 递归 + 枚举
    Topcoder SRM655 DIV2 250 BichromeBoard 水
    2015 Google code jam Qualification Round B 枚举 + 贪心
    2015 Google code jam Qualification Round A 水
  • 原文地址:https://www.cnblogs.com/bomo/p/5160710.html
Copyright © 2011-2022 走看看