using System; using Avalonia.Controls.Templates; using Avalonia.Interactivity; using Avalonia.Logging; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Styling; #nullable enable namespace Avalonia.Controls.Primitives { /// /// A lookless control whose visual appearance is defined by its . /// public class TemplatedControl : Control, ITemplatedControl { /// /// Defines the property. /// public static readonly StyledProperty BackgroundProperty = Border.BackgroundProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty BorderBrushProperty = Border.BorderBrushProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty BorderThicknessProperty = Border.BorderThicknessProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty CornerRadiusProperty = Border.CornerRadiusProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty FontFamilyProperty = TextBlock.FontFamilyProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty FontSizeProperty = TextBlock.FontSizeProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty FontStyleProperty = TextBlock.FontStyleProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty FontWeightProperty = TextBlock.FontWeightProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty ForegroundProperty = TextBlock.ForegroundProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty PaddingProperty = Decorator.PaddingProperty.AddOwner(); /// /// Defines the property. /// public static readonly StyledProperty TemplateProperty = AvaloniaProperty.Register(nameof(Template)); /// /// Defines the IsTemplateFocusTarget attached property. /// public static readonly AttachedProperty IsTemplateFocusTargetProperty = AvaloniaProperty.RegisterAttached("IsTemplateFocusTarget"); /// /// Defines the routed event. /// public static readonly RoutedEvent TemplateAppliedEvent = RoutedEvent.Register( "TemplateApplied", RoutingStrategies.Direct); private IControlTemplate? _appliedTemplate; /// /// Initializes static members of the class. /// static TemplatedControl() { ClipToBoundsProperty.OverrideDefaultValue(true); TemplateProperty.Changed.AddClassHandler((x, e) => x.OnTemplateChanged(e)); } /// /// Raised when the control's template is applied. /// public event EventHandler TemplateApplied { add { AddHandler(TemplateAppliedEvent, value); } remove { RemoveHandler(TemplateAppliedEvent, value); } } /// /// Gets or sets the brush used to draw the control's background. /// public IBrush? Background { get { return GetValue(BackgroundProperty); } set { SetValue(BackgroundProperty, value); } } /// /// Gets or sets the brush used to draw the control's border. /// public IBrush? BorderBrush { get { return GetValue(BorderBrushProperty); } set { SetValue(BorderBrushProperty, value); } } /// /// Gets or sets the thickness of the control's border. /// public Thickness BorderThickness { get { return GetValue(BorderThicknessProperty); } set { SetValue(BorderThicknessProperty, value); } } /// /// Gets or sets the radius of the border rounded corners. /// public CornerRadius CornerRadius { get { return GetValue(CornerRadiusProperty); } set { SetValue(CornerRadiusProperty, value); } } /// /// Gets or sets the font family used to draw the control's text. /// public FontFamily FontFamily { get { return GetValue(FontFamilyProperty); } set { SetValue(FontFamilyProperty, value); } } /// /// Gets or sets the size of the control's text in points. /// public double FontSize { get { return GetValue(FontSizeProperty); } set { SetValue(FontSizeProperty, value); } } /// /// Gets or sets the font style used to draw the control's text. /// public FontStyle FontStyle { get { return GetValue(FontStyleProperty); } set { SetValue(FontStyleProperty, value); } } /// /// Gets or sets the font weight used to draw the control's text. /// public FontWeight FontWeight { get { return GetValue(FontWeightProperty); } set { SetValue(FontWeightProperty, value); } } /// /// Gets or sets the brush used to draw the control's text and other foreground elements. /// public IBrush? Foreground { get { return GetValue(ForegroundProperty); } set { SetValue(ForegroundProperty, value); } } /// /// Gets or sets the padding placed between the border of the control and its content. /// public Thickness Padding { get { return GetValue(PaddingProperty); } set { SetValue(PaddingProperty, value); } } /// /// Gets or sets the template that defines the control's appearance. /// public IControlTemplate? Template { get { return GetValue(TemplateProperty); } set { SetValue(TemplateProperty, value); } } /// /// Gets the value of the IsTemplateFocusTargetProperty attached property on a control. /// /// The control. /// The property value. /// public static bool GetIsTemplateFocusTarget(Control control) { return control.GetValue(IsTemplateFocusTargetProperty); } /// /// Sets the value of the IsTemplateFocusTargetProperty attached property on a control. /// /// The control. /// The property value. /// /// When a control is navigated to using the keyboard, a focus adorner is shown - usually /// around the control itself. However if the TemplatedControl.IsTemplateFocusTarget /// attached property is set to true on an element in the control template, then the focus /// adorner will be shown around that control instead. /// public static void SetIsTemplateFocusTarget(Control control, bool value) { control.SetValue(IsTemplateFocusTargetProperty, value); } /// public sealed override void ApplyTemplate() { var template = Template; var logical = (ILogical)this; // Apply the template if it is not the same as the template already applied - except // for in the case that the template is null and we're not attached to the logical // tree. In that case, the template has probably been cleared because the style setting // the template has been detached, so we want to wait until it's re-attached to the // logical tree as if it's re-attached to the same tree the template will be the same // and we don't need to do anything. if (_appliedTemplate != template && (template != null || logical.IsAttachedToLogicalTree)) { if (VisualChildren.Count > 0) { foreach (var child in this.GetTemplateChildren()) { child.SetValue(TemplatedParentProperty, null); ((ISetLogicalParent)child).SetParent(null); } VisualChildren.Clear(); } if (template != null) { Logger.TryGet(LogEventLevel.Verbose, LogArea.Control)?.Log(this, "Creating control template"); var (child, nameScope) = template.Build(this); ApplyTemplatedParent(child); ((ISetLogicalParent)child).SetParent(this); VisualChildren.Add(child); // Existing code kinda expect to see a NameScope even if it's empty if (nameScope == null) nameScope = new NameScope(); var e = new TemplateAppliedEventArgs(nameScope); OnApplyTemplate(e); #pragma warning disable CS0618 // Type or member is obsolete OnTemplateApplied(e); #pragma warning restore CS0618 // Type or member is obsolete RaiseEvent(e); } _appliedTemplate = template; } } /// protected override IControl GetTemplateFocusTarget() { foreach (Control child in this.GetTemplateChildren()) { if (GetIsTemplateFocusTarget(child)) { return child; } } return this; } protected sealed override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) { var count = VisualChildren.Count; for (var i = 0; i < count; ++i) { if (VisualChildren[i] is ILogical logical) { logical.NotifyResourcesChanged(e); } } base.NotifyChildResourcesChanged(e); } /// protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { if (VisualChildren.Count > 0) { ((ILogical)VisualChildren[0]).NotifyAttachedToLogicalTree(e); } base.OnAttachedToLogicalTree(e); } /// protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { if (VisualChildren.Count > 0) { ((ILogical)VisualChildren[0]).NotifyDetachedFromLogicalTree(e); } base.OnDetachedFromLogicalTree(e); } protected virtual void OnApplyTemplate(TemplateAppliedEventArgs e) { } /// /// Called when the control's template is applied. /// /// The event args. [Obsolete("Use OnApplyTemplate")] protected virtual void OnTemplateApplied(TemplateAppliedEventArgs e) { } /// /// Called when the property changes. /// /// The event args. protected virtual void OnTemplateChanged(AvaloniaPropertyChangedEventArgs e) { InvalidateMeasure(); } /// /// Sets the TemplatedParent property for the created template children. /// /// The control. private void ApplyTemplatedParent(IControl control) { control.SetValue(TemplatedParentProperty, this); var children = control.LogicalChildren; var count = children.Count; for (var i = 0; i < count; i++) { if (children[i] is IControl child) { ApplyTemplatedParent(child); } } } } }