Skip to content

Commit 4310846

Browse files
StephaneDelcroixPureWeen
authored andcommitted
[C] Fix binding to interface-inherited properties like IReadOnlyList<T>.Count (#32912)
* Fix binding to interface-inherited properties like IReadOnlyList<T>.Count Fixes #13872 The runtime binding expression was not resolving properties inherited from parent interfaces. For example, IReadOnlyList<T>.Count would return null because Count is defined on IReadOnlyCollection<T>, not directly on IReadOnlyList<T>. Added GetProperty() method that searches both base classes and implemented interfaces recursively, similar to how GetIndexer() already handles this case. * Fix Maui32879Tests expected output after pragma warning merge
1 parent 9cb447e commit 4310846

File tree

3 files changed

+73
-5
lines changed

3 files changed

+73
-5
lines changed

src/Controls/src/Core/BindingExpression.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,28 @@ PropertyInfo GetIndexer(TypeInfo sourceType, string indexerName, string content)
318318
return null;
319319
}
320320

321+
PropertyInfo GetProperty(TypeInfo sourceType, string propertyName)
322+
{
323+
// First, check the type and its base classes
324+
TypeInfo type = sourceType;
325+
do
326+
{
327+
var property = type.GetDeclaredProperty(propertyName);
328+
if (property != null)
329+
return property;
330+
} while ((type = type.BaseType?.GetTypeInfo()) != null);
331+
332+
// If not found, check implemented interfaces (for interface-inherited properties like IReadOnlyList<T>.Count)
333+
foreach (var iface in sourceType.ImplementedInterfaces)
334+
{
335+
var property = GetProperty(iface.GetTypeInfo(), propertyName);
336+
if (property != null)
337+
return property;
338+
}
339+
340+
return null;
341+
}
342+
321343

322344
void SetupPart(TypeInfo sourceType, BindingExpressionPart part)
323345
{
@@ -382,11 +404,7 @@ void SetupPart(TypeInfo sourceType, BindingExpressionPart part)
382404
}
383405
else
384406
{
385-
TypeInfo type = sourceType;
386-
do
387-
{
388-
property = type.GetDeclaredProperty(part.Content);
389-
} while (property == null && (type = type.BaseType?.GetTypeInfo()) != null);
407+
property = GetProperty(sourceType, part.Content);
390408
}
391409
if (property != null)
392410
{
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
5+
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui13872">
6+
<VerticalStackLayout>
7+
<Label x:Name="label0" Text="{Binding List.Count}"/>
8+
<Label x:Name="label1" Text="{Binding ListCount}"/>
9+
<Label x:Name="label2" Text="{Binding List.Count}" x:DataType="local:Maui13872ViewModel"/>
10+
<Label x:Name="label3" Text="{Binding ListCount}" x:DataType="local:Maui13872ViewModel"/>
11+
</VerticalStackLayout>
12+
</ContentPage>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Collections.Generic;
2+
using NUnit.Framework;
3+
4+
namespace Microsoft.Maui.Controls.Xaml.UnitTests;
5+
6+
public partial class Maui13872 : ContentPage
7+
{
8+
public Maui13872() => InitializeComponent();
9+
10+
[TestFixture]
11+
class Tests
12+
{
13+
[Test]
14+
public void CompiledBindingToIReadOnlyListCount([Values] XamlInflator inflator)
15+
{
16+
var page = new Maui13872(inflator);
17+
page.BindingContext = new Maui13872ViewModel();
18+
19+
// Uncompiled bindings (no x:DataType) - should work with all inflators
20+
Assert.That(page.label0.Text, Is.EqualTo("3"), "Uncompiled binding to List.Count");
21+
Assert.That(page.label1.Text, Is.EqualTo("3"), "Uncompiled binding to ListCount");
22+
23+
// Compiled bindings (with x:DataType) - IReadOnlyList<T>.Count should resolve correctly.
24+
// Count is defined on IReadOnlyCollection<T> which IReadOnlyList<T> inherits.
25+
Assert.That(page.label2.Text, Is.EqualTo("3"), "Compiled binding to List.Count");
26+
Assert.That(page.label3.Text, Is.EqualTo("3"), "Compiled binding to ListCount");
27+
}
28+
}
29+
}
30+
31+
public class Maui13872ViewModel
32+
{
33+
private readonly string[] _list = ["Bill", "Steve", "John"];
34+
35+
public IReadOnlyList<string> List => _list;
36+
37+
public int ListCount => _list.Length;
38+
}

0 commit comments

Comments
 (0)