using System;
using System.Diagnostics;
using System.Globalization;
using Avalonia.Controls;
using Avalonia.Reactive;
namespace Avalonia.Xaml.Interactivity;
///
/// A base class for behaviors, implementing the basic plumbing of .
///
public abstract class StyledElementBehavior : StyledElement, IBehavior, IBehaviorEventsHandler
{
private IDisposable? _dataContextDisposable;
///
/// Identifies the avalonia property.
///
public static readonly StyledProperty IsEnabledProperty =
AvaloniaProperty.Register(nameof(IsEnabled), defaultValue: true);
///
/// Gets the to which the behavior is attached.
///
public AvaloniaObject? AssociatedObject { get; private set; }
///
/// Gets the to which this behavior is attached.
///
public StyledElement? AssociatedStyledElement => AssociatedObject as StyledElement;
///
/// Gets or sets a value indicating whether this instance is enabled.
///
/// true if this instance is enabled; otherwise, false.
public bool IsEnabled
{
get => GetValue(IsEnabledProperty);
set => SetValue(IsEnabledProperty, value);
}
///
/// Attaches the behavior to the specified .
///
/// The to which to attach.
/// is null.
public void Attach(AvaloniaObject? associatedObject)
{
if (Equals(associatedObject, AssociatedObject))
{
return;
}
if (AssociatedObject is not null)
{
throw new InvalidOperationException(string.Format(
CultureInfo.CurrentCulture,
"An instance of a behavior cannot be attached to more than one object at a time."));
}
Debug.Assert(associatedObject is not null, "Cannot attach the behavior to a null object.");
AssociatedObject = associatedObject ?? throw new ArgumentNullException(nameof(associatedObject));
_dataContextDisposable = SynchronizeDataContext(associatedObject);
OnAttached();
}
///
/// Detaches the behaviors from the .
///
public void Detach()
{
OnDetaching();
_dataContextDisposable?.Dispose();
AssociatedObject = null;
}
///
/// Called after the behavior is attached to the .
///
///
/// Override this to hook up functionality to the
///
protected virtual void OnAttached()
{
}
///
/// Called when the behavior is being detached from its .
///
///
/// Override this to unhook functionality from the
///
protected virtual void OnDetaching()
{
}
void IBehaviorEventsHandler.AttachedToVisualTreeEventHandler()
{
AttachBehaviorToLogicalTree();
OnAttachedToVisualTree();
}
void IBehaviorEventsHandler.DetachedFromVisualTreeEventHandler()
{
DetachBehaviorFromLogicalTree();
OnDetachedFromVisualTree();
}
void IBehaviorEventsHandler.AttachedToLogicalTreeEventHandler()
{
AttachBehaviorToLogicalTree();
OnAttachedToLogicalTree();
}
void IBehaviorEventsHandler.DetachedFromLogicalTreeEventHandler()
{
DetachBehaviorFromLogicalTree();
OnDetachedFromLogicalTree();
}
void IBehaviorEventsHandler.LoadedEventHandler() => OnLoaded();
void IBehaviorEventsHandler.UnloadedEventHandler() => OnUnloaded();
///
/// Called after the is attached to the visual tree.
///
///
/// Invoked only when the is of type .
///
protected virtual void OnAttachedToVisualTree()
{
}
///
/// Called when the is being detached from the visual tree.
///
///
/// Invoked only when the is of type .
///
protected virtual void OnDetachedFromVisualTree()
{
}
///
/// Called after the is attached to the logical tree.
///
///
/// Invoked only when the is of type .
///
protected virtual void OnAttachedToLogicalTree()
{
}
///
/// Called when the is being detached from the logical tree.
///
///
/// Invoked only when the is of type .
///
protected virtual void OnDetachedFromLogicalTree()
{
}
///
/// Called after the is loaded.
///
///
/// Invoked only when the is of type .
///
protected virtual void OnLoaded()
{
}
///
/// Called when the is unloaded.
///
///
/// Invoked only when the is of type .
///
protected virtual void OnUnloaded()
{
}
private void AttachBehaviorToLogicalTree()
{
if (AssociatedObject is not StyledElement styledElement)
{
return;
}
if (styledElement.Parent is not null)
{
((ISetLogicalParent)this).SetParent(null);
((ISetLogicalParent)this).SetParent(styledElement);
}
}
private void DetachBehaviorFromLogicalTree()
{
((ISetLogicalParent)this).SetParent(null);
}
private IDisposable? SynchronizeDataContext(AvaloniaObject associatedObject)
{
if (associatedObject is StyledElement styledElement)
{
return styledElement
.GetObservable(DataContextProperty)
.Subscribe(new AnonymousObserver