using System; using System.Collections.Generic; using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Defines a style. /// public class Style : AvaloniaObject, IStyle, IResourceProvider { private IResourceHost? _owner; private StyleChildren? _children; private IResourceDictionary? _resources; private List? _setters; private List? _animations; /// /// Initializes a new instance of the class. /// public Style() { } /// /// Initializes a new instance of the class. /// /// The style selector. public Style(Func selector) { Selector = selector(null); } /// /// Gets the children of the style. /// public IList Children => _children ??= new(this); /// /// Gets the or Application that hosts the style. /// public IResourceHost? Owner { get => _owner; private set { if (_owner != value) { _owner = value; OwnerChanged?.Invoke(this, EventArgs.Empty); } } } /// /// Gets the parent style if this style is hosted in a collection. /// public Style? Parent { get; private set; } /// /// Gets or sets a dictionary of style resources. /// public IResourceDictionary Resources { get => _resources ?? (Resources = new ResourceDictionary()); set { value = value ?? throw new ArgumentNullException(nameof(value)); var hadResources = _resources?.HasResources ?? false; _resources = value; if (Owner is object) { _resources.AddOwner(Owner); if (hadResources || _resources.HasResources) { Owner.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty); } } } } /// /// Gets or sets the style's selector. /// public Selector? Selector { get; set; } /// /// Gets the style's setters. /// [Content] public IList Setters => _setters ??= new List(); /// /// Gets the style's animations. /// public IList Animations => _animations ??= new List(); bool IResourceNode.HasResources => _resources?.Count > 0; IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty(); public event EventHandler? OwnerChanged; public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) { target = target ?? throw new ArgumentNullException(nameof(target)); var match = Selector is object ? Selector.Match(target, Parent) : target == host ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; if (match.IsMatch && (_setters is object || _animations is object)) { var instance = new StyleInstance(this, target, _setters, _animations, match.Activator); target.StyleApplied(instance); instance.Start(); } return match.Result; } public bool TryGetResource(object key, out object? result) { result = null; return _resources?.TryGetResource(key, out result) ?? false; } /// /// Returns a string representation of the style. /// /// A string representation of the style. public override string ToString() { if (Selector != null) { return "Style: " + Selector.ToString(); } else { return "Style"; } } void IResourceProvider.AddOwner(IResourceHost owner) { owner = owner ?? throw new ArgumentNullException(nameof(owner)); if (Owner != null) { throw new InvalidOperationException("The Style already has a parent."); } Owner = owner; _resources?.AddOwner(owner); } void IResourceProvider.RemoveOwner(IResourceHost owner) { owner = owner ?? throw new ArgumentNullException(nameof(owner)); if (Owner == owner) { Owner = null; _resources?.RemoveOwner(owner); } } internal void SetParent(Style? parent) => Parent = parent; } }