| File: StyleSheets\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 System.Xml; using Microsoft.Maui.Controls.Xaml; namespace Microsoft.Maui.Controls.StyleSheets { sealed class Style { Style() { } public IDictionary<string, string> Declarations { get; set; } = new Dictionary<string, string>(StringComparer.Ordinal); Dictionary<KeyValuePair<string, string>, object> convertedValues = new Dictionary<KeyValuePair<string, string>, object>(); public static Style Parse(CssReader reader, char stopChar = '\0') { Style style = new Style(); string propertyName = null, propertyValue = null; int p; reader.SkipWhiteSpaces(); bool readingName = true; while ((p = reader.Peek()) > 0) { switch (unchecked((char)p)) { case ':': reader.Read(); readingName = false; reader.SkipWhiteSpaces(); break; case ';': reader.Read(); if (!string.IsNullOrEmpty(propertyName) && !string.IsNullOrEmpty(propertyValue)) style.Declarations.Add(propertyName, propertyValue); propertyName = propertyValue = null; readingName = true; reader.SkipWhiteSpaces(); break; default: if ((char)p == stopChar) return style; if (readingName) { propertyName = reader.ReadIdent(); if (propertyName == null) throw new Exception(); } else propertyValue = reader.ReadUntil(stopChar, ';', ':'); break; } } return style; } public void Apply(VisualElement styleable, Selector.SelectorSpecificity selectorSpecificity = default, bool inheriting = false) { if (styleable == null) throw new ArgumentNullException(nameof(styleable)); foreach (var decl in Declarations) { var property = ((IStylable)styleable).GetProperty(decl.Key, inheriting); if (property == null) continue; if (string.Equals(decl.Value, "initial", StringComparison.OrdinalIgnoreCase)) styleable.SetValue(property, property.DefaultValue, new SetterSpecificity(SetterSpecificity.StyleImplicit, (byte)selectorSpecificity.Id, (byte)selectorSpecificity.Class, (byte)selectorSpecificity.Type)); else { object value; if (!convertedValues.TryGetValue(decl, out value)) convertedValues[decl] = (value = Convert(styleable, decl.Value, property)); //FIXME: compute distance styleable.SetValue(property, value, new SetterSpecificity(SetterSpecificity.StyleImplicit, (byte)selectorSpecificity.Id, (byte)selectorSpecificity.Class, (byte)selectorSpecificity.Type)); } } foreach (var child in ((IVisualTreeElement)styleable).GetVisualChildren()) { var ve = child as VisualElement; if (ve == null) continue; Apply(ve, inheriting: true); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] static object Convert(object target, object value, BindableProperty property) { var serviceProvider = new StyleSheetServiceProvider(target, property); Func<MemberInfo> minforetriever = () => { MemberInfo minfo = null; try { minfo = property.DeclaringType.GetRuntimeProperty(property.PropertyName); } catch (AmbiguousMatchException e) { throw new XamlParseException($"Multiple properties with name '{property.DeclaringType}.{property.PropertyName}' found.", serviceProvider, innerException: e); } if (minfo != null) return minfo; try { return property.DeclaringType.GetRuntimeMethod("Get" + property.PropertyName, new[] { typeof(BindableObject) }); } catch (AmbiguousMatchException e) { throw new XamlParseException($"Multiple methods with name '{property.DeclaringType}.Get{property.PropertyName}' found.", serviceProvider, innerException: e); } }; var ret = value.ConvertTo(property.ReturnType, minforetriever, serviceProvider, out Exception exception); if (exception != null) throw exception; return ret; } public void UnApply(IStylable styleable) { throw new NotImplementedException(); } } }