initial commit

This commit is contained in:
Matthias Heil
2026-08-10 14:46:18 +02:00
commit c946aa63e6
69 changed files with 8865 additions and 0 deletions
+213
View File
@@ -0,0 +1,213 @@
# 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
```csharp
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
```csharp
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
```sh
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
```sh
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
```xml
<pg:PropertyGrid x:Name="MyPropertyGrid" ... />
```
```csharp
public MainWindow()
{
InitializeComponent();
var vehicle = new Vehicle();
this.Opened += (s, e) => MyPropertyGrid.SelectedObject = vehicle;
}
```
### Option 2: RelativeSource Binding
```xml
<pg:PropertyGrid SelectedObject="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}" />
```
### Option 3: Property in Window
```csharp
public Vehicle MyVehicle { get; set; } = new Vehicle();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
```
```xml
<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