diff --git a/src/Controls/src/SourceGen/IMethodSymbolExtensions.cs b/src/Controls/src/SourceGen/IMethodSymbolExtensions.cs index a63e15042151..e63a194f18f1 100644 --- a/src/Controls/src/SourceGen/IMethodSymbolExtensions.cs +++ b/src/Controls/src/SourceGen/IMethodSymbolExtensions.cs @@ -39,16 +39,23 @@ public static bool MatchXArguments(this IMethodSymbol method, ElementNode enode, // } var argType = getNodeValue?.Invoke(nodeparameters[i], paramType)?.Type ?? context.Variables[nodeparameters[i]].Type; - if (!argType.InheritsFrom(paramType, context)) + + // Check interface implementation first (interfaces don't use inheritance) + if (paramType.IsInterface()) { - parameters = null; - return false; + if (!argType.Implements(paramType)) + { + parameters = null; + return false; + } } - if (paramType.IsInterface() && !argType.Implements(paramType)) + // Then check class inheritance + else if (!argType.InheritsFrom(paramType, context)) { parameters = null; return false; } + parameters.Add((nodeparameters[i], paramType, null)); } return true; diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui32764.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui32764.xaml new file mode 100644 index 000000000000..cdff05a2484a --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui32764.xaml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui32764.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui32764.xaml.cs new file mode 100644 index 000000000000..a4ae3636c906 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui32764.xaml.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +// Simulate third-party control pattern (like CardView.MAUI) where constructors use interface parameters +public interface ITestProcessor +{ +} + +public class TestProcessor : ITestProcessor +{ + public double ScaleFactor { get; set; } +} + +public class TestCardsView : ContentView +{ + public TestCardsView(ITestProcessor processor) + { + // Constructor that takes interface parameter + } +} + +public class TestCarouselView : TestCardsView +{ + // Parameterless constructor chains to constructor with interface parameter + public TestCarouselView() : this(new TestProcessor()) + { + } + + // Constructor with interface parameter that chains to base + public TestCarouselView(ITestProcessor processor) : base(processor) + { + } +} + +public partial class Maui32764 : ContentPage +{ + public Maui32764() => InitializeComponent(); + + [TestFixture] + class Tests + { + [Test] + public void XArgumentsWithInterfaceParameterShouldWork([Values] XamlInflator inflator) + { + // This test reproduces issue #32764 + // The bug was that MatchXArguments incorrectly checked class inheritance + // before interface implementation, causing it to fail to match constructors + // with interface parameters when using x:Arguments in XAML + var page = new Maui32764(inflator); + + // Just verifying the page loads without throwing is sufficient + // The original bug would throw MAUIX2003: "No method found for 'TestCarouselView'" + // because the source generator couldn't match the constructor taking ITestProcessor + Assert.That(page, Is.Not.Null); + Assert.That(page.carouselView, Is.Not.Null); + } + } +}