diff --git a/src/Controls/src/SourceGen/KnownMarkups.cs b/src/Controls/src/SourceGen/KnownMarkups.cs
index 0c769f625592..c9ade31c9a2a 100644
--- a/src/Controls/src/SourceGen/KnownMarkups.cs
+++ b/src/Controls/src/SourceGen/KnownMarkups.cs
@@ -341,23 +341,25 @@ private static bool ProvideValueForBindingExtension(ElementNode markupNode, Inde
returnType = context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.BindingBase")!;
ITypeSymbol? dataTypeSymbol = null;
- // Check if the binding has a Source property with a RelativeSource.
- // In this case, we should NOT compile the binding using x:DataType because
- // the source type will be determined at runtime by the RelativeSource, not x:DataType.
- bool hasRelativeSource = HasRelativeSourceBinding(markupNode);
+ // When Source is explicitly set (RelativeSource or x:Reference), x:DataType does not describe
+ // the actual source — skip compilation and fall back to runtime binding.
+ bool hasExplicitSource = HasExplicitBindingSource(markupNode);
context.Variables.TryGetValue(markupNode, out ILocalValue? extVariable);
- if ( !hasRelativeSource
- && extVariable is not null
- && TryGetXDataType(markupNode, context, out dataTypeSymbol)
- && dataTypeSymbol is not null)
+ if ( !hasExplicitSource
+ && extVariable is not null)
{
- var compiledBindingMarkup = new CompiledBindingMarkup(markupNode, GetBindingPath(markupNode), extVariable, context);
- if (compiledBindingMarkup.TryCompileBinding(dataTypeSymbol, isTemplateBinding, out string? newBindingExpression) && newBindingExpression is not null)
+ TryGetXDataType(markupNode, context, out dataTypeSymbol);
+
+ if (dataTypeSymbol is not null)
{
- value = newBindingExpression;
- return true;
+ var compiledBindingMarkup = new CompiledBindingMarkup(markupNode, GetBindingPath(markupNode), extVariable, context);
+ if (compiledBindingMarkup.TryCompileBinding(dataTypeSymbol, isTemplateBinding, out string? newBindingExpression) && newBindingExpression is not null)
+ {
+ value = newBindingExpression;
+ return true;
+ }
}
}
@@ -628,10 +630,10 @@ static bool IsBindingContextBinding(ElementNode node)
&& propertyName.LocalName == "BindingContext";
}
- // Checks if the binding has a Source property that is a RelativeSource extension.
- // When a binding uses RelativeSource, the source type is determined at runtime,
+ // Checks if the binding has a Source property set to RelativeSource or x:Reference.
+ // When Source is explicitly set, x:DataType does not describe the actual binding source,
// so we should NOT compile the binding using x:DataType.
- static bool HasRelativeSourceBinding(ElementNode bindingNode)
+ static bool HasExplicitBindingSource(ElementNode bindingNode)
{
// Check if Source property exists
if (!bindingNode.Properties.TryGetValue(new XmlName("", "Source"), out INode? sourceNode)
@@ -640,12 +642,13 @@ static bool HasRelativeSourceBinding(ElementNode bindingNode)
return false;
}
- // Check if the Source is a RelativeSourceExtension
+ // Check if the Source is a RelativeSourceExtension or ReferenceExtension
if (sourceNode is ElementNode sourceElementNode)
{
- // Check if the element is a RelativeSourceExtension
- return sourceElementNode.XmlType.Name == "RelativeSourceExtension"
- || sourceElementNode.XmlType.Name == "RelativeSource";
+ return sourceElementNode.XmlType.Name is "RelativeSourceExtension"
+ or "RelativeSource"
+ or "ReferenceExtension"
+ or "Reference";
}
return false;
diff --git a/src/Controls/tests/SourceGen.UnitTests/BindingDiagnosticsTests.cs b/src/Controls/tests/SourceGen.UnitTests/BindingDiagnosticsTests.cs
index e106ffcddcb3..61471531093e 100644
--- a/src/Controls/tests/SourceGen.UnitTests/BindingDiagnosticsTests.cs
+++ b/src/Controls/tests/SourceGen.UnitTests/BindingDiagnosticsTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.Maui.Controls.SourceGen;
@@ -250,6 +251,58 @@ public class ViewModel
Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity);
}
+ [Fact]
+ public void BindingWithXReferenceSourceInDataTemplate_DoesNotReportFalsePositive()
+ {
+ var xaml =
+"""
+
+
+
+
+
+
+
+
+
+
+""";
+
+ var csharp =
+"""
+namespace Test;
+
+public partial class TestPage : Microsoft.Maui.Controls.ContentPage { }
+
+public class ViewModel
+{
+ public System.Collections.Generic.List Items { get; set; }
+ public Microsoft.Maui.Controls.Command SelectItemCommand { get; set; }
+}
+
+public class ItemModel
+{
+ public string Name { get; set; }
+}
+""";
+
+ var compilation = CreateMauiCompilation()
+ .AddSyntaxTrees(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(csharp));
+ var result = RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml), assertNoCompilationErrors: false);
+
+ // x:Reference bindings skip compilation entirely — no MAUIG2045 should be emitted
+ // for properties on the DataTemplate's x:DataType (ItemModel).
+ Assert.DoesNotContain(result.Diagnostics, d => d.Id == "MAUIG2045" && d.GetMessage().Contains("ItemModel", StringComparison.Ordinal));
+ }
+
[Fact]
public void BindingIndexerTypeUnsupported_ReportsCorrectDiagnostic()
{
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui34490.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui34490.xaml
new file mode 100644
index 000000000000..6edb2be3bdad
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui34490.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui34490.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui34490.xaml.cs
new file mode 100644
index 000000000000..9787cce17e40
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui34490.xaml.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Input;
+using Microsoft.Maui.ApplicationModel;
+using Microsoft.Maui.Controls.Core.UnitTests;
+using Xunit;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public class Maui34490ViewModel
+{
+ public List Items { get; set; }
+ public ICommand SelectItemCommand { get; set; }
+}
+
+public class Maui34490ItemModel
+{
+ public string Name { get; set; }
+}
+
+public partial class Maui34490 : ContentPage
+{
+ public Maui34490() => InitializeComponent();
+
+ [Collection("Issue")]
+ public class Tests : IDisposable
+ {
+ public Tests() => AppInfo.SetCurrent(new MockAppInfo());
+ public void Dispose() => AppInfo.SetCurrent(null);
+
+ [Theory]
+ [XamlInflatorData]
+ internal void XReferenceSourceInDataTemplateShouldNotWarn(XamlInflator inflator)
+ {
+ if (inflator == XamlInflator.SourceGen)
+ {
+ var result = MockSourceGenerator.CreateMauiCompilation()
+ .RunMauiSourceGenerator(typeof(Maui34490));
+ // The path is resolved against ContentPage (x:Reference target), NOT Maui34490ItemModel (x:DataType).
+ // BindingContext is 'object' on BindableObject, so SelectItemCommand warning on 'object' is expected,
+ // but a warning mentioning Maui34490ItemModel would mean it's still resolving against the wrong type.
+ Assert.DoesNotContain(result.Diagnostics, d => d.Id == "MAUIG2045" && d.GetMessage().Contains("Maui34490ItemModel", StringComparison.Ordinal));
+ }
+
+ var page = new Maui34490(inflator);
+ Assert.NotNull(page);
+ }
+ }
+}