I'm trying to make a PropertyGrid Custom Control. The PropertyGrid will be very similar to the PropertyGrid used in Visual Studio. I've tried using the PropertyGrid of the Extended WPF Toolkit but you have to specify the category of a property with an attribute and we need to change the categories runtime. Which as far as I know is impossible with attributes.
So I'm making the PropertyGrid myself. This is my code so far:
The Generic.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HomeMadePropertyGrid"
xmlns:System="clr-namespace:System;assembly=mscorlib">
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"></BooleanToVisibilityConverter>
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<ControlTemplate x:Key="toggleButtonTemplate" TargetType="ToggleButton">
<Grid Width="15" Height="13" Background="Transparent">
<Path x:Name="ExpandPath" Fill="{StaticResource GlyphBrush}" Data="M 4 0 L 8 4 L 4 8 Z" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="1,1,1,1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Data" TargetName="ExpandPath" Value="M 0 4 L 8 4 L 4 8 Z"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="toggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Template" Value="{StaticResource toggleButtonTemplate}" />
</Style>
<Style TargetType="{x:Type local:PropertyGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PropertyGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsControl ItemsSource="{TemplateBinding ItemsSource}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Background="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="toggleButton" Height="20" Width="20" Style="{StaticResource toggleButtonStyle}"/>
<TextBlock Text="{Binding Name}" FontWeight="Bold"></TextBlock>
</StackPanel>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter={StaticResource BoolToVisConverter}}"
Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<TextBlock Background="White" Text="{Binding Path=Name}"/>
</Border>
<GridSplitter Width="1"
Grid.RowSpan="4" Grid.Column="1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}"/>
<Border Grid.Column="2" BorderThickness="1" BorderBrush="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<ContentPresenter Grid.Column="2" Content="{Binding Value}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type System:String}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Int32}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
TextAlignment="Right"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Double}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
TextAlignment="Right"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Boolean}">
<CheckBox IsChecked="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
PropertyGrid.cs
public class PropertyGrid : ItemsControl
{
public Brush GridColor
{
get { return (Brush)GetValue(GridColorProperty); }
set { SetValue(GridColorProperty, value); }
}
public static readonly DependencyProperty GridColorProperty =
DependencyProperty.Register("GridColor", typeof(Brush), typeof(PropertyGrid), new UIPropertyMetadata(new SolidColorBrush(Colors.Transparent)));
static PropertyGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid)));
}
}
PropertyGroup
public class PropertyGroup
{
public string Name { get; set; }
public List<PropertyGridItem> Items { get; set; }
public PropertyGroup()
{
Items = new List<PropertyGridItem>();
Name = "";
}
}
PropertyGridItem
public class PropertyGridItem
{
public string Name { get; set; }
public object Value { get; set; }
public PropertyGridItem(string propertyName, object propertyValue)
{
Name = propertyName;
Value = propertyValue;
}
}
This code in my MainWindow.xaml:
<local:PropertyGrid ItemsSource="{Binding Path=Groups}" GridColor="#f0f0f0"/>
Code behind of my ViewModel:
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
The ViewModel
public class ViewModel
{
public List<PropertyGroup> Groups { get; set; }
public ViewModel()
{
Groups = new List<PropertyGroup>();
PropertyGroup group1 = new PropertyGroup();
group1.Name = "Group1";
group1.Items.Add(new PropertyGridItem("Item1", "test"));
group1.Items.Add(new PropertyGridItem("Item2", 300));
group1.Items.Add(new PropertyGridItem("Item3", true));
group1.Items.Add(new PropertyGridItem("Item4", 5.2));
Groups.Add(group1);
PropertyGroup group2 = new PropertyGroup();
group2.Name = "Group2";
group2.Items.Add(new PropertyGridItem("Item1", "test"));
group2.Items.Add(new PropertyGridItem("Item2", 300));
group2.Items.Add(new PropertyGridItem("Item3", true));
group2.Items.Add(new PropertyGridItem("Item4", 5.2));
Groups.Add(group2);
}
}
The problem I'm having is that the GridSplitter is applied every row. I want the GridSplitter to be applied to all rows of a group. I understand that this is because I make a new Grid for every item. For the attached properties to work the items have to be a direct child of the Grid. A DataGrid also isn't an option because the GridSplitter is only available between column headers.
So to make a long story short: how can I use a Grid in an ItemsControl with a GridSplitter that applies to all rows of ideally a group or the entire Grid if that isn't possible.