Files
HandyControl/src/Shared/System.Windows.Interactivity/EventTriggerBase.cs
T

313 lines
11 KiB
C#

using System;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Windows;
namespace HandyControl.Interactivity;
public abstract class EventTriggerBase : TriggerBase
{
public static readonly DependencyProperty SourceNameProperty = DependencyProperty.Register("SourceName",
typeof(string), typeof(EventTriggerBase), new PropertyMetadata(OnSourceNameChanged));
public static readonly DependencyProperty SourceObjectProperty = DependencyProperty.Register("SourceObject",
typeof(object), typeof(EventTriggerBase), new PropertyMetadata(OnSourceObjectChanged));
private MethodInfo _eventHandlerMethodInfo;
internal EventTriggerBase(Type sourceTypeConstraint) : base(typeof(DependencyObject))
{
SourceTypeConstraint = sourceTypeConstraint;
SourceNameResolver = new NameResolver();
RegisterSourceChanged();
}
protected sealed override Type AssociatedObjectTypeConstraint
{
get
{
if (TypeDescriptor.GetAttributes(GetType())[typeof(TypeConstraintAttribute)] is TypeConstraintAttribute attribute)
return attribute.Constraint;
return typeof(DependencyObject);
}
}
private bool IsLoadedRegistered { get; set; }
private bool IsSourceChangedRegistered { get; set; }
private bool IsSourceNameSet
{
get
{
if (string.IsNullOrEmpty(SourceName))
return ReadLocalValue(SourceNameProperty) != DependencyProperty.UnsetValue;
return true;
}
}
public object Source
{
get
{
object associatedObject = AssociatedObject;
if (SourceObject != null)
return SourceObject;
if (IsSourceNameSet)
{
associatedObject = SourceNameResolver.Object;
if (associatedObject != null && !SourceTypeConstraint.IsInstanceOfType(associatedObject))
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
ExceptionStringTable.RetargetedTypeConstraintViolatedExceptionMessage, GetType().Name,
associatedObject.GetType(), SourceTypeConstraint, "Source"));
}
return associatedObject;
}
}
public string SourceName
{
get =>
(string) GetValue(SourceNameProperty);
set => SetValue(SourceNameProperty, value);
}
private NameResolver SourceNameResolver { get; }
public object SourceObject
{
get =>
GetValue(SourceObjectProperty);
set => SetValue(SourceObjectProperty, value);
}
protected Type SourceTypeConstraint { get; }
protected abstract string GetEventName();
private static bool IsValidEvent(EventInfo eventInfo)
{
var eventHandlerType = eventInfo.EventHandlerType;
if (!typeof(Delegate).IsAssignableFrom(eventInfo.EventHandlerType))
return false;
var parameters = eventHandlerType.GetMethod("Invoke")?.GetParameters();
return parameters != null && parameters.Length == 2 && typeof(object).IsAssignableFrom(parameters[0].ParameterType) && typeof(EventArgs).IsAssignableFrom(parameters[1].ParameterType);
}
protected override void OnAttached()
{
base.OnAttached();
var associatedObject = AssociatedObject;
var behavior = associatedObject as Behavior;
var element = associatedObject as FrameworkElement;
RegisterSourceChanged();
if (behavior != null)
{
behavior.AssociatedObjectChanged += OnBehaviorHostChanged;
}
else if (SourceObject != null || element == null)
{
try
{
OnSourceChanged(null, Source);
}
catch (InvalidOperationException)
{
}
}
else
{
SourceNameResolver.NameScopeReferenceElement = element;
}
if (string.Compare(GetEventName(), "Loaded", StringComparison.Ordinal) == 0 && element != null &&
!Interaction.IsElementLoaded(element))
RegisterLoaded(element);
}
private void OnBehaviorHostChanged(object sender, EventArgs e)
{
SourceNameResolver.NameScopeReferenceElement =
((IAttachedObject) sender).AssociatedObject as FrameworkElement;
}
protected override void OnDetaching()
{
base.OnDetaching();
var associatedObject = AssociatedObject as Behavior;
var associatedElement = AssociatedObject as FrameworkElement;
try
{
OnSourceChanged(Source, null);
}
catch (InvalidOperationException)
{
}
UnregisterSourceChanged();
if (associatedObject != null)
associatedObject.AssociatedObjectChanged -= OnBehaviorHostChanged;
SourceNameResolver.NameScopeReferenceElement = null;
if (string.Compare(GetEventName(), "Loaded", StringComparison.Ordinal) == 0 && associatedElement != null)
UnregisterLoaded(associatedElement);
}
protected virtual void OnEvent(EventArgs eventArgs)
{
InvokeActions(eventArgs);
}
private void OnEventImpl(object sender, EventArgs eventArgs)
{
OnEvent(eventArgs);
}
internal void OnEventNameChanged(string oldEventName, string newEventName)
{
if (AssociatedObject != null)
{
var source = Source as FrameworkElement;
if (source != null && string.Compare(oldEventName, "Loaded", StringComparison.Ordinal) == 0)
UnregisterLoaded(source);
else if (!string.IsNullOrEmpty(oldEventName))
UnregisterEvent(Source, oldEventName);
if (source != null && string.Compare(newEventName, "Loaded", StringComparison.Ordinal) == 0)
RegisterLoaded(source);
else if (!string.IsNullOrEmpty(newEventName))
RegisterEvent(Source, newEventName);
}
}
private void OnSourceChanged(object oldSource, object newSource)
{
if (AssociatedObject != null)
OnSourceChangedImpl(oldSource, newSource);
}
internal virtual void OnSourceChangedImpl(object oldSource, object newSource)
{
if (!string.IsNullOrEmpty(GetEventName()) &&
string.Compare(GetEventName(), "Loaded", StringComparison.Ordinal) != 0)
{
if (oldSource != null && SourceTypeConstraint.IsInstanceOfType(oldSource))
UnregisterEvent(oldSource, GetEventName());
if (newSource != null && SourceTypeConstraint.IsInstanceOfType(newSource))
RegisterEvent(newSource, GetEventName());
}
}
private static void OnSourceNameChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var base2 = (EventTriggerBase) obj;
base2.SourceNameResolver.Name = (string) args.NewValue;
}
private void OnSourceNameResolverElementChanged(object sender, NameResolvedEventArgs e)
{
if (SourceObject == null)
OnSourceChanged(e.OldObject, e.NewObject);
}
private static void OnSourceObjectChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var base2 = (EventTriggerBase) obj;
object newSource = base2.SourceNameResolver.Object;
if (args.NewValue == null)
{
base2.OnSourceChanged(args.OldValue, newSource);
}
else
{
if (args.OldValue == null && newSource != null)
base2.UnregisterEvent(newSource, base2.GetEventName());
base2.OnSourceChanged(args.OldValue, args.NewValue);
}
}
private void RegisterEvent(object obj, string eventName)
{
var eventInfo = obj.GetType().GetEvent(eventName);
if (eventInfo == null)
{
if (SourceObject != null)
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
ExceptionStringTable.EventTriggerCannotFindEventNameExceptionMessage,
new object[] { eventName, obj.GetType().Name }));
}
else if (!IsValidEvent(eventInfo))
{
if (SourceObject != null)
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
ExceptionStringTable.EventTriggerBaseInvalidEventExceptionMessage,
new object[] { eventName, obj.GetType().Name }));
}
else
{
_eventHandlerMethodInfo =
typeof(EventTriggerBase).GetMethod("OnEventImpl", BindingFlags.NonPublic | BindingFlags.Instance);
eventInfo.AddEventHandler(obj,
Delegate.CreateDelegate(eventInfo.EventHandlerType, this, _eventHandlerMethodInfo ?? throw new InvalidOperationException()));
}
}
private void RegisterLoaded(FrameworkElement associatedElement)
{
if (!IsLoadedRegistered && associatedElement != null)
{
associatedElement.Loaded += OnEventImpl;
IsLoadedRegistered = true;
}
}
private void RegisterSourceChanged()
{
if (!IsSourceChangedRegistered)
{
SourceNameResolver.ResolvedElementChanged += OnSourceNameResolverElementChanged;
IsSourceChangedRegistered = true;
}
}
private void UnregisterEvent(object obj, string eventName)
{
if (string.Compare(eventName, "Loaded", StringComparison.Ordinal) == 0)
{
if (obj is FrameworkElement associatedElement)
UnregisterLoaded(associatedElement);
}
else
{
UnregisterEventImpl(obj, eventName);
}
}
private void UnregisterEventImpl(object obj, string eventName)
{
var type = obj.GetType();
if (_eventHandlerMethodInfo != null)
{
var info = type.GetEvent(eventName);
info.RemoveEventHandler(obj,
Delegate.CreateDelegate(info.EventHandlerType, this, _eventHandlerMethodInfo));
_eventHandlerMethodInfo = null;
}
}
private void UnregisterLoaded(FrameworkElement associatedElement)
{
if (IsLoadedRegistered && associatedElement != null)
{
associatedElement.Loaded -= OnEventImpl;
IsLoadedRegistered = false;
}
}
private void UnregisterSourceChanged()
{
if (IsSourceChangedRegistered)
{
SourceNameResolver.ResolvedElementChanged -= OnSourceNameResolverElementChanged;
IsSourceChangedRegistered = false;
}
}
}