如何同时获取对象的所有非公共属性及其值,以便可以在运行时将对象提供给属性网格以填充其值。
无论何时选择TreeView的TreeViewItem,propertyGrid都会随着TreeView中的选择更改而自动更改PropertyGrid。
这是 xaml 文件。
<UserControl x:Class="WpfExperiment.AdditionalProperty"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:wpg="clr-namespace:System.Windows.Controls"
>
<UserControl.Resources>
<DataTemplate DataType="{x:Type sys:String}" x:Key="RadioButtons">
<RadioButton Margin="0,3" Content="{Binding .}" GroupName="radios" Checked="Item_Checked" />
</DataTemplate>
<ObjectDataProvider x:Key="SortTypes" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="wpg:PropertySort"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<wpg:WpfPropertyGrid x:Name="PropertyGrid1"
Margin="20,20,118,21" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
HelpVisible="{Binding ElementName=ShowDescrip, Path=IsChecked}"
ToolbarVisible="{Binding ElementName=ShowToolbar, Path=IsChecked}"
PropertySort="{Binding ElementName=ComboSort, Path=SelectedItem}" />
<StackPanel Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Top">
<TextBlock Margin="0,0,0,10" >Selected Object:</TextBlock>
<ItemsControl x:Name="ItemList" ItemTemplate="{StaticResource RadioButtons}" Height="100" Visibility="{Binding }" />
<TextBlock Margin="0,10,0,3" >Selection Type:</TextBlock>
<TextBlock Margin="0,10,0,3" >Features:</TextBlock>
<CheckBox Name="ShowDescrip" Margin="0,3,0,3" Content="Help Visible" />
<CheckBox Name="ShowToolbar" Margin="0,3,0,3" IsChecked="True" Content="Toolbar Visible" />
<ComboBox Name="ComboSort" Margin="0,3,0,0" Width="95" FontSize="10"
SelectedIndex="0" ItemsSource="{Binding Source={StaticResource SortTypes}}" />
</StackPanel>
</Grid>
</UserControl>
AdditionalProperty.xaml.cs
/// <summary>
/// Interaction logic for AdditionalProperty.xaml
/// </summary>
public partial class AdditionalProperty : UserControl
{
static private Person Person;
public AdditionalProperty(Person _person)
{
Person = _person;
}
// names must match the data members
private object[] ItemArray = { "Person" };
public AdditionalProperty()
{
InitializeComponent();
this.ItemList.ItemTemplate = this.Resources["RadioButtons"] as DataTemplate;
this.ItemList.ItemsSource = this.ItemArray;
}
private void Item_Checked(object sender, RoutedEventArgs e)
{
if (e.Source is RadioButton)
{
object selected = this.GetType().GetField("Person", System.Reflection.BindingFlags.NonPublic).GetValue(this);
this. PropertyGrid1.SelectedObject = selected;
}
}
}
}
这个用户控件嵌入在这样的窗口控件中。
<Window x:Class="WpfExperiment.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:wpg="clr-namespace:System.Windows.Controls"
xmlns:local="clr-namespace:WpfExperiment"
Title="MainWindow" Height="350" Width="525">
<local:AdditionalProperty Grid.Column="1" />
</Grid>
</Window>
主窗口.xaml.cs
public partial class MainWindow : Window
{
private Person Person = new Person("Shakti");
// names must match the data members
private object[] ItemArray = { "Person"};
public MainWindow()
{
InitializeComponent();
new AdditionalProperty(Person);
}
}
WpfPropertyGrid 代码:
namespace System.Windows.Controls
{
public enum PropertySort
{
NoSort = 0,
Alphabetical = 1,
Categorized = 2,
CategorizedAlphabetical = 3
};
/// <summary>WPF Native PropertyGrid class, uses Workflow Foundation's PropertyInspector</summary>
public class WpfPropertyGrid : Grid
{
#region Private fields
private WorkflowDesigner Designer;
private MethodInfo RefreshMethod;
private MethodInfo OnSelectionChangedMethod;
private MethodInfo IsInAlphaViewMethod;
private TextBlock SelectionTypeLabel;
private Control PropertyToolBar;
private Border HelpText;
private GridSplitter Splitter;
private double HelpTextHeight = 60;
#endregion
#region Public properties
/// <summary>Get or sets the selected object. Can be null.</summary>
public object SelectedObject
{
get { return GetValue(SelectedObjectProperty); }
set { SetValue(SelectedObjectProperty, value); }
}
/// <summary>Get or sets the selected object collection. Returns empty array by default.</summary>
public object[] SelectedObjects
{
get { return GetValue(SelectedObjectsProperty) as object[]; }
set { SetValue(SelectedObjectsProperty, value); }
}
/// <summary>XAML information with PropertyGrid's font and color information</summary>
/// <seealso>Documentation for WorkflowDesigner.PropertyInspectorFontAndColorData</seealso>
public string FontAndColorData
{
set
{
Designer.PropertyInspectorFontAndColorData = value;
}
}
/// <summary>Shows the description area on the top of the control</summary>
public bool HelpVisible
{
get { return (bool)GetValue(HelpVisibleProperty); }
set { SetValue(HelpVisibleProperty, value); }
}
/// <summary>Shows the tolbar on the top of the control</summary>
public bool ToolbarVisible
{
get { return (bool)GetValue(ToolbarVisibleProperty); }
set { SetValue(ToolbarVisibleProperty, value); }
}
public PropertySort PropertySort
{
get { return (PropertySort)GetValue(PropertySortProperty); }
set { SetValue(PropertySortProperty, value); }
}
#endregion
#region Dependency properties registration
public static readonly DependencyProperty SelectedObjectProperty =
DependencyProperty.Register("SelectedObject", typeof(object), typeof(WpfPropertyGrid),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedObjectPropertyChanged));
public static readonly DependencyProperty SelectedObjectsProperty =
DependencyProperty.Register("SelectedObjects", typeof(object[]), typeof(WpfPropertyGrid),
new FrameworkPropertyMetadata(new object[0], FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedObjectsPropertyChanged, CoerceSelectedObjects));
public static readonly DependencyProperty HelpVisibleProperty =
DependencyProperty.Register("HelpVisible", typeof(bool), typeof(WpfPropertyGrid),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, HelpVisiblePropertyChanged));
public static readonly DependencyProperty ToolbarVisibleProperty =
DependencyProperty.Register("ToolbarVisible", typeof(bool), typeof(WpfPropertyGrid),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ToolbarVisiblePropertyChanged));
public static readonly DependencyProperty PropertySortProperty =
DependencyProperty.Register("PropertySort", typeof(PropertySort), typeof(WpfPropertyGrid),
new FrameworkPropertyMetadata(PropertySort.CategorizedAlphabetical, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PropertySortPropertyChanged));
#endregion
#region Dependency properties events
private static object CoerceSelectedObject(DependencyObject d, object value)
{
WpfPropertyGrid pg = d as WpfPropertyGrid;
object[] collection = pg.GetValue(SelectedObjectsProperty) as object[];
return collection.Length == 0 ? null : value;
}
private static object CoerceSelectedObjects(DependencyObject d, object value)
{
WpfPropertyGrid pg = d as WpfPropertyGrid;
object single = pg.GetValue(SelectedObjectsProperty);
return single == null ? new object[0] : value;
}
private static void SelectedObjectPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
WpfPropertyGrid pg = source as WpfPropertyGrid;
pg.CoerceValue(SelectedObjectsProperty);
if (e.NewValue == null)
{
pg.OnSelectionChangedMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { null });
pg.SelectionTypeLabel.Text = string.Empty;
}
else
{
var context = new EditingContext();
var mtm = new ModelTreeManager(context);
mtm.Load(e.NewValue);
Selection selection = Selection.Select(context, mtm.Root);
pg.OnSelectionChangedMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { selection });
pg.SelectionTypeLabel.Text = e.NewValue.GetType().Name;
}
pg.ChangeHelpText(string.Empty, string.Empty);
}
private static void SelectedObjectsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
WpfPropertyGrid pg = source as WpfPropertyGrid;
pg.CoerceValue(SelectedObjectsProperty);
object[] collection = e.NewValue as object[];
if (collection.Length == 0)
{
pg.OnSelectionChangedMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { null });
pg.SelectionTypeLabel.Text = string.Empty;
}
else
{
bool same = true;
Type first = null;
var context = new EditingContext();
var mtm = new ModelTreeManager(context);
Selection selection = null;
// Accumulates the selection and determines the type to be shown in the top of the PG
for (int i = 0; i < collection.Length; i++)
{
mtm.Load(collection[i]);
if (i == 0)
{
selection = Selection.Select(context, mtm.Root);
first = collection[0].GetType();
}
else
{
selection = Selection.Union(context, mtm.Root);
if (!collection[i].GetType().Equals(first))
same = false;
}
}
pg.OnSelectionChangedMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { selection });
pg.SelectionTypeLabel.Text = same ? first.Name + " <multiple>" : "Object <multiple>";
}
pg.ChangeHelpText(string.Empty, string.Empty);
}
private static void HelpVisiblePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
WpfPropertyGrid pg = source as WpfPropertyGrid;
if (e.NewValue != e.OldValue)
{
if (e.NewValue.Equals(true))
{
pg.RowDefinitions[1].Height = new GridLength(5);
pg.RowDefinitions[2].Height = new GridLength(pg.HelpTextHeight);
}
else
{
pg.HelpTextHeight = pg.RowDefinitions[2].Height.Value;
pg.RowDefinitions[1].Height = new GridLength(0);
pg.RowDefinitions[2].Height = new GridLength(0);
}
}
}
private static void ToolbarVisiblePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
WpfPropertyGrid pg = source as WpfPropertyGrid;
pg.PropertyToolBar.Visibility = e.NewValue.Equals(true) ? Visibility.Visible : Visibility.Collapsed;
}
private static void PropertySortPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
WpfPropertyGrid pg = source as WpfPropertyGrid;
PropertySort sort = (PropertySort)e.NewValue;
bool isAlpha = (sort == PropertySort.Alphabetical || sort == PropertySort.NoSort);
pg.IsInAlphaViewMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { isAlpha });
}
#endregion
/// <summary>Default constructor, creates the UIElements including a PropertyInspector</summary>
public WpfPropertyGrid()
{
this.ColumnDefinitions.Add(new ColumnDefinition());
this.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
this.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0) });
this.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0) });
this.Designer = new WorkflowDesigner();
TextBlock title = new TextBlock()
{
Visibility = Windows.Visibility.Visible,
TextWrapping = TextWrapping.NoWrap,
TextTrimming = TextTrimming.CharacterEllipsis,
FontWeight = FontWeights.Bold
};
TextBlock descrip = new TextBlock()
{
Visibility = Windows.Visibility.Visible,
TextWrapping = TextWrapping.Wrap,
TextTrimming = TextTrimming.CharacterEllipsis
};
DockPanel dock = new DockPanel()
{
Visibility = Windows.Visibility.Visible,
LastChildFill = true,
Margin = new Thickness(3, 0, 3, 0)
};
title.SetValue(DockPanel.DockProperty, Dock.Top);
dock.Children.Add(title);
dock.Children.Add(descrip);
this.HelpText = new Border()
{
Visibility = Windows.Visibility.Visible,
BorderBrush = SystemColors.ActiveBorderBrush,
Background = SystemColors.ControlBrush,
BorderThickness = new Thickness(1),
Child = dock
};
this.Splitter = new GridSplitter()
{
Visibility = Windows.Visibility.Visible,
ResizeDirection = GridResizeDirection.Rows,
Height = 5,
HorizontalAlignment = Windows.HorizontalAlignment.Stretch
};
var inspector = Designer.PropertyInspectorView;
inspector.Visibility = Visibility.Visible;
inspector.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Stretch);
this.Splitter.SetValue(Grid.RowProperty, 1);
this.Splitter.SetValue(Grid.ColumnProperty, 0);
this.HelpText.SetValue(Grid.RowProperty, 2);
this.HelpText.SetValue(Grid.ColumnProperty, 0);
Binding binding = new Binding("Parent.Background");
title.SetBinding(BackgroundProperty, binding);
descrip.SetBinding(BackgroundProperty, binding);
this.Children.Add(inspector);
this.Children.Add(this.Splitter);
this.Children.Add(this.HelpText);
Type inspectorType = inspector.GetType();
var props = inspectorType.GetProperties(Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance |
Reflection.BindingFlags.DeclaredOnly);
var methods = inspectorType.GetMethods(Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance |
Reflection.BindingFlags.DeclaredOnly);
this.RefreshMethod = inspectorType.GetMethod("RefreshPropertyList",
Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance | Reflection.BindingFlags.DeclaredOnly);
this.IsInAlphaViewMethod = inspectorType.GetMethod("set_IsInAlphaView",
Reflection.BindingFlags.Public | Reflection.BindingFlags.Instance | Reflection.BindingFlags.DeclaredOnly);
this.OnSelectionChangedMethod = inspectorType.GetMethod("OnSelectionChanged",
Reflection.BindingFlags.Public | Reflection.BindingFlags.Instance | Reflection.BindingFlags.DeclaredOnly);
this.SelectionTypeLabel = inspectorType.GetMethod("get_SelectionTypeLabel",
Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance |
Reflection.BindingFlags.DeclaredOnly).Invoke(inspector, new object[0]) as TextBlock;
this.PropertyToolBar = inspectorType.GetMethod("get_PropertyToolBar",
Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance |
Reflection.BindingFlags.DeclaredOnly).Invoke(inspector, new object[0]) as Control;
inspectorType.GetEvent("GotFocus").AddEventHandler(this,
Delegate.CreateDelegate(typeof(RoutedEventHandler), this, "GotFocusHandler", false));
this.SelectionTypeLabel.Text = string.Empty;
}
/// <summary>Updates the PropertyGrid's properties</summary>
public void RefreshPropertyList()
{
RefreshMethod.Invoke(Designer.PropertyInspectorView, new object[] { false });
}
/// <summary>Traps the change of focused property and updates the help text</summary>
/// <param name="sender">Not used</param>
/// <param name="args">Points to the source control containing the selected property</param>
private void GotFocusHandler(object sender, RoutedEventArgs args)
{
//if (args.OriginalSource is TextBlock)
//{
string title = string.Empty;
string descrip = string.Empty;
var theSelectedObjects = this.GetValue(SelectedObjectsProperty) as object[];
if (theSelectedObjects != null && theSelectedObjects.Length > 0)
{
Type first = theSelectedObjects[0].GetType();
for (int i = 1; i < theSelectedObjects.Length; i++)
{
if (!theSelectedObjects[i].GetType().Equals(first))
{
ChangeHelpText(title, descrip);
return;
}
}
object data = (args.OriginalSource as FrameworkElement).DataContext;
PropertyInfo propEntry = data.GetType().GetProperty("PropertyEntry");
if (propEntry == null)
{
propEntry = data.GetType().GetProperty("ParentProperty");
}
if (propEntry != null)
{
object propEntryValue = propEntry.GetValue(data, null);
string propName = propEntryValue.GetType().GetProperty("PropertyName").GetValue(propEntryValue, null) as string;
title = propEntryValue.GetType().GetProperty("DisplayName").GetValue(propEntryValue, null) as string;
PropertyInfo property = theSelectedObjects[0].GetType().GetProperty(propName);
object[] attrs = property.GetCustomAttributes(typeof(DescriptionAttribute), true);
if (attrs != null && attrs.Length > 0)
descrip = (attrs[0] as DescriptionAttribute).Description;
}
ChangeHelpText(title, descrip);
}
//}
}
/// <summary>Changes the text help area contents</summary>
/// <param name="title">Title in bold</param>
/// <param name="descrip">Description with ellipsis</param>
private void ChangeHelpText(string title, string descrip)
{
DockPanel dock = this.HelpText.Child as DockPanel;
(dock.Children[0] as TextBlock).Text = title;
(dock.Children[1] as TextBlock).Text = descrip;
}
}
}
人物类:
public class Person
{
public enum Gender { Male, Female }
#region private fields
private string[] _Names = new string[3];
#endregion
// The following properties are wrapping an array of strings
#region Public Properties
[Category("Name")]
[DisplayName("First Name")]
public string FirstName
{
set { _Names[0] = value; }
get { return _Names[0]; }
}
[Category("Name")]
[DisplayName("Mid Name")]
public string MidName
{
set { _Names[1] = value; }
get { return _Names[1]; }
}
[Category("Name")]
[DisplayName("Last Name")]
public string LastName
{
set { _Names[2] = value; }
get { return _Names[2]; }
}
// The following are autoimplemented properties (C# 3.0 and up)
[Category("Characteristics")]
[DisplayName("Gender")]
public Gender PersonGender { get; set; }
[Category("Characteristics")]
[DisplayName("Birth Date")]
public DateTime BirthDate { get; set; }
[Category("Characteristics")]
public int Income { get; set; }
// Other cases of hidden read-only property and formatted property
[DisplayName("GUID"), ReadOnly(true), Browsable(true)] // many attributes defined in the same row
public string GuidStr
{
get { return Guid.ToString(); }
}
[Browsable(false)] // this property will not be displayed
public System.Guid Guid
{
get;
private set;
}
#endregion
public Person(string name)
{
// default values
for (int i = 0; i < 3; i++)
_Names[i] = name;
this.PersonGender = Gender.Male;
this.Guid = System.Guid.NewGuid();
}
public override string ToString()
{
return string.Format("{0} {1} {2}", FirstName, MidName, LastName).Trim().Replace(" ", " ");
}
}
我无法理解为什么部分代码返回 null:
object selected = **this.GetType().GetField("Person", System.Reflection.BindingFlags.NonPublic)**.GetValue(this);
仅供参考,此代码已改编自 [http://www.codeproject.com/Articles/87715/Native-WPF-4-PropertyGrid]