| File: Style.cs | Web Access |
| Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls) |
#nullable disable using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using Microsoft.Maui.Controls.Internals; namespace Microsoft.Maui.Controls { /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="Type[@FullName='Microsoft.Maui.Controls.Style']/Docs/*" /> [ContentProperty(nameof(Setters))] public sealed class Style : IStyle { internal const string StyleClassPrefix = "Microsoft.Maui.Controls.StyleClass."; readonly BindableProperty _basedOnResourceProperty = BindableProperty.CreateAttached("BasedOnResource", typeof(Style), typeof(Style), default(Style), propertyChanged: OnBasedOnResourceChanged); readonly ConditionalWeakTable<BindableObject, object> _targets = new(); Style _basedOnStyle; string _baseResourceKey; IList<Behavior> _behaviors; IList<TriggerBase> _triggers; /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='.ctor']/Docs/*" /> public Style([System.ComponentModel.TypeConverter(typeof(TypeTypeConverter))][Parameter("TargetType")] Type targetType) { TargetType = targetType ?? throw new ArgumentNullException(nameof(targetType)); Setters = new List<Setter>(); } /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='ApplyToDerivedTypes']/Docs/*" /> public bool ApplyToDerivedTypes { get; set; } /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='BasedOn']/Docs/*" /> public Style BasedOn { get { return _basedOnStyle; } set { if (_basedOnStyle == value) return; if (!ValidateBasedOn(value)) throw new ArgumentException("BasedOn.TargetType is not compatible with TargetType"); Style oldValue = _basedOnStyle; _basedOnStyle = value; BasedOnChanged(oldValue, value); if (value != null) BaseResourceKey = null; } } /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='BaseResourceKey']/Docs/*" /> public string BaseResourceKey { get { return _baseResourceKey; } set { if (_baseResourceKey == value) return; _baseResourceKey = value; //update all DynamicResources foreach (var target in (IEnumerable<KeyValuePair<BindableObject, object>>)(object)_targets) { target.Key.RemoveDynamicResource(_basedOnResourceProperty); if (value != null) target.Key.SetDynamicResource(_basedOnResourceProperty, value); } if (value != null) BasedOn = null; } } /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='Behaviors']/Docs/*" /> public IList<Behavior> Behaviors => _behaviors ??= new AttachedCollection<Behavior>(); /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='CanCascade']/Docs/*" /> public bool CanCascade { get; set; } /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='Class']/Docs/*" /> public string Class { get; set; } /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='Setters']/Docs/*" /> public IList<Setter> Setters { get; } /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='Triggers']/Docs/*" /> public IList<TriggerBase> Triggers => _triggers ??= new AttachedCollection<TriggerBase>(); void IStyle.Apply(BindableObject bindable, SetterSpecificity specificity) { lock (_targets) { #if NETSTANDARD2_0 _targets.Remove(bindable); _targets.Add(bindable, specificity); #else _targets.AddOrUpdate(bindable, specificity); #endif } if (BaseResourceKey != null) bindable.SetDynamicResource(_basedOnResourceProperty, BaseResourceKey); ApplyCore(bindable, BasedOn ?? GetBasedOnResource(bindable), specificity); } /// <include file="../../docs/Microsoft.Maui.Controls/Style.xml" path="//Member[@MemberName='TargetType']/Docs/*" /> public Type TargetType { get; } void IStyle.UnApply(BindableObject bindable) { UnApplyCore(bindable, BasedOn ?? GetBasedOnResource(bindable)); bindable.RemoveDynamicResource(_basedOnResourceProperty); lock (_targets) { _targets.Remove(bindable); } } internal bool CanBeAppliedTo(Type targetType) { if (TargetType == targetType) return true; if (!ApplyToDerivedTypes) return false; do { targetType = targetType.BaseType; if (TargetType == targetType) return true; } while (targetType != typeof(Element)); return false; } void BasedOnChanged(Style oldValue, Style newValue) { foreach (var target in (IEnumerable<KeyValuePair<BindableObject, object>>)(object)_targets) { UnApplyCore(target.Key, oldValue); ApplyCore(target.Key, newValue, (SetterSpecificity)target.Value); } } Style GetBasedOnResource(BindableObject bindable) => (Style)bindable.GetValue(_basedOnResourceProperty); static void OnBasedOnResourceChanged(BindableObject bindable, object oldValue, object newValue) { Style style = (bindable as IStyleElement).Style; if (style == null) return; if (!style._targets.TryGetValue(bindable, out var objectspecificity)) return; style.UnApplyCore(bindable, (Style)oldValue); style.ApplyCore(bindable, (Style)newValue, (SetterSpecificity)objectspecificity); } ConditionalWeakTable<BindableObject, object> specificities = new(); void ApplyCore(BindableObject bindable, Style basedOn, SetterSpecificity specificity) { if (basedOn != null) ((IStyle)basedOn).Apply(bindable, specificity.AsBaseStyle()); #if NETSTANDARD2_0 specificities.Remove(bindable); specificities.Add(bindable, specificity); #else specificities.AddOrUpdate(bindable, specificity); #endif foreach (Setter setter in Setters) setter.Apply(bindable, specificity); ((AttachedCollection<Behavior>)Behaviors).AttachTo(bindable); ((AttachedCollection<TriggerBase>)Triggers).AttachTo(bindable); } void UnApplyCore(BindableObject bindable, Style basedOn) { ((AttachedCollection<TriggerBase>)Triggers).DetachFrom(bindable); ((AttachedCollection<Behavior>)Behaviors).DetachFrom(bindable); if (!specificities.TryGetValue(bindable, out var specificity)) return; foreach (Setter setter in Setters) setter.UnApply(bindable, (SetterSpecificity)specificity); if (basedOn != null) ((IStyle)basedOn).UnApply(bindable); } bool ValidateBasedOn(Style value) => value is null || value.TargetType.IsAssignableFrom(TargetType); } }