Skip to content

[Proposal] [XSG] Reduce compiled binding friction #34696

@simonrozsival

Description

@simonrozsival

Problem

Compiled bindings require too much manual ceremony and produce confusing diagnostics. As AOT/trimming become the default, these friction points block adoption:

  1. Manual type annotations everywhere. x:DataType must be set on every DataTemplate and overridden on every cross-scope binding — even when the compiler has enough information to infer the type.
  2. False warnings on working code. MAUIG2045 fires for x:Reference and Behavior bindings that work at runtime. With TreatWarningsAsErrors, this breaks builds.
  3. Cross-scope bindings aren't compiled. Binding to the page ViewModel from inside a DataTemplate requires verbose RelativeSource or x:Reference markup — and the result still uses reflection, which gets trimmed under AOT.

Before / After

Binding to the page ViewModel from inside a CollectionView.ItemTemplate:

Today: verbose, not compiled, false warnings

<ContentPage x:Name="PageRoot"
             x:Class="local:ProductsPage"
             x:DataType="vm:ProductsViewModel"> <!-- tells the compiler the BindingContext type -->
  <CollectionView ItemsSource="{Binding Products}">
    <CollectionView.ItemTemplate>
      <!-- must manually match ObservableCollection<Product> -->
      <DataTemplate x:DataType="models:Product">
        <Grid>
          <Label Text="{Binding Name}" />
          <!-- ⚠ x:DataType override needed to suppress false MAUIG2045 -->
          <!-- ❌ not compiled — falls back to reflection, trimmed under AOT -->
          <Button Command="{Binding BindingContext.SelectCommand, Source={x:Reference PageRoot}, x:DataType=local:ContentPage}"
                  CommandParameter="{Binding .}" />
        </Grid>
      </DataTemplate>
    </CollectionView.ItemTemplate>
  </CollectionView>
</ContentPage>

Goal: same intent, compiled, no warnings

<ContentPage x:Name="PageRoot"
             x:Class="local:ProductsPage"
             x:DataType="vm:ProductsViewModel"> <!-- tells the compiler the BindingContext type -->
  <CollectionView ItemsSource="{Binding Products}">
    <CollectionView.ItemTemplate>
      <!-- ✅ x:DataType inferred from ProductsViewModel.Products : ObservableCollection<Product> -->
      <DataTemplate>
        <Grid>
          <Label Text="{Binding Name}" />
          <!-- ✅ compiled — PageRoot resolves to ProductsPage, x:DataType gives the BindingContext cast -->
          <Button Command="{Binding BindingContext.SelectCommand, Source={x:Reference PageRoot}}"
                  CommandParameter="{Binding .}" />
        </Grid>
      </DataTemplate>
    </CollectionView.ItemTemplate>
  </CollectionView>
</ContentPage>

No new syntax. The compiler uses type information already present in the XAML.

Proposed improvements

New sub-issues

  1. Compile x:Reference bindings — two inference steps:

    • Element type: {x:Reference PageRoot} → resolve the XAML element and its type: ContentPage with x:Classlocal:ProductsPage. Properties like Title compile directly.
    • BindingContext cast: when the path starts with BindingContext.Y, use the referenced element's x:DataType to infer the cast → ((ProductsViewModel)source.BindingContext).Y.

    Related: [net11.0] Compile x:Reference bindings against resolved element type #34513.

  2. Support named element access in C# expressions{PageRoot.BindingContext.Cmd} resolves named elements as page fields. Opt-in, experimental.

  3. Improve warning quality — suppress false MAUIG2045 for non-context sources, include types in diagnostics, suppress XC0022 for Source={x:Null}/{x:Static}.

Tracked in existing issues

  1. Infer DataTemplate x:DataType from collection generic type[Feature] Auto compiled binding on CollectionView's ItemTemplate, GroupHeaderTemplate and GroupFooterTemplate #8161
  2. Compile RelativeSource AncestorType[XSG] Optimize compiled bindings for RelativeSource with explicit Source #33249 / PR Fix for RelativeSource AncestorType bindings not generating compiled bindings under AOT #34408
  3. Compile RelativeSource Self — element type is always known at compile time; trivial case.
  4. Fix Behavior binding storyEnable the inheritance of the parent's BindingContext for Behaviors, based on a Flag #26705, RelativeSource Binding Does Not Work For PlatformBehaviors (MCT Touch Behavior) #24313
  5. Update documentation and examples — reflect new inference capabilities in dotnet/docs-maui.

Risks

These improvements share a common pattern: bindings that were previously reflection-based become compiled. This is generally positive (better perf, AOT-safe), but has consequences:

Risk Impact Mitigation
New warnings on existing code. Reflection silently resolves bad paths; compiled bindings report MAUIG2045. Builds with TreatWarningsAsErrors could break. These are pre-existing bugs being surfaced. Warnings are suppressible.
Inferred vs explicit x:DataType conflict. E.g., DataTemplate x:DataType="Animal" but collection is ObservableCollection<Dog>. Could confuse developers or cause silent misresolution. Explicit x:DataType always wins. Emit info diagnostic on mismatch.
Runtime type differs from compile-time type. Shadowed (new) members or subclass overrides may resolve differently when compiled. Rare, but behavior could change. Compiled bindings already have this characteristic; inference doesn't make it worse.
Source generator ordering. Inferred types may depend on other generators (e.g., CommunityToolkit.Mvvm [RelayCommand]). Path resolution could fail if generated members aren't visible yet. Fall back to runtime binding when type can't be resolved.

Related PRs

PR Status Description
#34513 Open (.NET 11) Compile x:Reference bindings
#34408 Open (.NET 10 SR6) Compile RelativeSource AncestorType
#34447 Merged (.NET 10 SR6) Fix explicit-source bindings in DataTemplates
#34501 Merged (.NET 10 SR5.1) Fix false MAUIG2045 for x:Reference in DataTemplate
#33693 Merged (.NET 10 SR4) XAML C# Expressions (experimental)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions