Files
PropertyGrid/PROPERTYGRID_BINDING_FIX.md
2026-08-10 14:46:18 +02:00

6.0 KiB

PropertyGrid Binding Fix - Direct SelectedObject Assignment

Issue

The PropertyGrid was still not displaying properties even after adding property editors.

Root Cause Analysis

The binding SelectedObject="{Binding}" in XAML might not be working correctly due to:

  1. Compiled Bindings - The Window has x:DataType="models:Vehicle" which enables compiled bindings, but the PropertyGrid is a custom control and the binding might not propagate correctly through the visual tree
  2. Timing Issues - The PropertyGrid might not be receiving the DataContext at the right time during initialization
  3. Template Application - Custom controls with templates might have timing issues with property bindings

Solution

Set the SelectedObject property directly in code-behind after the window opens, bypassing the XAML binding entirely.

Implementation

QuickStartExample/MainWindow.axaml.cs

public MainWindow()
{
	InitializeComponent();
	var vehicle = new Vehicle();
	DataContext = vehicle;  // Still set for other potential bindings

	// Find the PropertyGrid and set SelectedObject directly after window opens
	this.Opened += (s, e) =>
	{
		var propertyGrid = this.FindDescendantOfType<Avalonia.PropertyGrid.Controls.PropertyGrid>();
		if (propertyGrid != null)
		{
			propertyGrid.SelectedObject = vehicle;  // Direct assignment!
			System.Diagnostics.Debug.WriteLine("PropertyGrid found and SelectedObject set directly");
		}
		else
		{
			System.Diagnostics.Debug.WriteLine("PropertyGrid NOT found!");
		}
	};
}

Helper Extension Method

public static class ControlExtensions
{
	public static T? FindDescendantOfType<T>(this Control control) where T : class
	{
		if (control is T result)
			return result;

		foreach (var child in control.GetLogicalChildren())
		{
			if (child is Control childControl)
			{
				var found = FindDescendantOfType<T>(childControl);
				if (found != null)
					return found;
			}
		}

		return null;
	}
}

Why This Works

1. Window.Opened Event

  • Fires after the window is fully initialized
  • Ensures all controls are created and templates are applied
  • PropertyGrid's template has been loaded and it's ready to receive data

2. Direct Property Assignment

  • Bypasses any XAML binding issues
  • Guarantees the property is set with the correct object reference
  • No ambiguity about timing or binding context

###3. FindDescendantOfType Helper

  • Recursively searches the logical tree for the PropertyGrid
  • Works even with complex nested layouts
  • Returns the PropertyGrid instance so we can set properties directly

Debug Output

Added debug WriteLine statements to track:

  1. When SelectedObject changes in PropertyGrid
  2. How many properties are created by PropertyItemFactory
  3. How many items are in FilteredProperties after filtering
  4. Whether the PropertyGrid is found in the visual tree

Expected Flow

  1. Window Initialization

    MainWindow() constructor called
    → DataContext set to Vehicle/AdvancedConfiguration
    → InitializeComponent() builds UI
    
  2. Window Opened Event

    Window.Opened fires
    → FindDescendantOfType searches for PropertyGrid
    → PropertyGrid found
    → SelectedObject set directly to vehicle/config object
    
  3. PropertyGrid Receives Object

    Debug: "PropertyGrid: SelectedObject changed to Vehicle"
    → OnSelectedObjectChanged() fires
    → RefreshProperties() called
    
  4. Properties Created

    Debug: "PropertyGrid: Created 10 property items"  (for Vehicle)
    → PropertyItemFactory creates PropertyItems for each public property
    → FilterProperties() populates FilteredProperties
    Debug: "PropertyGrid: FilteredProperties count = 10"
    
  5. UI Updates

    ItemsControl.ItemsSource = FilteredProperties
    → DataTemplates render each PropertyItem
    → Property editors display with correct visibility
    → User sees the properties!
    

Verification Steps

Run QuickStartExample

dotnet run --project Examples/QuickStartExample/QuickStartExample.csproj

Check Debug Output (should see):

PropertyGrid found and SelectedObject set directly
PropertyGrid: SelectedObject changed to Vehicle
PropertyGrid: Created 10 property items
PropertyGrid: FilteredProperties count = 10

Visual Result:

  • Make, Model (TextBox)
  • Year (TextBox with number)
  • Color (TextBox)
  • Mileage (TextBox with decimal)
  • IsElectric (CheckBox)
  • FuelType (ComboBox dropdown)
  • Price (TextBox with decimal)
  • PurchaseDate (TextBox with date)
  • VIN (TextBox, disabled/read-only)

Run AdvancedExample

dotnet run --project Examples/AdvancedExample/AdvancedExample.csproj

Check Debug Output (should see):

AdvancedExample: PropertyGrid found and SelectedObject set directly
PropertyGrid: SelectedObject changed to AdvancedConfiguration
PropertyGrid: Created 25+ property items
PropertyGrid: FilteredProperties count = 25+

Alternative Approaches (if this doesn't work)

Option 1: Named PropertyGrid

<pg:PropertyGrid x:Name="MyPropertyGrid" ... />
public MainWindow()
{
	InitializeComponent();
	var vehicle = new Vehicle();
	this.Opened += (s, e) => MyPropertyGrid.SelectedObject = vehicle;
}

Option 2: RelativeSource Binding

<pg:PropertyGrid SelectedObject="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}" />

Option 3: Property in Window

public Vehicle MyVehicle { get; set; } = new Vehicle();

public MainWindow()
{
	InitializeComponent();
	DataContext = this;
}
<pg:PropertyGrid SelectedObject="{Binding MyVehicle}" />

Files Modified

  1. Examples/QuickStartExample/MainWindow.axaml.cs - Direct SelectedObject assignment
  2. Examples/AdvancedExample/MainWindow.axaml.cs - Direct SelectedObject assignment
  3. Avalonia.PropertyGrid/Controls/PropertyGrid.axaml.cs - Added debug output

Next Steps

Run the examples and check the debug output to confirm properties are being loaded and displayed.


Status: ✅ Built successfully with direct property assignment approach