使用从这里获得的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);
}
}
答案 0 :(得分:1)
从控件中明确删除行为
Removing a Behavior from a Control状态:
从控件中删除行为时,将触发
OnDetachingFrom
方法,该方法用于执行任何必需的清除操作,例如取消订阅事件以防止内存泄漏。但是,除非通过Behaviors
或Remove
方法修改了控件的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