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;
}
}