using System; using System.Collections.Generic; using System.ComponentModel; using Avalonia.Automation.Peers; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; using Avalonia.LogicalTree; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Threading; using Avalonia.VisualTree; namespace Avalonia.Controls { /// /// Base class for Avalonia controls. /// /// /// The control class extends and adds the following features: /// /// - A property to allow user-defined data to be attached to the control. /// - and other context menu related members. /// public class Control : InputElement, IDataTemplateHost, IVisualBrushInitialize, ISetterValue { /// /// Defines the property. /// public static readonly StyledProperty?> FocusAdornerProperty = AvaloniaProperty.Register?>(nameof(FocusAdorner)); /// /// Defines the property. /// public static readonly StyledProperty TagProperty = AvaloniaProperty.Register(nameof(Tag)); /// /// Defines the property. /// public static readonly StyledProperty ContextMenuProperty = AvaloniaProperty.Register(nameof(ContextMenu)); /// /// Defines the property /// public static readonly StyledProperty ContextFlyoutProperty = AvaloniaProperty.Register(nameof(ContextFlyout)); /// /// Event raised when an element wishes to be scrolled into view. /// public static readonly RoutedEvent RequestBringIntoViewEvent = RoutedEvent.Register( "RequestBringIntoView", RoutingStrategies.Bubble); /// /// Provides event data for the event. /// public static readonly RoutedEvent ContextRequestedEvent = RoutedEvent.Register( nameof(ContextRequested), RoutingStrategies.Tunnel | RoutingStrategies.Bubble); /// /// Defines the event. /// public static readonly RoutedEvent LoadedEvent = RoutedEvent.Register( nameof(Loaded), RoutingStrategies.Direct); /// /// Defines the event. /// public static readonly RoutedEvent UnloadedEvent = RoutedEvent.Register( nameof(Unloaded), RoutingStrategies.Direct); /// /// Defines the event. /// public static readonly RoutedEvent SizeChangedEvent = RoutedEvent.Register( nameof(SizeChanged), RoutingStrategies.Direct); // Note the following: // _loadedQueue : // Is the queue where any control will be added to indicate that its loaded // event should be scheduled and called later. // _loadedProcessingQueue : // Contains a copied snapshot of the _loadedQueue at the time when processing // starts and individual events are being fired. This was needed to avoid // exceptions if new controls were added in the Loaded event itself. private static bool _isLoadedProcessing = false; private static readonly HashSet _loadedQueue = new HashSet(); private static readonly HashSet _loadedProcessingQueue = new HashSet(); private LoadState _loadState = LoadState.Unloaded; private DataTemplates? _dataTemplates; private Control? _focusAdorner; private AutomationPeer? _automationPeer; /// /// Gets or sets the control's focus adorner. /// public ITemplate? FocusAdorner { get => GetValue(FocusAdornerProperty); set => SetValue(FocusAdornerProperty, value); } /// /// Gets or sets the data templates for the control. /// /// /// Each control may define data templates which are applied to the control itself and its /// children. /// public DataTemplates DataTemplates => _dataTemplates ??= new DataTemplates(); /// /// Gets or sets a context menu to the control. /// public ContextMenu? ContextMenu { get => GetValue(ContextMenuProperty); set => SetValue(ContextMenuProperty, value); } /// /// Gets or sets a context flyout to the control /// public FlyoutBase? ContextFlyout { get => GetValue(ContextFlyoutProperty); set => SetValue(ContextFlyoutProperty, value); } /// /// Gets a value indicating whether the control is fully constructed in the visual tree /// and both layout and render are complete. /// /// /// This is set to true while raising the event. /// public bool IsLoaded => _loadState == LoadState.Loaded; /// /// Gets or sets a user-defined object attached to the control. /// public object? Tag { get => GetValue(TagProperty); set => SetValue(TagProperty, value); } /// /// Occurs when the user has completed a context input gesture, such as a right-click. /// public event EventHandler? ContextRequested { add => AddHandler(ContextRequestedEvent, value); remove => RemoveHandler(ContextRequestedEvent, value); } /// /// Occurs when the control has been fully constructed in the visual tree and both /// layout and render are complete. /// /// /// This event is guaranteed to occur after the control template is applied and references /// to objects created after the template is applied are available. This makes it different /// from OnAttachedToVisualTree which doesn't have these references. This event occurs at the /// latest possible time in the control creation life-cycle. /// public event EventHandler? Loaded { add => AddHandler(LoadedEvent, value); remove => RemoveHandler(LoadedEvent, value); } /// /// Occurs when the control is removed from the visual tree. /// /// /// This is API symmetrical with and exists for compatibility with other /// XAML frameworks; however, it behaves the same as OnDetachedFromVisualTree. /// public event EventHandler? Unloaded { add => AddHandler(UnloadedEvent, value); remove => RemoveHandler(UnloadedEvent, value); } /// /// Occurs when the bounds (actual size) of the control have changed. /// public event EventHandler? SizeChanged { add => AddHandler(SizeChangedEvent, value); remove => RemoveHandler(SizeChangedEvent, value); } /// bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; /// void ISetterValue.Initialize(SetterBase setter) { if (setter is Setter s && s.Property == ContextFlyoutProperty) { return; // Allow ContextFlyout to not need wrapping in