diff --git a/src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs b/src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs index d7488a10fea9..9a1055ae3e8b 100644 --- a/src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs +++ b/src/Controls/src/Build.Tasks/CompiledConverters/BindablePropertyConverter.cs @@ -37,10 +37,12 @@ static bool IsOfAnyType(XmlType xmlType, params string[] types) return true; return false; } + public FieldReference GetBindablePropertyFieldReference(string value, ILContext context, ModuleDefinition module, BaseNode node) { FieldReference bpRef = null; - string typeName = null, propertyName = null; + XmlType typeName = null; + string propertyName = null; var parts = value.Split('.'); if (parts.Length == 1) @@ -54,7 +56,7 @@ public FieldReference GetBindablePropertyFieldReference(string value, ILContext } else if (IsOfAnyType(parent.XmlType, nameof(VisualState))) { - typeName = FindTypeNameForVisualState(parent, node); + typeName = FindTypeNameForVisualState(parent, node, context); } } else if (IsOfAnyType((node.Parent as ElementNode)?.XmlType, nameof(Trigger))) @@ -65,7 +67,8 @@ public FieldReference GetBindablePropertyFieldReference(string value, ILContext } else if (parts.Length == 2) { - typeName = parts[0]; + var targetType = parts[0]; + typeName = TypeArgumentsParser.ParseSingle(targetType, node.NamespaceResolver, (IXmlLineInfo)node); propertyName = parts[1]; } else @@ -74,7 +77,7 @@ public FieldReference GetBindablePropertyFieldReference(string value, ILContext if (typeName == null || propertyName == null) throw new BuildException(Conversion, node, null, value, typeof(BindableProperty)); - var typeRef = XmlTypeExtensions.GetTypeReference(context.Cache, typeName, module, node); + var typeRef = typeName.GetTypeReference(context.Cache, module, node); if (typeRef == null) throw new BuildException(TypeResolution, node, null, typeName); @@ -83,11 +86,14 @@ public FieldReference GetBindablePropertyFieldReference(string value, ILContext 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 XmlType GetTargetTypeName(INode node) + { + var targetType = ((node as ElementNode).Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string; + return TypeArgumentsParser.ParseSingle(targetType, node.NamespaceResolver, (IXmlLineInfo)node); + } } - static string FindTypeNameForVisualState(IElementNode parent, IXmlLineInfo lineInfo) + static XmlType FindTypeNameForVisualState(IElementNode parent, IXmlLineInfo lineInfo, ILContext context) { //1. parent is VisualState, don't check that @@ -105,9 +111,12 @@ static string FindTypeNameForVisualState(IElementNode parent, IXmlLineInfo lineI //4. target is now a Setter in a Style, or a VE if (IsOfAnyType(target.XmlType, nameof(Setter))) - return ((target?.Parent as IElementNode)?.Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string; + { + var targetType = ((target?.Parent as IElementNode)?.Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string; + return TypeArgumentsParser.ParseSingle(targetType, parent.NamespaceResolver, lineInfo); + } else - return target.XmlType.Name; + return target.XmlType; } public static FieldReference GetBindablePropertyFieldReference(XamlCache cache, TypeReference typeRef, string propertyName, ModuleDefinition module) diff --git a/src/Controls/src/Build.Tasks/XamlCache.cs b/src/Controls/src/Build.Tasks/XamlCache.cs index 8287c85c034e..a57a7ee12af6 100644 --- a/src/Controls/src/Build.Tasks/XamlCache.cs +++ b/src/Controls/src/Build.Tasks/XamlCache.cs @@ -30,7 +30,7 @@ static TValue GetOrAdd(Dictionary dictionary, TKey k return value; } - public IList GetXmlsDefinitions(ModuleDefinition module, Func> valueFactory) => + public IList GetXmlnsDefinitions(ModuleDefinition module, Func> valueFactory) => GetOrAdd(_xmlnsDefinitions, module, valueFactory); public TypeDefinition Resolve(TypeReference typeReference) => diff --git a/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs b/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs index 1884ec126b54..3f4c5ba6dd6e 100644 --- a/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs +++ b/src/Controls/src/Build.Tasks/XmlTypeExtensions.cs @@ -101,7 +101,7 @@ public static TypeReference GetTypeReference(XamlCache cache, string namespaceUR public static bool TryGetTypeReference(this XmlType xmlType, XamlCache cache, ModuleDefinition module, IXmlLineInfo xmlInfo, bool expandToExtension, out TypeReference typeReference) { - IList xmlnsDefinitions = cache.GetXmlsDefinitions(module, GatherXmlnsDefinitionAttributes); + IList xmlnsDefinitions = cache.GetXmlnsDefinitions(module, GatherXmlnsDefinitionAttributes); var typeArguments = xmlType.TypeArguments; diff --git a/src/Controls/src/SourceGen/NodeSGExtensions.cs b/src/Controls/src/SourceGen/NodeSGExtensions.cs index 498601dece51..5b281962f660 100644 --- a/src/Controls/src/SourceGen/NodeSGExtensions.cs +++ b/src/Controls/src/SourceGen/NodeSGExtensions.cs @@ -625,14 +625,17 @@ public static IFieldSymbol GetBindableProperty(this ValueNode node, SourceGenCon else target = (IElementNode)target.Parent; - string? typeName = null; + XmlType? typeName = null; //4. target is now a Setter in a Style, or a VE if (IsOfAnyType(target.XmlType, "Setter")) - typeName = ((target?.Parent as IElementNode)?.Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string; + { + var targetType = ((target?.Parent as IElementNode)?.Properties[new XmlName("", "TargetType")] as ValueNode)?.Value as string; + typeName = TypeArgumentsParser.ParseSingle(targetType, parent.NamespaceResolver, lineInfo); + } else - typeName = target.XmlType.Name; + typeName = target.XmlType; - return XmlTypeExtensions.GetTypeSymbol(typeName!, context.ReportDiagnostic, context.Compilation, context.XmlnsCache, parent); + return typeName!.GetTypeSymbol(context.ReportDiagnostic, context.Compilation, context.XmlnsCache); } public static bool RepresentsType(this INode node, string namespaceUri, string name) diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui31186.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui31186.xaml new file mode 100644 index 000000000000..1694f7235573 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui31186.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui31186.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui31186.xaml.cs new file mode 100644 index 000000000000..c00be486341a --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui31186.xaml.cs @@ -0,0 +1,86 @@ +using System; +using Microsoft.Maui.Graphics; +using NUnit.Framework; + +using static Microsoft.Maui.Controls.Xaml.UnitTests.MockSourceGenerator; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public class MyButton : Button +{ + +} + +[XamlProcessing(XamlInflator.Default, true)] +public partial class Maui31186 : ContentPage +{ + public Maui31186() + { + InitializeComponent(); + } + int count = 0; + + void OnCounterClicked(object sender, EventArgs e) + { + count++; + + if (count == 1) + CounterBtn.Text = $"Clicked {count} time"; + else + CounterBtn.Text = $"Clicked {count} times"; + } + + [TestFixture] + class Tests + { + [Test] + public void XmlnsResolutionForVisualState([Values] XamlInflator inflator) + { + if (inflator == XamlInflator.SourceGen) + { + var result = CreateMauiCompilation() + .WithAdditionalSource( + """ +using System; +using Microsoft.Maui.Graphics; +using NUnit.Framework; + +using static Microsoft.Maui.Controls.Xaml.UnitTests.MockSourceGenerator; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public class MyButton : Button +{ + +} + +[XamlProcessing(XamlInflator.Runtime, true)] +public partial class Maui31186 : ContentPage +{ + public Maui31186() + { + InitializeComponent(); + } + int count = 0; + + void OnCounterClicked(object sender, EventArgs e) + { + count++; + + if (count == 1) + CounterBtn.Text = $"Clicked {count} time"; + else + CounterBtn.Text = $"Clicked {count} times"; + } +} +""") + .RunMauiSourceGenerator(typeof(Maui31186)); + Assert.That(result.Diagnostics, Is.Empty); + } + var page = new Maui31186(inflator); + Assert.That(page, Is.Not.Null); + VisualStateManager.GoToState(page.CounterBtn, "Disabled"); + Assert.That(page.CounterBtn.BackgroundColor, Is.EqualTo(Colors.LightBlue)); + } + } +} \ No newline at end of file