如何与EventToCommandBehavior分离?

时间:2019-09-10 11:21:49

标签: c# xamarin

使用从这里获得的EventToCommandBehavior类时,我似乎遇到了重复的事件:https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/behaviors/reusable/event-to-command-behavior

我正在遵循MVVM模式,所以在我的XAML中,我有这个:

<Switch IsToggled="{Binding FilterOptions.IsActive}"
        HorizontalOptions="Center">
    <Switch.Behaviors>
        <behaviour:EventToCommandBehaviour EventName="Toggled" Command="{Binding OnActiveCommand}" CommandParameter="{Binding FilterOptions.IsActive}" />
    </Switch.Behaviors>
</Switch>

在我的ViewModel中,我有这个:

public ICommand OnActiveCommand => new Command<Boolean>(OnActive);
public ICommand OnDiscontinuedCommand => new Command<Boolean>(OnDiscontinued);

private void OnDiscontinued(Boolean value) {
    Debug.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);
    if (!value) {
        FilterOptions.IsActive = false;
    }
}

private void OnActive(Boolean value) {
    Debug.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);
    if (!value) {
        FilterOptions.IsDiscontinued = false;
    }
}

它连接完美,当我与开关交互时,命令按预期方式被调用。但是,我注意到如果我关闭当前页面并返回到该页面,当我与任何开关进行交互时,命令是否重复?

我已经在OnDetachingFrom()(在BahviourBase.cs中)和DeregisterEvent()(在EventToCommandBehaviour.cs)中放置了一个断点,假设如果我关闭页面可以调用这些断点?两者都显示如下:

BahviourBase.cs

public class BehaviourBase<T> : Behavior<T> where T : BindableObject {
    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;
    }

    void OnBindingContextChanged(object sender, EventArgs e) {
        OnBindingContextChanged();
    }

    protected override void OnBindingContextChanged() {
        base.OnBindingContextChanged();
        BindingContext = AssociatedObject.BindingContext;
    }
}

EventToCommandBehaviour.cs

public class EventToCommandBehaviour : BehaviourBase<View> {
    Delegate eventHandler;

    public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehaviour), null, propertyChanged: OnEventNameChanged);
    public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehaviour), null);
    public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehaviour), null);
    public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehaviour), 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);
    }

    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(EventToCommandBehaviour).GetTypeInfo().GetDeclaredMethod("OnEvent");
        eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
        eventInfo.AddEventHandler(AssociatedObject, eventHandler);
    }

    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;
    }

    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);
        }
    }

    static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue) {
        var behavior = (EventToCommandBehaviour)bindable;
        if (behavior.AssociatedObject == null) {
            return;
        }

        string oldEventName = (string)oldValue;
        string newEventName = (string)newValue;

        behavior.DeregisterEvent(oldEventName);
        behavior.RegisterEvent(newEventName);
    }
}

1 个答案:

答案 0 :(得分:1)

从控件中明确删除行为

Removing a Behavior from a Control状态:

  

从控件中删除行为时,将触发OnDetachingFrom方法,该方法用于执行任何必需的清除操作,例如取消订阅事件以防止内存泄漏。但是,除非通过BehaviorsRemove方法修改了控件的Clear集合,否则不会从控件中隐式删除行为。

     

...

     

此外,请注意,从导航堆栈中弹出页面时,不会从控件中隐式删除行为。相反,必须在页面超出范围之前将它们明确删除。

要调用OnDetachingFrom,请删除特定行为或从控件中清除行为集合。

名为Entry的{​​{1}}页面的示例:

Page.xaml:

entry

Page.cs.xaml:

<Entry x:Name="entry" ...
    <Entry.Behaviors>
        <behaviors:EventToCommandBehavior ...

修改BehaviorBase.cs和BindingContext

protected override void OnDisappearing() { base.OnDisappearing(); //var toRemove = entry.Behaviors.FirstOrDefault(b => b is EventToCommandBehavior); //if (toRemove != null) //{ // entry.Behaviors.Remove(toRemove); //} entry.Behaviors.Clear(); } 中替换BehavorBase.cs方法(基于Xamarin Forms Behavior Cleanup Example):

void OnBindingContextChanged(object sender, EventArgs e)

void OnBindingContextChanged(object sender, EventArgs e) { OnBindingContextChanged(); var visualElement = sender as VisualElement; if (visualElement == null) { return; } if (visualElement.BindingContext == null) { OnDetachingFrom(visualElement); } } OnDisappearing

OnAppearing