在Xaml中,說到綁定,咱們用的最多的應該就是ICommand了,經過Command實現ViewModel到View之間的命令處理,例如Button默認就提供了Command支持,以下ide
Xaml:this
<Button Content="TestWithCommand" Command="{Binding TestCommand}" />
ViewModelspa
/// <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(); }
public class MainPageViewModel { public ICommand TestCommand { get; private set; } public MainPageViewModel() { TestCommand = new RelayCommand(TestCmd); } public void TestCmd() { Debug.WriteLine("TestCmd"); } }
上面只是一個最簡單的例子,可是若是須要綁定的方法不少的時候,就會有一大堆的ICommand屬性定義,而且也須要初始化,代碼看起來特別臃腫code
下面咱們使用Behavior代替Command完成方法的綁定orm
在WinRT中,Behavior是以組件的方法安裝到VS上的,而在UWP上,官方並無提供對應的組件,咱們能夠經過Nuget添加UWP版本的Behavior組件:blog
http://www.nuget.org/packages/Microsoft.Xaml.Behaviors.Uwp.Managed/事件
使用以下ip
Xamlget
<Button Content="TestWithAction">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="Test"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Button>
ViewModelstring
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); } } }
/// <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); } } }
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