If you want to use some simple XAML, the following is visually identical to the WinForms PropertyGrid, but itβs much easier to work with:
<Style x:Key="InnerBorder" TargetType="{x:Type Border}"> <Setter Property="BorderThickness" Value="1" /> <Setter Property="Margin" Value="4" /> <Setter Property="BorderBrush" Value="#B4B0A8" /> </Style> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Border Style="{StaticResource InnerBorder}"> <ListBox ItemsSource="{Binding Parameters}" IsSynchronizedWithCurrentItem="True" KeyboardNavigation.TabNavigation="Continue" HorizontalContentAlignment="Stretch" BorderThickness="0"> <ListBox.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" Background="#D4D0C8" FontWeight="Bold" Padding="2 2 0 4" Margin="0 0 0 3"/> </DataTemplate> </GroupStyle.HeaderTemplate> <GroupStyle.ContainerStyle> <Style> <Setter Property="Control.Margin" Value="0 0 0 8" /> </Style> </GroupStyle.ContainerStyle> </GroupStyle> </ListBox.GroupStyle> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="Focusable" Value="False" /> <Setter Property="TabIndex" Value="0" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <DockPanel Margin="4 0 0 0" IsKeyboardFocusWithinChanged="DockPanel_IsKeyboardFocusWithinChanged" MouseDown="DockPanel_MouseDown"> <TextBlock Name="TitleBlock" Text="{Binding DisplayName}" Width="135" /> <ContentPresenter /> </DockPanel> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="true"> <Setter TargetName="TitleBlock" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> <Setter TargetName="TitleBlock" Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </ListBox.ItemContainerStyle> </ListBox> </Border> <Border Style="{StaticResource InnerBorder}" Grid.Row="1" DataContext="{Binding Parameters}"> <StackPanel HorizontalAlignment="Stretch" Margin="2"> <TextBlock FontWeight="Bold" Text="{Binding /DisplayName}" /> <TextBlock Text="{Binding /Description}" TextWrapping="Wrap" /> </StackPanel> </Border> </Grid>
And here is the code for
private void DockPanel_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e) { var element = (FrameworkElement)sender; if(element.IsKeyboardFocusWithin) { Visual cur = element; while(cur!=null && !(cur is ListBoxItem)) cur = (Visual)VisualTreeHelper.GetParent(cur); ((ListBoxItem)cur).IsSelected = true; } } private void DockPanel_MouseDown(object sender, MouseEventArgs e) { ((FrameworkElement)sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); } private void InitializeView() { var view = CollectionViewSource.GetDefaultView(Parameters); if(view.GroupDescriptions.Count==0) view.GroupDescriptions.Add(new PropertyGroupDescription("Category")); if(view.SortDescriptions.Count==0) { view.SortDescriptions.Add(new SortDescription("Category", ListSortDirection.Ascending)); view.SortDescriptions.Add(new SortDescription("DisplayName", ListSortDirection.Ascending)); } }
The reason this property grid works better with WPF is because you can add any object to the Parameters collection if it has a Category, DisplayName, and Description.
If you want to use it to actually display the properties of a specific object, it takes only a few lines to load a collection of parameters using the corresponding objects.
Ray burns
source share