在Xaml中,说到绑定,我们用的最多的应该就是ICommand了,通过Command实现ViewModel到View之间的命令处理,例如Button默认就提供了Command支持,如下
Xaml:
ViewModel
///Provides a base implementation of the public abstract class CommandBase : ICommand { ///interface. Gets a value indicating whether the command can execute in its current state. public abstract bool CanExecute { get; } ///Defines the method to be called when the command is invoked. protected abstract void Execute(); ///Tries to execute the command by checking the ///property /// and executes the command only when it can be executed. True if command has been executed; false otherwise. public bool TryExecute() { if (!CanExecute) return false; Execute(); return true; } ///Occurs when changes occur that affect whether or not the command should execute. public event EventHandler CanExecuteChanged; void ICommand.Execute(object parameter) { Execute(); } bool ICommand.CanExecute(object parameter) { return CanExecute; } } ///Provides an implementation of the public class RelayCommand : CommandBase { private readonly Action _execute; private readonly Funcinterface. _canExecute; /// Initializes a new instance of the /// The action to execute. public RelayCommand(Action execute) : this(execute, null) { } ///class. Initializes a new instance of the /// The action to execute. /// The predicate to check whether the function can be executed. public RelayCommand(Action execute, Funcclass. canExecute) { if (execute == null) throw new ArgumentNullException(nameof(execute)); _execute = execute; _canExecute = canExecute; } /// Defines the method to be called when the command is invoked. protected override void Execute() { _execute(); } ///Gets a value indicating whether the command can execute in its current state. public override bool CanExecute => _canExecute == null || _canExecute(); }
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组件:
使用如下
Xaml
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); } } }
////// 带参数的Action /// [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), typeof (WdCallMethodAction), new PropertyMetadata(new List ())); /// /// 方法名:参数有?,eventArgs,sender,dataContext /// eg:Test /// eg:Test(?,?) /// eg:Test(sender,?,?) /// public string MethodName { get { return (string) GetValue(MethodNameProperty); } set { SetValue(MethodNameProperty, value); } } public ICollectionParameters { get { return (ICollection ) 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 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
Xaml
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