Add support for Category, Name, and Description to PropertyGrid using DisplayAttribute

This commit is contained in:
Brant Martin
2018-02-11 22:43:22 -05:00
parent 04e949ed0c
commit c32badb750
3 changed files with 136 additions and 78 deletions

View File

@@ -118,7 +118,7 @@
</StackPanel> </StackPanel>
<Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" /> <Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" />
</Grid> </Grid>
<views:PropertyGrid Grid.Column="1" Margin="3" DataContext="{Binding SessionSettings}" /> <views:PropertyGrid Grid.Column="1" Margin="3" DataContext="{Binding SessionSettings}" IgnoreDisplay ="True" />
<GridSplitter Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" ShowsPreview="True" <GridSplitter Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" ShowsPreview="True"
Width="2" Background="Gray" /> Width="2" Background="Gray" />
</Grid> </Grid>

View File

@@ -9,6 +9,7 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition/> <RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="0"> <Grid Grid.Row="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -19,5 +20,6 @@
<TextBox Grid.Column="1" Margin="3" TextChanged="UpdateFilter"/> <TextBox Grid.Column="1" Margin="3" TextChanged="UpdateFilter"/>
</Grid> </Grid>
<ScrollViewer Grid.Row="1" x:Name="ScrollViewer"/> <ScrollViewer Grid.Row="1" x:Name="ScrollViewer"/>
<TextBlock x:Name="TbDescription" Grid.Row="2" MinHeight="18" Background="#FFBBB9B9"/>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -29,6 +30,14 @@ namespace Torch.Views
{ {
private Dictionary<Type, Grid> _viewCache = new Dictionary<Type, Grid>(); private Dictionary<Type, Grid> _viewCache = new Dictionary<Type, Grid>();
public static readonly DependencyProperty IgnoreDisplayProperty = DependencyProperty.Register("IgnoreDisplay", typeof(bool), typeof(PropertyGrid));
public bool IgnoreDisplay
{
get => (bool)base.GetValue(IgnoreDisplayProperty);
set => base.SetValue(IgnoreDisplayProperty, value);
}
public PropertyGrid() public PropertyGrid()
{ {
InitializeComponent(); InitializeComponent();
@@ -59,114 +68,161 @@ namespace Torch.Views
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
var curRow = 0; var categories = new Dictionary<string, List<PropertyInfo>>();
foreach (var property in properties.OrderBy(x => x.Name)) var descriptors = new Dictionary<PropertyInfo, DisplayAttribute>(properties.Length);
foreach (var property in properties)
{ {
if (property.GetGetMethod() == null) var a = property.GetCustomAttribute<DisplayAttribute>();
continue; if (IgnoreDisplay)
a = null;
descriptors[property] = a;
string category = a?.GroupName ?? "Misc";
if (!categories.TryGetValue(category, out List<PropertyInfo> l))
{
l = new List<PropertyInfo>();
categories[category] = l;
}
l.Add(property);
}
var curRow = 0;
foreach (var c in categories.OrderBy(x => x.Key))
{
grid.RowDefinitions.Add(new RowDefinition()); grid.RowDefinitions.Add(new RowDefinition());
var cl = new TextBlock
{
Text = c.Key,
VerticalAlignment = VerticalAlignment.Center
};
cl.SetValue(Grid.ColumnProperty, 0);
cl.SetValue(Grid.ColumnSpanProperty, 2);
cl.SetValue(Grid.RowProperty, curRow);
cl.Margin = new Thickness(3);
cl.FontWeight = FontWeights.Bold;
grid.Children.Add(cl);
curRow++;
var displayName = property.GetCustomAttribute<DisplayAttribute>()?.Name; c.Value.Sort((a,b)=> string.Compare((descriptors[a]?.Name ?? a.Name), descriptors[b]?.Name ?? b.Name, StringComparison.Ordinal));
var propertyType = property.PropertyType;
var text = new TextBlock foreach (var property in c.Value)
{ {
Text = property.Name, if (property.GetGetMethod() == null)
ToolTip = displayName, continue;
VerticalAlignment = VerticalAlignment.Center
};
text.SetValue(Grid.ColumnProperty, 0);
text.SetValue(Grid.RowProperty, curRow);
text.Margin = new Thickness(3);
grid.Children.Add(text);
FrameworkElement valueControl; grid.RowDefinitions.Add(new RowDefinition());
if (property.GetSetMethod() == null)
{ var descriptor = descriptors[property];
valueControl = new TextBlock(); var displayName = descriptor?.Name;
var binding = new Binding(property.Name) var propertyType = property.PropertyType;
var text = new TextBlock
{
Text = displayName ?? property.Name,
ToolTip = displayName,
VerticalAlignment = VerticalAlignment.Center
};
text.SetValue(Grid.ColumnProperty, 0);
text.SetValue(Grid.RowProperty, curRow);
text.Margin = new Thickness(3);
text.Tag = $"{text.Text}: {descriptor?.Description}";
text.IsMouseDirectlyOverChanged += Text_IsMouseDirectlyOverChanged;
grid.Children.Add(text);
FrameworkElement valueControl;
if (property.GetSetMethod() == null)
{ {
Mode = BindingMode.OneWay valueControl = new TextBlock();
}; var binding = new Binding(property.Name)
valueControl.SetBinding(TextBlock.TextProperty, binding); {
} Mode = BindingMode.OneWay
else if (propertyType == typeof(bool) || propertyType == typeof(bool?)) };
{ valueControl.SetBinding(TextBlock.TextProperty, binding);
valueControl = new CheckBox(); }
valueControl.SetBinding(CheckBox.IsCheckedProperty, property.Name); else if (propertyType == typeof(bool) || propertyType == typeof(bool?))
}
else if (propertyType.IsEnum)
{
valueControl = new ComboBox
{ {
ItemsSource = Enum.GetValues(property.PropertyType) valueControl = new CheckBox();
}; valueControl.SetBinding(CheckBox.IsCheckedProperty, property.Name);
valueControl.SetBinding(ComboBox.SelectedItemProperty, property.Name); }
} else if (propertyType.IsEnum)
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
var button = new Button
{ {
Content = "Edit Collection" valueControl = new ComboBox
}; {
button.SetBinding(Button.DataContextProperty, property.Name); ItemsSource = Enum.GetValues(property.PropertyType)
button.Click += (sender, args) => EditDictionary(((Button)sender).DataContext); };
valueControl.SetBinding(ComboBox.SelectedItemProperty, property.Name);
}
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
var button = new Button
{
Content = "Edit Collection"
};
button.SetBinding(Button.DataContextProperty, property.Name);
button.Click += (sender, args) => EditDictionary(((Button)sender).DataContext);
valueControl = button; valueControl = button;
} }
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(SerializableDictionary<,>)) else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(SerializableDictionary<,>))
{
var button = new Button
{ {
Content = "Edit Collection" var button = new Button
}; {
button.SetBinding(Button.DataContextProperty, $"{property.Name}.Dictionary"); Content = "Edit Collection"
button.Click += (sender, args) => EditDictionary(((Button)sender).DataContext); };
button.SetBinding(Button.DataContextProperty, $"{property.Name}.Dictionary");
button.Click += (sender, args) => EditDictionary(((Button)sender).DataContext);
valueControl = button; valueControl = button;
} }
else if (propertyType.IsGenericType && typeof(ICollection).IsAssignableFrom(propertyType.GetGenericTypeDefinition())) else if (propertyType.IsGenericType && typeof(ICollection).IsAssignableFrom(propertyType.GetGenericTypeDefinition()))
{ {
var button = new Button var button = new Button
{ {
Content = "Edit Collection" Content = "Edit Collection"
}; };
button.SetBinding(Button.DataContextProperty, $"{property.Name}"); button.SetBinding(Button.DataContextProperty, $"{property.Name}");
var gt = propertyType.GetGenericArguments()[0]; var gt = propertyType.GetGenericArguments()[0];
//TODO: Is this the best option? Probably not //TODO: Is this the best option? Probably not
if (gt.IsPrimitive || gt == typeof(string)) if (gt.IsPrimitive || gt == typeof(string))
{ {
button.Click += (sender, args) => EditPrimitiveCollection(((Button)sender).DataContext); button.Click += (sender, args) => EditPrimitiveCollection(((Button)sender).DataContext);
}
else
{
button.Click += (sender, args) => EditObjectCollection(((Button)sender).DataContext);
}
valueControl = button;
} }
else else
{ {
button.Click += (sender, args) => EditObjectCollection(((Button)sender).DataContext); valueControl = new TextBox();
valueControl.SetBinding(TextBox.TextProperty, property.Name);
} }
valueControl = button; valueControl.Margin = new Thickness(3);
} valueControl.VerticalAlignment = VerticalAlignment.Center;
else valueControl.SetValue(Grid.ColumnProperty, 1);
{ valueControl.SetValue(Grid.RowProperty, curRow);
valueControl = new TextBox(); valueControl.IsMouseDirectlyOverChanged += Text_IsMouseDirectlyOverChanged;
valueControl.SetBinding(TextBox.TextProperty, property.Name); grid.Children.Add(valueControl);
}
valueControl.Margin = new Thickness(3); curRow++;
valueControl.VerticalAlignment = VerticalAlignment.Center; }
valueControl.SetValue(Grid.ColumnProperty, 1);
valueControl.SetValue(Grid.RowProperty, curRow);
grid.Children.Add(valueControl);
curRow++;
} }
_viewCache.Add(t, grid); _viewCache.Add(t, grid);
return grid; return grid;
} }
private void Text_IsMouseDirectlyOverChanged(object sender, DependencyPropertyChangedEventArgs e)
{
TbDescription.Text = (sender as FrameworkElement)?.Tag?.ToString() ?? string.Empty;
}
private void EditDictionary(object dict) private void EditDictionary(object dict)
{ {
var dic = (IDictionary)dict; var dic = (IDictionary)dict;