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