Вот версия ответа @ adabyron, которая предотвращает утечку EventArgs
.
Во-первых, измененный класс EventToCommandBehavior
(теперь это общий абстрактный класс, отформатированный с помощью очистки кода ReSharper).Обратите внимание на новый GetCommandParameter
виртуальный метод и его реализацию по умолчанию:
public abstract class EventToCommandBehavior<TEventArgs> : Behavior<FrameworkElement>
where TEventArgs : EventArgs
{
public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(null, OnEventChanged));
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(null));
public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(false));
private Delegate _handler;
private EventInfo _oldEvent;
public string Event
{
get { return (string)GetValue(EventProperty); }
set { SetValue(EventProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public bool PassArguments
{
get { return (bool)GetValue(PassArgumentsProperty); }
set { SetValue(PassArgumentsProperty, value); }
}
protected override void OnAttached()
{
AttachHandler(Event);
}
protected virtual object GetCommandParameter(TEventArgs e)
{
return e;
}
private void AttachHandler(string eventName)
{
_oldEvent?.RemoveEventHandler(AssociatedObject, _handler);
if (string.IsNullOrEmpty(eventName))
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetEvent(eventName);
if (eventInfo != null)
{
MethodInfo methodInfo = typeof(EventToCommandBehavior<TEventArgs>).GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
_handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo);
eventInfo.AddEventHandler(AssociatedObject, _handler);
_oldEvent = eventInfo;
}
else
{
throw new ArgumentException($"The event '{eventName}' was not found on type '{AssociatedObject.GetType().FullName}'.");
}
}
private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (EventToCommandBehavior<TEventArgs>)d;
if (behavior.AssociatedObject != null)
{
behavior.AttachHandler((string)e.NewValue);
}
}
// ReSharper disable once UnusedMember.Local
// ReSharper disable once UnusedParameter.Local
private void ExecuteCommand(object sender, TEventArgs e)
{
object parameter = PassArguments ? GetCommandParameter(e) : null;
if (Command?.CanExecute(parameter) == true)
{
Command.Execute(parameter);
}
}
}
Далее приведен пример производного класса, который скрывает DragCompletedEventArgs
.Некоторые люди выражали беспокойство по поводу утечки абстракции EventArgs
в их сборку модели представления.Чтобы предотвратить это, я создал интерфейс, который представляет ценности, которые нас интересуют.Интерфейс может жить в сборке модели представления с частной реализацией в сборке пользовательского интерфейса:
// UI assembly
public class DragCompletedBehavior : EventToCommandBehavior<DragCompletedEventArgs>
{
protected override object GetCommandParameter(DragCompletedEventArgs e)
{
return new DragCompletedArgs(e);
}
private class DragCompletedArgs : IDragCompletedArgs
{
public DragCompletedArgs(DragCompletedEventArgs e)
{
Canceled = e.Canceled;
HorizontalChange = e.HorizontalChange;
VerticalChange = e.VerticalChange;
}
public bool Canceled { get; }
public double HorizontalChange { get; }
public double VerticalChange { get; }
}
}
// View model assembly
public interface IDragCompletedArgs
{
bool Canceled { get; }
double HorizontalChange { get; }
double VerticalChange { get; }
}
Приведите параметр команды к IDragCompletedArgs
, аналогично ответу @ adabyron.