Refactor PropertyGrid UI, converters, and solution setup

- Improved PropertyGrid.axaml formatting and control styles; ensured spinner buttons are always visible and ColorPicker layouts are flexible; disabled horizontal scrolling in property list.
- Reformatted property editors and group/category display for clarity.
- Made all value converters sealed, added XML docs, and updated ConvertBack to throw NotImplementedException where unsupported.
- Enhanced search logic to use StringComparison.OrdinalIgnoreCase.
- Minor code style fixes (e.g., char literals for string checks).
- Removed default value assignment for _useProxy in AdvancedConfiguration.cs.
- Updated Program.cs in examples to use sealed Program classes.
- Added PropertyGrid.slnx to organize solution and projects.
This commit is contained in:
Matthias Heil
2026-08-13 09:49:27 +02:00
parent 750721b549
commit 37720f103b
13 changed files with 551 additions and 378 deletions
+368 -354
View File
@@ -4,361 +4,375 @@
xmlns:models="using:Avalonia.PropertyGrid.Models" xmlns:models="using:Avalonia.PropertyGrid.Models"
xmlns:sys="clr-namespace:System;assembly=System.Runtime"> xmlns:sys="clr-namespace:System;assembly=System.Runtime">
<Design.PreviewWith> <Design.PreviewWith>
<Border Padding="20" Width="400" Height="600"> <Border Padding="20" Width="400" Height="600">
<controls:PropertyGrid /> <controls:PropertyGrid />
</Border>
</Design.PreviewWith>
<Style Selector="controls|PropertyGrid">
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAltHighBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<Grid RowDefinitions="Auto,*">
<!-- Search/Filter Box -->
<Border Grid.Row="0"
Padding="8"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,0,1">
<StackPanel>
<TextBox Name="PART_SearchBox"
PlaceholderText="Search properties..."
Text="{Binding $parent[controls:PropertyGrid].SearchText, Mode=TwoWay}" />
<!-- Debug: Show count -->
<TextBlock Text="{Binding $parent[controls:PropertyGrid].FilteredProperties.Count, StringFormat='Properties: {0}'}"
FontSize="10"
Foreground="Gray"
Margin="0,4,0,0" />
</StackPanel>
</Border>
<!-- Properties List -->
<ScrollViewer Grid.Row="1"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Panel>
<!-- Category View -->
<ItemsControl Name="PART_CategoryItemsControl"
ItemsSource="{TemplateBinding CategoryGroups}"
IsVisible="{TemplateBinding ShowCategories}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="models:CategoryGroup">
<StackPanel>
<!-- Category Header -->
<Border Background="{DynamicResource SystemControlHighlightAccentBrush}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,0,1"
Padding="8,4"
HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal" Spacing="4">
<!-- Expand/Collapse Toggle -->
<ToggleButton IsChecked="{Binding IsExpanded, Mode=TwoWay}"
Width="16" Height="16"
Padding="0"
VerticalAlignment="Center">
<Panel>
<!-- Right Arrow (collapsed/closed state) -->
<Path IsVisible="{Binding !IsExpanded}"
Data="M 4,2 L 10,8 L 4,14 Z"
Fill="White"
Width="12" Height="12"
Stretch="Uniform" />
<!-- Down Arrow (expanded/open state) -->
<Path IsVisible="{Binding IsExpanded}"
Data="M 2,4 L 8,10 L 14,4 Z"
Fill="White"
Width="12" Height="12"
Stretch="Uniform" />
</Panel>
</ToggleButton>
<!-- Category Name -->
<TextBlock Text="{Binding CategoryName}"
FontWeight="Bold"
Foreground="White"
VerticalAlignment="Center" />
<!-- Property Count -->
<TextBlock Text="{Binding PropertyCount, StringFormat='({0})'}"
FontSize="11"
Foreground="White"
Opacity="0.8"
VerticalAlignment="Center"
Margin="4,0,0,0" />
</StackPanel>
</Border>
<!-- Properties in Category -->
<ItemsControl ItemsSource="{Binding Properties}"
IsVisible="{Binding IsExpanded}"
Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="models:PropertyItem">
<Border Margin="16,0,0,0" BorderThickness="0">
<Grid MinHeight="32"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="150" MaxWidth="400" SharedSizeGroup="PropertyName" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<!-- Property Name -->
<Border Grid.Column="0"
Padding="8,4"
Margin="{Binding IndentLevel, Converter={StaticResource IndentConverter}}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,1,1">
<StackPanel Orientation="Horizontal" Spacing="4">
<!-- Expander for complex types with arrow icons -->
<ToggleButton IsVisible="{Binding IsExpandable}"
IsChecked="{Binding IsExpanded}"
Width="16" Height="16"
Padding="0"
VerticalAlignment="Center">
<Panel>
<!-- Right Arrow (collapsed/closed state) -->
<Path IsVisible="{Binding !IsExpanded}"
Data="M 4,2 L 10,8 L 4,14 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
<!-- Down Arrow (expanded/open state) -->
<Path IsVisible="{Binding IsExpanded}"
Data="M 2,4 L 8,10 L 14,4 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
</Panel>
</ToggleButton>
<TextBlock Text="{Binding DisplayName}"
VerticalAlignment="Center"
ToolTip.Tip="{Binding Description}" />
</StackPanel>
</Border>
<!-- Splitter -->
<GridSplitter Grid.Column="1"
Width="4"
Background="Transparent"
ResizeDirection="Columns" />
<!-- Property Value Editor -->
<Border Grid.Column="2"
Padding="4"
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,0,1">
<Grid>
<!-- Color Picker for Color properties with colored background -->
<Border Background="{Binding ColorValue, Converter={StaticResource ColorToBrushConverter}}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsColorTypeConverter}}"
CornerRadius="4"
Padding="4">
<ColorPicker Color="{Binding ColorValue, Mode=TwoWay}"
IsEnabled="{Binding !IsReadOnly}"
IsAlphaEnabled="True"
HorizontalAlignment="Stretch">
<ColorPicker.Styles>
<Style Selector="ColorPicker">
<Setter Property="MaxWidth" Value="{x:Static sys:Double.PositiveInfinity}" />
</Style>
</ColorPicker.Styles>
</ColorPicker>
</Border>
<!-- NumericUpDown for numeric properties -->
<NumericUpDown Value="{Binding NumericValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsNumericTypeConverter}}"
IsReadOnly="{Binding IsReadOnly}"
FormatString="{Binding PropertyType, Converter={StaticResource NumericFormatConverter}}"
AllowSpin="True"
ShowButtonSpinner="True"
HorizontalAlignment="Stretch" />
<!-- CheckBox for boolean properties -->
<CheckBox IsChecked="{Binding BooleanValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsBooleanTypeConverter}}"
IsEnabled="{Binding !IsReadOnly}"
Content="{Binding DisplayName}"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<!-- ComboBox for enum properties -->
<ComboBox SelectedItem="{Binding Value, Mode=TwoWay}"
ItemsSource="{Binding PropertyType, Converter={StaticResource EnumValuesConverter}}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsEnumConverter}}"
IsEnabled="{Binding !IsReadOnly}"
HorizontalAlignment="Stretch" />
<!-- TextBox for other properties -->
<TextBox Text="{Binding ValueString, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsOtherTypeConverter}}"
IsReadOnly="{Binding IsReadOnly}"
HorizontalAlignment="Stretch" />
</Grid>
</Border>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Flat List View -->
<ItemsControl Name="PART_ItemsControl"
ItemsSource="{TemplateBinding FilteredProperties}"
IsVisible="{TemplateBinding ShowCategories, Converter={StaticResource NegateBoolConverter}}"
Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="models:PropertyItem">
<Border Margin="0,0,0,0" BorderThickness="0">
<Grid MinHeight="32"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="150" MaxWidth="400" SharedSizeGroup="PropertyName" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<!-- Property Name -->
<Border Grid.Column="0"
Padding="8,4"
Margin="{Binding IndentLevel, Converter={StaticResource IndentConverter}}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,1,1">
<StackPanel Orientation="Horizontal" Spacing="4">
<!-- Expander for complex types with arrow icons -->
<ToggleButton IsVisible="{Binding IsExpandable}"
IsChecked="{Binding IsExpanded}"
Width="16" Height="16"
Padding="0"
VerticalAlignment="Center">
<Panel>
<!-- Right Arrow (collapsed/closed state) -->
<Path IsVisible="{Binding !IsExpanded}"
Data="M 4,2 L 10,8 L 4,14 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
<!-- Down Arrow (expanded/open state) -->
<Path IsVisible="{Binding IsExpanded}"
Data="M 2,4 L 8,10 L 14,4 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
</Panel>
</ToggleButton>
<TextBlock Text="{Binding DisplayName}"
VerticalAlignment="Center"/>
</StackPanel>
</Border>
<!-- Splitter -->
<GridSplitter Grid.Column="1"
Width="4"
Background="Transparent"
ResizeDirection="Columns" />
<!-- Property Value -->
<Border Grid.Column="2"
Padding="8,4"
Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,0,1">
<Grid>
<!-- ColorPicker for color properties with colored background -->
<Border Background="{Binding ColorValue, Converter={StaticResource ColorToBrushConverter}}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsColorTypeConverter}}"
CornerRadius="4"
Padding="4">
<ColorPicker Color="{Binding ColorValue, Mode=TwoWay}"
IsEnabled="{Binding !IsReadOnly}"
HorizontalAlignment="Stretch">
<ColorPicker.Styles>
<Style Selector="ColorPicker">
<Setter Property="Width" Value="{x:Static sys:Double.PositiveInfinity}" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</ColorPicker.Styles>
</ColorPicker>
</Border>
<!-- NumericUpDown for numeric properties -->
<NumericUpDown Value="{Binding NumericValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsNumericTypeConverter}}"
IsReadOnly="{Binding IsReadOnly}"
FormatString="{Binding PropertyType, Converter={StaticResource NumericFormatConverter}}"
AllowSpin="True"
ShowButtonSpinner="True"
HorizontalAlignment="Stretch" />
<!-- CheckBox for boolean properties -->
<CheckBox IsChecked="{Binding BooleanValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsBooleanTypeConverter}}"
IsEnabled="{Binding !IsReadOnly}"
Content="{Binding DisplayName}"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<!-- ComboBox for enum properties -->
<ComboBox SelectedItem="{Binding Value, Mode=TwoWay}"
ItemsSource="{Binding PropertyType, Converter={StaticResource EnumValuesConverter}}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsEnumConverter}}"
IsEnabled="{Binding !IsReadOnly}"
HorizontalAlignment="Stretch" />
<!-- TextBox for other properties -->
<TextBox Text="{Binding ValueString, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsOtherTypeConverter}}"
IsReadOnly="{Binding IsReadOnly}"
HorizontalAlignment="Stretch" />
</Grid>
</Border>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Panel>
</ScrollViewer>
</Grid>
</Border> </Border>
</ControlTemplate> </Design.PreviewWith>
</Setter>
</Style>
<!-- Converters and Resources --> <!-- Force NumericUpDown spinner buttons to always be visible -->
<Style Selector="controls|PropertyGrid"> <Style Selector="controls|PropertyGrid NumericUpDown">
<Style.Resources> <Setter Property="AllowSpin" Value="True" />
<controls:IndentToMarginConverter x:Key="IndentConverter" /> <Setter Property="ShowButtonSpinner" Value="True" />
<controls:EnumValuesConverter x:Key="EnumValuesConverter" /> <Setter Property="ButtonSpinnerLocation" Value="Right" />
<controls:IsEnumConverter x:Key="IsEnumConverter" /> </Style>
<controls:IsSimpleTypeConverter x:Key="IsSimpleTypeConverter" />
<controls:TypeToBoolConverter x:Key="TypeToBoolConverter" /> <Style Selector="controls|PropertyGrid NumericUpDown /template/ TextBox#PART_TextBox">
<controls:ColorToStringConverter x:Key="ColorToStringConverter" /> <Setter Property="MinWidth" Value="0" />
<controls:ColorToBrushConverter x:Key="ColorToBrushConverter" /> </Style>
<controls:IsColorTypeConverter x:Key="IsColorTypeConverter" /> <Style Selector="controls|PropertyGrid ColorPicker /template/ DropDownButton /template/ PathIcon#DropDownGlyph">
<controls:IsNumericTypeConverter x:Key="IsNumericTypeConverter" /> <Setter Property="IsVisible" Value="False" />
<controls:IsBooleanTypeConverter x:Key="IsBooleanTypeConverter" /> </Style>
<controls:IsOtherTypeConverter x:Key="IsOtherTypeConverter" />
<controls:NumericFormatConverter x:Key="NumericFormatConverter" /> <Style Selector="controls|PropertyGrid">
<controls:NegateBoolConverter x:Key="NegateBoolConverter" /> <Setter Property="Background" Value="{DynamicResource SystemControlBackgroundAltHighBrush}" />
</Style.Resources> <Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
</Style> <Setter Property="BorderThickness" Value="1" />
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<Grid RowDefinitions="Auto,*">
<!-- Search/Filter Box -->
<Border Grid.Row="0"
Padding="8"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,0,1">
<StackPanel>
<TextBox Name="PART_SearchBox"
PlaceholderText="Search properties..."
Text="{Binding $parent[controls:PropertyGrid].SearchText, Mode=TwoWay}" />
<!-- Debug: Show count -->
<TextBlock Text="{Binding $parent[controls:PropertyGrid].FilteredProperties.Count, StringFormat='Properties: {0}'}"
FontSize="10"
Foreground="Gray"
Margin="0,4,0,0" />
</StackPanel>
</Border>
<!-- Properties List -->
<ScrollViewer Grid.Row="1"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Panel>
<!-- Category View -->
<ItemsControl Name="PART_CategoryItemsControl"
ItemsSource="{TemplateBinding CategoryGroups}"
IsVisible="{TemplateBinding ShowCategories}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="models:CategoryGroup">
<StackPanel>
<!-- Category Header -->
<Border Background="{DynamicResource SystemControlHighlightAccentBrush}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,0,1"
Padding="8,4"
HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal" Spacing="4">
<!-- Expand/Collapse Toggle -->
<ToggleButton IsChecked="{Binding IsExpanded, Mode=TwoWay}"
Width="16" Height="16"
Padding="0"
VerticalAlignment="Center">
<Panel>
<!-- Right Arrow (collapsed/closed state) -->
<Path IsVisible="{Binding !IsExpanded}"
Data="M 4,2 L 10,8 L 4,14 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
<!-- Down Arrow (expanded/open state) -->
<Path IsVisible="{Binding IsExpanded}"
Data="M 2,4 L 8,10 L 14,4 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
</Panel>
</ToggleButton>
<!-- Category Name -->
<TextBlock Text="{Binding CategoryName}"
FontWeight="Bold"
Foreground="White"
VerticalAlignment="Center" />
<!-- Property Count -->
<TextBlock Text="{Binding PropertyCount, StringFormat='({0})'}"
FontSize="11"
Foreground="White"
Opacity="0.8"
VerticalAlignment="Center"
Margin="4,0,0,0" />
</StackPanel>
</Border>
<!-- Properties in Category -->
<ItemsControl ItemsSource="{Binding Properties}"
IsVisible="{Binding IsExpanded}"
Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="models:PropertyItem">
<Border Margin="16,0,0,0" BorderThickness="0">
<Grid MinHeight="32"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="150" MaxWidth="400" SharedSizeGroup="PropertyName" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<!-- Property Name -->
<Border Grid.Column="0"
Padding="8,4"
Margin="{Binding IndentLevel, Converter={StaticResource IndentConverter}}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,1,1">
<StackPanel Orientation="Horizontal" Spacing="4">
<!-- Expander for complex types with arrow icons -->
<ToggleButton IsVisible="{Binding IsExpandable}"
IsChecked="{Binding IsExpanded}"
Width="16" Height="16"
Padding="0"
VerticalAlignment="Center">
<Panel >
<!-- Right Arrow (collapsed/closed state) -->
<Path IsVisible="{Binding !IsExpanded}"
Data="M 4,2 L 10,8 L 4,14 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
<!-- Down Arrow (expanded/open state) -->
<Path IsVisible="{Binding IsExpanded}"
Data="M 2,4 L 8,10 L 14,4 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
</Panel>
</ToggleButton>
<TextBlock Text="{Binding DisplayName}"
VerticalAlignment="Center"
ToolTip.Tip="{Binding Description}" />
</StackPanel>
</Border>
<!-- Splitter -->
<GridSplitter Grid.Column="1"
Width="4"
Background="Transparent"
ResizeDirection="Columns" />
<!-- Property Value Editor -->
<Border Grid.Column="2"
Padding="4"
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,0,1">
<Grid>
<!-- Color Picker for Color properties -->
<ColorPicker Color="{Binding ColorValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsColorTypeConverter}}"
IsEnabled="{Binding !IsReadOnly}"
IsAlphaEnabled="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Width="{Binding $parent[Grid].Bounds.Width}">
<ColorPicker.Styles>
<Style Selector="ColorPicker">
<Setter Property="MinWidth" Value="0" />
<Setter Property="MaxWidth" Value="999999" />
</Style>
<Style Selector="ColorPicker > :is(Control)">
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</ColorPicker.Styles>
</ColorPicker>
<!-- NumericUpDown for numeric properties -->
<NumericUpDown Value="{Binding NumericValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsNumericTypeConverter}}"
IsReadOnly="{Binding IsReadOnly}"
FormatString="{Binding PropertyType, Converter={StaticResource NumericFormatConverter}}"
HorizontalAlignment="Stretch" />
<!-- CheckBox for boolean properties -->
<CheckBox IsChecked="{Binding BooleanValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsBooleanTypeConverter}}"
IsEnabled="{Binding !IsReadOnly}"
Content="{Binding DisplayName}"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<!-- ComboBox for enum properties -->
<ComboBox SelectedItem="{Binding Value, Mode=TwoWay}"
ItemsSource="{Binding PropertyType, Converter={StaticResource EnumValuesConverter}}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsEnumConverter}}"
IsEnabled="{Binding !IsReadOnly}"
HorizontalAlignment="Stretch" />
<!-- TextBox for other properties -->
<TextBox Text="{Binding ValueString, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsOtherTypeConverter}}"
IsReadOnly="{Binding IsReadOnly}"
HorizontalAlignment="Stretch" />
</Grid>
</Border>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Flat List View -->
<ItemsControl Name="PART_ItemsControl"
ItemsSource="{TemplateBinding FilteredProperties}"
IsVisible="{TemplateBinding ShowCategories, Converter={StaticResource NegateBoolConverter}}"
Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="models:PropertyItem">
<Border Margin="0,0,0,0" BorderThickness="0">
<Grid MinHeight="32"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="150" MaxWidth="400" SharedSizeGroup="PropertyName" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<!-- Property Name -->
<Border Grid.Column="0"
Padding="8,4"
Margin="{Binding IndentLevel, Converter={StaticResource IndentConverter}}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,1,1">
<StackPanel Orientation="Horizontal" Spacing="4">
<!-- Expander for complex types with arrow icons -->
<ToggleButton IsVisible="{Binding IsExpandable}"
IsChecked="{Binding IsExpanded}"
Width="16" Height="16"
Padding="0"
VerticalAlignment="Center">
<Panel>
<!-- Right Arrow (collapsed/closed state) -->
<Path IsVisible="{Binding !IsExpanded}"
Data="M 4,2 L 10,8 L 4,14 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
<!-- Down Arrow (expanded/open state) -->
<Path IsVisible="{Binding IsExpanded}"
Data="M 2,4 L 8,10 L 14,4 Z"
Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
Width="12" Height="12"
Stretch="Uniform" />
</Panel>
</ToggleButton>
<TextBlock Text="{Binding DisplayName}"
VerticalAlignment="Center"/>
</StackPanel>
</Border>
<!-- Splitter -->
<GridSplitter Grid.Column="1"
Width="4"
Background="Transparent"
ResizeDirection="Columns" />
<!-- Property Value -->
<Border Grid.Column="2"
Padding="8,4"
Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="0,0,0,1">
<Grid>
<!-- Color Picker for Color properties -->
<ColorPicker Color="{Binding ColorValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsColorTypeConverter}}"
IsEnabled="{Binding !IsReadOnly}"
IsAlphaEnabled="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Width="{Binding $parent[Grid].Bounds.Width}">
<ColorPicker.Styles>
<Style Selector="ColorPicker">
<Setter Property="MinWidth" Value="0" />
<Setter Property="MaxWidth" Value="999999" />
</Style>
<Style Selector="ColorPicker > :is(Control)">
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</ColorPicker.Styles>
</ColorPicker>
<!-- NumericUpDown for numeric properties -->
<NumericUpDown Value="{Binding NumericValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsNumericTypeConverter}}"
IsReadOnly="{Binding IsReadOnly}"
FormatString="{Binding PropertyType, Converter={StaticResource NumericFormatConverter}}"
HorizontalAlignment="Stretch" />
<!-- CheckBox for boolean properties -->
<CheckBox IsChecked="{Binding BooleanValue, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsBooleanTypeConverter}}"
IsEnabled="{Binding !IsReadOnly}"
Content="{Binding DisplayName}"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<!-- ComboBox for enum properties -->
<ComboBox SelectedItem="{Binding Value, Mode=TwoWay}"
ItemsSource="{Binding PropertyType, Converter={StaticResource EnumValuesConverter}}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsEnumConverter}}"
IsEnabled="{Binding !IsReadOnly}"
HorizontalAlignment="Stretch" />
<!-- TextBox for other properties -->
<TextBox Text="{Binding ValueString, Mode=TwoWay}"
IsVisible="{Binding PropertyType, Converter={StaticResource IsOtherTypeConverter}}"
IsReadOnly="{Binding IsReadOnly}"
HorizontalAlignment="Stretch" />
</Grid>
</Border>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Panel>
</ScrollViewer>
</Grid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<!-- Converters and Resources -->
<Style Selector="controls|PropertyGrid">
<Style.Resources>
<controls:IndentToMarginConverter x:Key="IndentConverter" />
<controls:EnumValuesConverter x:Key="EnumValuesConverter" />
<controls:IsEnumConverter x:Key="IsEnumConverter" />
<controls:IsSimpleTypeConverter x:Key="IsSimpleTypeConverter" />
<controls:TypeToBoolConverter x:Key="TypeToBoolConverter" />
<controls:ColorToStringConverter x:Key="ColorToStringConverter" />
<controls:ColorToBrushConverter x:Key="ColorToBrushConverter" />
<controls:IsColorTypeConverter x:Key="IsColorTypeConverter" />
<controls:IsNumericTypeConverter x:Key="IsNumericTypeConverter" />
<controls:IsBooleanTypeConverter x:Key="IsBooleanTypeConverter" />
<controls:IsOtherTypeConverter x:Key="IsOtherTypeConverter" />
<controls:NumericFormatConverter x:Key="NumericFormatConverter" />
<controls:NegateBoolConverter x:Key="NegateBoolConverter" />
</Style.Resources>
</Style>
</Styles> </Styles>
@@ -288,11 +288,10 @@ public class PropertyGrid : TemplatedControl
// Apply search filter // Apply search filter
if (!string.IsNullOrWhiteSpace(_searchText)) if (!string.IsNullOrWhiteSpace(_searchText))
{ {
var searchLower = _searchText.ToLowerInvariant();
filtered = filtered.Where(p => filtered = filtered.Where(p =>
p.DisplayName.ToLowerInvariant().Contains(searchLower) || p.DisplayName.Contains(_searchText, StringComparison.OrdinalIgnoreCase) ||
(p.Category?.ToLowerInvariant().Contains(searchLower) ?? false) || (p.Category?.Contains(_searchText, StringComparison.OrdinalIgnoreCase) ?? false) ||
(p.Description?.ToLowerInvariant().Contains(searchLower) ?? false)); (p.Description?.Contains(_searchText, StringComparison.OrdinalIgnoreCase) ?? false));
} }
// Populate flat list // Populate flat list
@@ -8,8 +8,16 @@ namespace Avalonia.PropertyGrid.Controls;
/// <summary> /// <summary>
/// Converts an Avalonia.Media.Color to a SolidColorBrush /// Converts an Avalonia.Media.Color to a SolidColorBrush
/// </summary> /// </summary>
public class ColorToBrushConverter : IValueConverter public sealed class ColorToBrushConverter : IValueConverter
{ {
/// <summary>
/// Converts a Color to a SolidColorBrush.
/// </summary>
/// <param name="value">The Color value to convert.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>A SolidColorBrush with the specified color.</returns>
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Color color) if (value is Color color)
@@ -21,9 +29,17 @@ public class ColorToBrushConverter : IValueConverter
return new SolidColorBrush(Colors.Transparent); return new SolidColorBrush(Colors.Transparent);
} }
/// <summary>
/// Converts a SolidColorBrush back to a Color.
/// </summary>
/// <param name="value">The SolidColorBrush value to convert back.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>The Color from the brush.</returns>
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is SolidColorBrush brush && brush.Color != null) if (value is SolidColorBrush brush)
{ {
return brush.Color; return brush.Color;
} }
@@ -7,8 +7,16 @@ namespace Avalonia.PropertyGrid.Controls;
/// <summary> /// <summary>
/// Converts boolean value to its negation for two-way binding /// Converts boolean value to its negation for two-way binding
/// </summary> /// </summary>
public class NegateBoolConverter : IValueConverter public sealed class NegateBoolConverter : IValueConverter
{ {
/// <summary>
/// Converts a boolean value to its negation.
/// </summary>
/// <param name="value">The boolean value to convert.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>The negated boolean value.</returns>
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is bool boolValue) if (value is bool boolValue)
@@ -18,6 +26,14 @@ public class NegateBoolConverter : IValueConverter
return false; return false;
} }
/// <summary>
/// Converts a boolean value back to its negation (two-way binding).
/// </summary>
/// <param name="value">The boolean value to convert back.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>The negated boolean value.</returns>
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is bool boolValue) if (value is bool boolValue)
@@ -7,8 +7,16 @@ namespace Avalonia.PropertyGrid.Controls;
/// <summary> /// <summary>
/// Converts PropertyType to appropriate numeric format string /// Converts PropertyType to appropriate numeric format string
/// </summary> /// </summary>
public class NumericFormatConverter : IValueConverter public sealed class NumericFormatConverter : IValueConverter
{ {
/// <summary>
/// Converts a Type to an appropriate numeric format string.
/// </summary>
/// <param name="value">The Type to convert.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>A format string like "N2" for floating point or "N0" for integers.</returns>
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Type type) if (value is Type type)
@@ -26,6 +34,15 @@ public class NumericFormatConverter : IValueConverter
return "N0"; return "N0";
} }
/// <summary>
/// Not supported for this converter.
/// </summary>
/// <param name="value">The value to convert back.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>Throws NotImplementedException.</returns>
/// <exception cref="NotImplementedException">This converter does not support ConvertBack.</exception>
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -7,7 +7,7 @@ namespace Avalonia.PropertyGrid.Controls;
/// <summary> /// <summary>
/// Converts indent level to left margin /// Converts indent level to left margin
/// </summary> /// </summary>
public class IndentToMarginConverter : IValueConverter public sealed class IndentToMarginConverter : IValueConverter
{ {
/// <summary> /// <summary>
/// Converts indent level to left margin. /// Converts indent level to left margin.
@@ -33,7 +33,7 @@ public class IndentToMarginConverter : IValueConverter
/// <summary> /// <summary>
/// Converts enum type to list of enum values /// Converts enum type to list of enum values
/// </summary> /// </summary>
public class EnumValuesConverter : IValueConverter public sealed class EnumValuesConverter : IValueConverter
{ {
/// <summary> /// <summary>
/// Converts an enum type to an array of its values. /// Converts an enum type to an array of its values.
@@ -59,8 +59,16 @@ public class EnumValuesConverter : IValueConverter
/// <summary> /// <summary>
/// Checks if a type is an enum /// Checks if a type is an enum
/// </summary> /// </summary>
public class IsEnumConverter : IValueConverter public sealed class IsEnumConverter : IValueConverter
{ {
/// <summary>
/// Checks if the provided type is an enum.
/// </summary>
/// <param name="value">The Type to check.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>True if the type is an enum, otherwise false.</returns>
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Type type) if (value is Type type)
@@ -70,6 +78,10 @@ public class IsEnumConverter : IValueConverter
return false; return false;
} }
/// <summary>
/// Not supported for this converter.
/// </summary>
/// <exception cref="NotImplementedException">This converter does not support ConvertBack.</exception>
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -79,8 +91,16 @@ public class IsEnumConverter : IValueConverter
/// <summary> /// <summary>
/// Checks if a type is a simple type (string, number, bool, etc.) /// Checks if a type is a simple type (string, number, bool, etc.)
/// </summary> /// </summary>
public class IsSimpleTypeConverter : IValueConverter public sealed class IsSimpleTypeConverter : IValueConverter
{ {
/// <summary>
/// Checks if the provided type is a simple type like string, number, DateTime, etc.
/// </summary>
/// <param name="value">The Type to check.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>True if the type is a simple type, otherwise false.</returns>
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Type type) if (value is Type type)
@@ -96,6 +116,10 @@ public class IsSimpleTypeConverter : IValueConverter
return false; return false;
} }
/// <summary>
/// Not supported for this converter.
/// </summary>
/// <exception cref="NotImplementedException">This converter does not support ConvertBack.</exception>
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -105,8 +129,16 @@ public class IsSimpleTypeConverter : IValueConverter
/// <summary> /// <summary>
/// Checks if a type matches the specified type name /// Checks if a type matches the specified type name
/// </summary> /// </summary>
public class TypeToBoolConverter : IValueConverter public sealed class TypeToBoolConverter : IValueConverter
{ {
/// <summary>
/// Checks if the type's name matches the specified parameter string.
/// </summary>
/// <param name="value">The Type to check.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">The type name to match against.</param>
/// <param name="culture">Culture information.</param>
/// <returns>True if the type name matches, otherwise false.</returns>
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Type type && parameter is string typeName) if (value is Type type && parameter is string typeName)
@@ -116,6 +148,10 @@ public class TypeToBoolConverter : IValueConverter
return false; return false;
} }
/// <summary>
/// Not supported for this converter.
/// </summary>
/// <exception cref="NotImplementedException">This converter does not support ConvertBack.</exception>
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -125,8 +161,16 @@ public class TypeToBoolConverter : IValueConverter
/// <summary> /// <summary>
/// Converts Color to/from hex string representation /// Converts Color to/from hex string representation
/// </summary> /// </summary>
public class ColorToStringConverter : IValueConverter public sealed class ColorToStringConverter : IValueConverter
{ {
/// <summary>
/// Converts a Color to a hex string representation.
/// </summary>
/// <param name="value">The Color to convert.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>A hex string representation of the color (e.g., "#AARRGGBB").</returns>
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Media.Color color) if (value is Media.Color color)
@@ -136,6 +180,14 @@ public class ColorToStringConverter : IValueConverter
return value?.ToString(); return value?.ToString();
} }
/// <summary>
/// Converts a hex string back to a Color.
/// </summary>
/// <param name="value">The hex string to convert.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>The parsed Color, or Black if parsing fails.</returns>
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is string hexString && !string.IsNullOrWhiteSpace(hexString)) if (value is string hexString && !string.IsNullOrWhiteSpace(hexString))
@@ -143,7 +195,7 @@ public class ColorToStringConverter : IValueConverter
try try
{ {
hexString = hexString.Trim(); hexString = hexString.Trim();
if (!hexString.StartsWith("#")) if (!hexString.StartsWith('#'))
hexString = "#" + hexString; hexString = "#" + hexString;
return Media.Color.Parse(hexString); return Media.Color.Parse(hexString);
@@ -161,8 +213,16 @@ public class ColorToStringConverter : IValueConverter
/// <summary> /// <summary>
/// Checks if a type is Color /// Checks if a type is Color
/// </summary> /// </summary>
public class IsColorTypeConverter : IValueConverter public sealed class IsColorTypeConverter : IValueConverter
{ {
/// <summary>
/// Checks if the provided type is Avalonia.Media.Color.
/// </summary>
/// <param name="value">The Type to check.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional "Invert" parameter to invert the result.</param>
/// <param name="culture">Culture information.</param>
/// <returns>True if the type is Color (or inverted if parameter is "Invert"), otherwise false.</returns>
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Type type) if (value is Type type)
@@ -187,6 +247,10 @@ public class IsColorTypeConverter : IValueConverter
return false; return false;
} }
/// <summary>
/// Not supported for this converter.
/// </summary>
/// <exception cref="NotImplementedException">This converter does not support ConvertBack.</exception>
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -196,8 +260,16 @@ public class IsColorTypeConverter : IValueConverter
/// <summary> /// <summary>
/// Checks if a type is numeric /// Checks if a type is numeric
/// </summary> /// </summary>
public class IsNumericTypeConverter : IValueConverter public sealed class IsNumericTypeConverter : IValueConverter
{ {
/// <summary>
/// Checks if the provided type is a numeric type (int, long, float, double, decimal, etc.).
/// </summary>
/// <param name="value">The Type to check.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional "Invert" parameter to invert the result.</param>
/// <param name="culture">Culture information.</param>
/// <returns>True if the type is numeric (or inverted if parameter is "Invert"), otherwise false.</returns>
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Type type) if (value is Type type)
@@ -225,6 +297,10 @@ public class IsNumericTypeConverter : IValueConverter
return false; return false;
} }
/// <summary>
/// Not supported for this converter.
/// </summary>
/// <exception cref="NotImplementedException">This converter does not support ConvertBack.</exception>
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -234,8 +310,16 @@ public class IsNumericTypeConverter : IValueConverter
/// <summary> /// <summary>
/// Checks if a type is neither Color nor Numeric nor Boolean nor Enum (for fallback TextBox) /// Checks if a type is neither Color nor Numeric nor Boolean nor Enum (for fallback TextBox)
/// </summary> /// </summary>
public class IsOtherTypeConverter : IValueConverter public sealed class IsOtherTypeConverter : IValueConverter
{ {
/// <summary>
/// Checks if the type is not Color, Numeric, Boolean, or Enum (used for fallback TextBox editor).
/// </summary>
/// <param name="value">The Type to check.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional parameter.</param>
/// <param name="culture">Culture information.</param>
/// <returns>True if the type is not a special type, otherwise false.</returns>
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Type type) if (value is Type type)
@@ -255,6 +339,10 @@ public class IsOtherTypeConverter : IValueConverter
return true; // Default to showing TextBox return true; // Default to showing TextBox
} }
/// <summary>
/// Not supported for this converter.
/// </summary>
/// <exception cref="NotImplementedException">This converter does not support ConvertBack.</exception>
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -264,8 +352,16 @@ public class IsOtherTypeConverter : IValueConverter
/// <summary> /// <summary>
/// Checks if a type is boolean /// Checks if a type is boolean
/// </summary> /// </summary>
public class IsBooleanTypeConverter : IValueConverter public sealed class IsBooleanTypeConverter : IValueConverter
{ {
/// <summary>
/// Checks if the provided type is boolean.
/// </summary>
/// <param name="value">The Type to check.</param>
/// <param name="targetType">The target type.</param>
/// <param name="parameter">Optional "Invert" parameter to invert the result.</param>
/// <param name="culture">Culture information.</param>
/// <returns>True if the type is boolean (or inverted if parameter is "Invert"), otherwise false.</returns>
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (value is Type type) if (value is Type type)
@@ -290,6 +386,10 @@ public class IsBooleanTypeConverter : IValueConverter
return false; return false;
} }
/// <summary>
/// Not supported for this converter.
/// </summary>
/// <exception cref="NotImplementedException">This converter does not support ConvertBack.</exception>
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@@ -266,7 +266,7 @@ public class DateTimeEditor : PropertyEditor
/// <summary> /// <summary>
/// Converter for DateTime to DateTimeOffset /// Converter for DateTime to DateTimeOffset
/// </summary> /// </summary>
internal class DateTimeToDateTimeOffsetConverter : Avalonia.Data.Converters.IValueConverter internal sealed class DateTimeToDateTimeOffsetConverter : Avalonia.Data.Converters.IValueConverter
{ {
public object? Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture) public object? Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo culture)
{ {
@@ -47,6 +47,10 @@ public class CategoryGroup : INotifyPropertyChanged
/// </summary> /// </summary>
public int PropertyCount => Properties.Count; public int PropertyCount => Properties.Count;
/// <summary>
/// Raises the PropertyChanged event for the specified property.
/// </summary>
/// <param name="propertyName">The name of the property that changed.</param>
protected virtual void OnPropertyChanged(string propertyName) protected virtual void OnPropertyChanged(string propertyName)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+1 -1
View File
@@ -182,7 +182,7 @@ public class PropertyItem : INotifyPropertyChanged
try try
{ {
var hexString = value?.Trim() ?? ""; var hexString = value?.Trim() ?? "";
if (!hexString.StartsWith("#")) if (!hexString.StartsWith('#'))
hexString = "#" + hexString; hexString = "#" + hexString;
Value = Media.Color.Parse(hexString); Value = Media.Color.Parse(hexString);
@@ -223,7 +223,7 @@ public class NetworkSettings : INotifyPropertyChanged
{ {
private string _hostname = "api.example.com"; private string _hostname = "api.example.com";
private int _port = 443; private int _port = 443;
private bool _useProxy = false; private bool _useProxy;
private string _proxyAddress = string.Empty; private string _proxyAddress = string.Empty;
[Display(Name = "Hostname", Description = "Server hostname or IP address")] [Display(Name = "Hostname", Description = "Server hostname or IP address")]
+1 -1
View File
@@ -3,7 +3,7 @@ using System;
namespace AdvancedExample; namespace AdvancedExample;
class Program sealed class Program
{ {
[STAThread] [STAThread]
public static void Main(string[] args) => BuildAvaloniaApp() public static void Main(string[] args) => BuildAvaloniaApp()
+1 -1
View File
@@ -3,7 +3,7 @@ using Avalonia;
namespace QuickStartExample; namespace QuickStartExample;
class Program sealed class Program
{ {
[STAThread] [STAThread]
public static void Main(string[] args) => BuildAvaloniaApp() public static void Main(string[] args) => BuildAvaloniaApp()
+7
View File
@@ -0,0 +1,7 @@
<Solution>
<Folder Name="/Examples/">
<Project Path="Examples/AdvancedExample/AdvancedExample.csproj" />
<Project Path="Examples/QuickStartExample/QuickStartExample.csproj" />
</Folder>
<Project Path="Avalonia.PropertyGrid/Avalonia.PropertyGrid.csproj" />
</Solution>