Skip to content

Fix x:Bind interface property access on concrete types without public members#22325

Draft
Copilot wants to merge 4 commits intomasterfrom
copilot/fix-xbind-reflection-issue
Draft

Fix x:Bind interface property access on concrete types without public members#22325
Copilot wants to merge 4 commits intomasterfrom
copilot/fix-xbind-reflection-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 9, 2026

GitHub Issue: closes #

PR Type:

  • 🐞 Bugfix

What is the current behavior? 🤔

x:Bind fails when accessing interface properties on concrete types that don't expose those properties publicly. For example:

<!-- ViewModel has: IReadOnlyList<T> Items { get; } = new[] { ... } -->
<TextBlock Text="{x:Bind Model.Items.Count, Mode=OneWay}" />

Fails with:

The [Count] property getter does not exist on type [T[]]

Arrays implement IReadOnlyList<T>.Count but only expose Length directly. x:Bind uses reflection on the concrete type rather than checking interface implementations.

What is the new behavior? 🚀

BindingPropertyHelper now checks implemented interfaces when a property is not found in the type hierarchy:

Core changes:

  • GetPropertyInfo: After checking class hierarchy, iterates through GetInterfaces() to find matching properties
  • GetIndexerInfo: Same pattern for interface indexers (e.g., IReadOnlyList<T>[index])
  • Priority: Concrete type properties found before interface properties (preserves existing behavior)

Trimming support:

  • Added DynamicallyAccessedMemberTypes.Interfaces annotation
  • Added UnconditionalSuppressMessage for GetInterfaces() calls

Tests added:

  • Runtime test page validating array/List Count access via x:Bind
  • Unit tests for BindingPropertyHelper covering arrays, lists, indexers, and priority ordering

PR Checklist ✅

Please check if your PR fulfills the following requirements:

Other information ℹ️

Matches WinUI3/WinAppSDK behavior where x:Bind correctly resolves interface properties regardless of concrete type implementation.

Original prompt

This section details on the original issue you should resolve

<issue_title>x:Bind appears to be doing reflection on concrete type instead of a compiled binding to actual property type.</issue_title>
<issue_description>### Current behavior 🐛

View model:

[ObservableProperty]
public partial IReadOnlyList<RepositorySummary> Repositories { get; private set; } = [];

View:

<TextBlock Text="No repositories found." Visibility="{x:Bind suxc:Visible.IfZero(Model.Repositories.Count), Mode=OneWay}"  />

<TextBlock Text="Repositories found." Visibility="{x:Bind suxc:Visible.IfNotZero(Model.Repositories.Count), Mode=OneWay}"  />

If the concrete type returned by Model.Repositories does not have a public Count property (i.e. if it is an array [with a Length property instead] or a collection expression result [with only explicitly implemented interface members]), you get the following error and the bindings above fail:

fail: Uno.UI.DataBinding.BindingPropertyHelper[0]
      The [Count] property getter does not exist on type [DropDoc.DataContracts.Repositories.RepositorySummary[]]

Expected behavior 🎯

x:Bind is a compiled binding - it should be accessing IReadOnlyList<RepositorySummary>.Count directly to get the value, not attempting to get it via reflection from a Count property on the concrete type returned by the property. This works fine in WinAppSdk/WinUI3, just not on Uno targets.

How to reproduce it (as minimally and precisely as possible) 🔬

See above.

Workaround 🛠️

Need to return a concrete type with a public Count property.

Renderer 🎨

  • Skia
  • Native

Affected platforms 📱💻🖥️

All platforms 🌍 (except WinAppSdk)

Uno.Sdk version (and other relevant versions) 📦

6.5.0-dev.100

IDE version 🧑‍💻

No response

Anything else we need to know? 💬

No response</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Modified GetPropertyInfo and GetIndexerInfo to check interface properties when not found in type hierarchy. This fixes x:Bind bindings that access properties defined on interfaces (e.g., IReadOnlyList.Count) when the concrete type is an array or has explicit interface implementations.

Co-authored-by: MartinZikmund <1075116+MartinZikmund@users.noreply.github.com>
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI and others added 2 commits January 9, 2026 16:02
Co-authored-by: MartinZikmund <1075116+MartinZikmund@users.noreply.github.com>
- Add periods to end of comments for consistency
- Extract bindingFlagsForInterfaces variable to reduce duplication

Co-authored-by: MartinZikmund <1075116+MartinZikmund@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix x:Bind reflection behavior with concrete types Fix x:Bind interface property access on concrete types without public members Jan 9, 2026
Copilot AI requested a review from MartinZikmund January 9, 2026 16:06
@@ -0,0 +1,22 @@
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed

@unodevops
Copy link
Copy Markdown
Contributor

🤖 Your WebAssembly Skia Sample App stage site is ready! Visit it here: https://unowasmprstaging.z20.web.core.windows.net/pr-22325/wasm-skia-net9/index.html

@unodevops
Copy link
Copy Markdown
Contributor

🤖 Your Docs stage site is ready! Visit it here: https://unodocsprstaging.z13.web.core.windows.net/pr-22325/docs/index.html

Copy link
Copy Markdown
Contributor

@Xiaoy312 Xiaoy312 Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

originalType.GetInterfaces() also return Type[]s
we could wrap the whole thing in a single foreach loop instead of duplicating the code:

IEnumerable<Type> EnumerateAllTypesAndInterfaces(Type type)
{
	for (var t = type; t is { }; t = t.BaseType)
	{
		yield return t;
	}
	foreach (var t in type.GetInterfaces())
	{
		yield return t;
	}
}

XyzInfo? GetXyzInfo(Type type, string name, bool allowPrivateMembers)
{
	var flags = 
		BindingFlags.Public |
		(allowPrivateMembers ? BindingFlags.NonPublic : BindingFlags.Default) |
		BindingFlags.Instance | BindingFlags.Static;
	foreach (var t in EnumerateAllTypesAndInterfaces())
	{
		var info = t.GetXyz(...,
			flags | (!info.IsInterface ? BindingFlags.DeclaredOnly : BindingFlags.Default), ...
		)
		...
	}
}

@github-actions
Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or it will be closed in 10 days.

@github-actions github-actions Bot added the stale This item has been marked as stale and will be closed if there is no activity. label Mar 15, 2026
@Xiaoy312 Xiaoy312 removed the stale This item has been marked as stale and will be closed if there is no activity. label Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

x:Bind appears to be doing reflection on concrete type instead of a compiled binding to actual property type.

6 participants