Skip to content

Indexer binding path with a '.' in the key ([a.b]) is split into two items and binds nothing #24937

Description

@SolidRockProgrammer

Current behavior

An indexer binding path whose key contains a . binds to nothing. Path="[a.b]" is split into two path items, [a and b]. The indexer is then called with the key "a", which returns null, and .b] is looked up on that null, so the bound TextBlock shows an empty string.

The cause is in Uno.UI.DataBinding.BindingPath.Parse (read from Uno.UI 6.7.135, the same code in lib/net10.0 (Skia desktop) and lib/net10.0-android36.0). It walks the path backwards and tracks two flags: one for (...) and one for [...]. The case '.' branch checks only the parenthesis flag:

case '.':
    if (!flag)            // flag = "inside ( )"; the "inside [ ]" flag (flag2) is not consulted
    {
        TryPrependItem(path, num2 + 1, num, ...);
        num = 0;
        continue;
    }
    break;

So a . inside brackets ends a path item. Read through BindingPath.GetPathItems():

Path Items
[PROJECT.Project_ID] [PROJECT, Project_ID]
[hub.current] [hub, current]
[PROJECT_ID] [PROJECT_ID] (one item, works)

Expected behavior

The text between [ and ] is the indexer argument, dots included, so [a.b] is one indexer item that calls this["a.b"].

That is the documented property-path syntax ("the value inside the brackets is the indexer argument"). I have not run the same binding on WinUI / Windows App SDK to confirm what it does there.

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

public sealed class Row
{
    private readonly Dictionary<string, string> _cells = new() { ["a.b"] = "dotted", ["ab"] = "plain" };
    public string? this[string key] => _cells.TryGetValue(key, out var v) ? v : null;
}

// Page code-behind
var row = new Row();
var dotted = new TextBlock { DataContext = row };
dotted.SetBinding(TextBlock.TextProperty, new Binding { Path = new PropertyPath("[a.b]") });
var plain = new TextBlock { DataContext = row };
plain.SetBinding(TextBlock.TextProperty, new Binding { Path = new PropertyPath("[ab]") });
// plain shows "plain"; dotted shows "" (expected "dotted")

The parse can also be shown without rendering anything. BindingPath is internal, so this uses reflection:

var type = typeof(Microsoft.UI.Xaml.Data.Binding).Assembly.GetType("Uno.UI.DataBinding.BindingPath")!;
var path = (IDisposable)Activator.CreateInstance(type, "[a.b]", null)!;
var items = ((IEnumerable)type.GetMethod("GetPathItems")!.Invoke(path, null)!).Cast<object>()
    .Select(i => (string)i.GetType().GetProperty("PropertyName")!.GetValue(i)!);
// items == ["[a", "b]"]; expected ["[a.b]"]

Workaround

Keep . out of indexer keys in binding paths. We substitute another character in the key and map it back inside the indexer.

Works on UWP/WinUI

I don't know (not tested here).

Environment

Nuget Package: Uno.WinUI (Uno.Sdk 6.7.x)

Package Version(s): 6.7.135

Affected platform(s):

  • Android (Skia renderer, measured on an API 35 emulator)
  • Skia desktop (Windows; same lib/net10.0 code)
  • other platforms not checked

IDE: Visual Studio 2026

Anything else we need to know?

In our app this blanked every cell of a WinUI.TableView grid whose TableViewTextColumn.Binding paths are [TABLE.Field]. The rows, row selection and accessible row names all worked, so the grid looked populated but empty.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions