using System;
using System.ComponentModel;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.VisualTree;
#nullable enable
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.
///
public class Control : InputElement, IControl, INamed, 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));
///
/// Event raised when an element wishes to be scrolled into view.
///
public static readonly RoutedEvent RequestBringIntoViewEvent =
RoutedEvent.Register("RequestBringIntoView", RoutingStrategies.Bubble);
private DataTemplates? _dataTemplates;
private IControl? _focusAdorner;
///
/// 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 user-defined object attached to the control.
///
public object? Tag
{
get => GetValue(TagProperty);
set => SetValue(TagProperty, value);
}
public new IControl? Parent => (IControl?)base.Parent;
///
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
///
void ISetterValue.Initialize(ISetter setter)
{
throw new InvalidOperationException(
"Cannot use a control as a Setter value. Wrap the control in a .");
}
///
void IVisualBrushInitialize.EnsureInitialized()
{
if (VisualRoot == null)
{
if (!IsInitialized)
{
foreach (var i in this.GetSelfAndVisualDescendants())
{
var c = i as IControl;
if (c?.IsInitialized == false && c is ISupportInitialize init)
{
init.BeginInit();
init.EndInit();
}
}
}
if (!IsArrangeValid)
{
Measure(Size.Infinity);
Arrange(new Rect(DesiredSize));
}
}
}
///
/// Gets the element that receives the focus adorner.
///
/// The control that receives the focus adorner.
protected virtual IControl? GetTemplateFocusTarget() => this;
///
protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTreeCore(e);
InitializeIfNeeded();
}
///
protected sealed override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTreeCore(e);
}
///
protected override void OnGotFocus(GotFocusEventArgs e)
{
base.OnGotFocus(e);
if (IsFocused &&
(e.NavigationMethod == NavigationMethod.Tab ||
e.NavigationMethod == NavigationMethod.Directional))
{
var adornerLayer = AdornerLayer.GetAdornerLayer(this);
if (adornerLayer != null)
{
if (_focusAdorner == null)
{
var template = GetValue(FocusAdornerProperty);
if (template != null)
{
_focusAdorner = template.Build();
}
}
if (_focusAdorner != null && GetTemplateFocusTarget() is Visual target)
{
AdornerLayer.SetAdornedElement((Visual)_focusAdorner, target);
adornerLayer.Children.Add(_focusAdorner);
}
}
}
}
///
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
if (_focusAdorner != null)
{
var adornerLayer = (IPanel)_focusAdorner.Parent;
adornerLayer.Children.Remove(_focusAdorner);
_focusAdorner = null;
}
}
}
}