| File: CompiledConverters\BindablePropertyConverter.cs | Web Access |
| Project: src\src\Controls\src\Build.Tasks\Controls.Build.Tasks.csproj (Microsoft.Maui.Controls.Build.Tasks) |
using System.Collections.Generic; using System.Linq; using System.Xml; using Microsoft.Maui.Controls.Build.Tasks; using Microsoft.Maui.Controls.Xaml; using Mono.Cecil; using Mono.Cecil.Cil; using static System.String; using static Microsoft.Maui.Controls.Build.Tasks.BuildExceptionCode; namespace Microsoft.Maui.Controls.XamlC { class BindablePropertyConverter : ICompiledTypeConverter { public IEnumerable<Instruction> ConvertFromString(string value, ILContext context, BaseNode node) { var module = context.Body.Method.Module; if (IsNullOrEmpty(value)) { yield return Instruction.Create(OpCodes.Ldnull); yield break; } var bpRef = GetBindablePropertyFieldReference(value, context, module, node); yield return Instruction.Create(OpCodes.Ldsfld, bpRef); } public FieldReference GetBindablePropertyFieldReference(string value, ILContext context, ModuleDefinition module, BaseNode node) { FieldReference bpRef = null; string typeName = null, propertyName = null; var parts = value.Split('.'); if (parts.Length == 1) { var parent = node.Parent?.Parent as IElementNode ?? (node.Parent?.Parent as IListNode)?.Parent as IElementNode; if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == XamlParser.MauiUri && ((node.Parent as ElementNode)?.XmlType.Name == nameof(Setter) || (node.Parent as ElementNode)?.XmlType.Name == nameof(PropertyCondition))) { if (parent.XmlType.NamespaceUri == XamlParser.MauiUri && (parent.XmlType.Name == nameof(Trigger) || parent.XmlType.Name == nameof(DataTrigger) || parent.XmlType.Name == nameof(MultiTrigger) || parent.XmlType.Name == nameof(Style))) { typeName = GetTargetTypeName(parent); } else if (parent.XmlType.NamespaceUri == XamlParser.MauiUri && parent.XmlType.Name == nameof(VisualState)) { typeName = FindTypeNameForVisualState(parent, node); } } else if ((node.Parent as ElementNode)?.XmlType.NamespaceUri == XamlParser.MauiUri && (node.Parent as ElementNode)?.XmlType.Name == nameof(Trigger)) { typeName = GetTargetTypeName(node.Parent); } propertyName = parts[0]; } else if (parts.Length == 2) { typeName = parts[0]; propertyName = parts[1]; } else throw new BuildException(Conversion, node, null, value, typeof(BindableProperty)); if (typeName == null || propertyName == null) throw new BuildException(Conversion, node, null, value, typeof(BindableProperty)); var typeRef = XmlTypeExtensions.GetTypeReference(context.Cache, typeName, module, node); if (typeRef == null) throw new BuildException(TypeResolution, node, null, typeName); bpRef = GetBindablePropertyFieldReference(context.Cache, typeRef, propertyName, module); if (bpRef == null) throw new BuildException(PropertyResolution, node, null, propertyName, typeRef.Name); return bpRef; static string GetTargetTypeName(INode node) => ((node as ElementNode).Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string; } static string FindTypeNameForVisualState(IElementNode parent, IXmlLineInfo lineInfo) { //1. parent is VisualState, don't check that //2. check that the VS is in a VSG if (!(parent.Parent is IElementNode target) || target.XmlType.NamespaceUri != XamlParser.MauiUri || target.XmlType.Name != nameof(VisualStateGroup)) throw new XamlParseException($"Expected {nameof(VisualStateGroup)} but found {parent.Parent}", lineInfo); //3. if the VSG is in a VSGL, skip that as it could be implicit if (target.Parent is ListNode || ((target.Parent as IElementNode)?.XmlType.NamespaceUri == XamlParser.MauiUri && (target.Parent as IElementNode)?.XmlType.Name == nameof(VisualStateGroupList))) target = target.Parent.Parent as IElementNode; else target = target.Parent as IElementNode; //4. target is now a Setter in a Style, or a VE if (target.XmlType.NamespaceUri == XamlParser.MauiUri && target.XmlType.Name == nameof(Setter)) return ((target?.Parent as IElementNode)?.Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string; else return target.XmlType.Name; } public static FieldReference GetBindablePropertyFieldReference(XamlCache cache, TypeReference typeRef, string propertyName, ModuleDefinition module) { TypeReference declaringTypeReference; FieldReference bpRef = typeRef.GetField(cache, fd => fd.Name == $"{propertyName}Property" && fd.IsStatic && fd.IsPublic, out declaringTypeReference); if (bpRef != null) { bpRef = module.ImportReference(bpRef.ResolveGenericParameters(declaringTypeReference)); bpRef.FieldType = module.ImportReference(bpRef.FieldType); } return bpRef; } } }