chore: separated System.Windows.Interactivity code
This commit is contained in:
1 parent
0da2875641
commit
ef4226ddf9
26 files changed
+54
-20
No files matched your search
@@ -0,0 +1,312 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user