[Xamarin] 將任意事件繫結到命令—EventToCommand
阿新 • • 發佈:2021-07-22
雖然在使用MVVM模式開發的時候,一般都使用Command來繫結事件處理程式,但並不是所有的事件都可以繫結到Command的,這樣一來就造成了程式碼中事件和命令混用的情況
通過附加Behavior可以完成將事件轉化為命令的功能
首先定義一個Behavior基類
public class BehaviorBase<T> : Behavior<T> where T : BindableObject { /// <summary> /// 附加物件 /// </summary> public T AssociatedObject { get; private set; } protected override void OnAttachedTo(T bindable) { base.OnAttachedTo(bindable); AssociatedObject = bindable; if (bindable.BindingContext != null) { BindingContext = bindable.BindingContext; } bindable.BindingContextChanged += OnBindingContextChanged; } protected override void OnDetachingFrom(T bindable) { base.OnDetachingFrom(bindable); bindable.BindingContextChanged -= OnBindingContextChanged; AssociatedObject = null; } private void OnBindingContextChanged(object sender, EventArgs e) { OnBindingContextChanged(); } protected override void OnBindingContextChanged() { base.OnBindingContextChanged(); BindingContext = AssociatedObject.BindingContext; } }
接下來實現EventToCommandBehavior
public class EventToCommandBehavior : BehaviorBase<View> { private Delegate eventHandler; public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged); public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null); public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null); public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null); public string EventName { get { return (string)GetValue(EventNameProperty); } set { SetValue(EventNameProperty, value); } } public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public object CommandParameter { get { return GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } public IValueConverter Converter { get { return (IValueConverter)GetValue(InputConverterProperty); } set { SetValue(InputConverterProperty, value); } } protected override void OnAttachedTo(View bindable) { base.OnAttachedTo(bindable); RegisterEvent(EventName); } protected override void OnDetachingFrom(View bindable) { DeregisterEvent(EventName); base.OnDetachingFrom(bindable); } private void RegisterEvent(string name) { if (string.IsNullOrWhiteSpace(name)) { return; } EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name); if (eventInfo == null) { throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName)); } MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent"); eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this); eventInfo.AddEventHandler(AssociatedObject, eventHandler); } private void DeregisterEvent(string name) { if (string.IsNullOrWhiteSpace(name)) { return; } if (eventHandler == null) { return; } EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name); if (eventInfo == null) { throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName)); } eventInfo.RemoveEventHandler(AssociatedObject, eventHandler); eventHandler = null; } /// <summary> /// 事件處理程式,使用委託呼叫 /// </summary> /// <param name="sender"></param> /// <param name="eventArgs"></param> private void OnEvent(object sender, object eventArgs) { if (Command == null) { return; } object resolvedParameter; if (CommandParameter != null) { resolvedParameter = CommandParameter; } else if (Converter != null) { resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null); } else { resolvedParameter = eventArgs; } if (Command.CanExecute(resolvedParameter)) { Command.Execute(resolvedParameter); } } private static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue) { var behavior = (EventToCommandBehavior)bindable; if (behavior.AssociatedObject == null) { return; } string oldEventName = (string)oldValue; string newEventName = (string)newValue; behavior.DeregisterEvent(oldEventName); behavior.RegisterEvent(newEventName); } }
在xaml中使用
<CheckBox x:Name="SingleCheckBox" Grid.Column="0" IsChecked="{Binding IsChoose}"> <CheckBox.Behaviors> <local:EventToCommandBehavior EventName="CheckedChanged" Command="{Binding Source = {x:Reference ShoppingCar},Path=BindingContext.SelectSingle}" CommandParameter="{Binding Source={x:Reference SingleCheckBox}, Path=IsChecked}"> </local:EventToCommandBehavior> </CheckBox.Behaviors> </CheckBox>