WpfPropertyGrid.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. // *********************************************************************
  2. // PLEASE DO NOT REMOVE THIS DISCLAIMER
  3. //
  4. // WpfPropertyGrid - By Jaime Olivares
  5. // July 11, 2011
  6. // Article site: http://www.codeproject.com/KB/grid/WpfPropertyGrid.aspx
  7. // Author site: www.jaimeolivares.com
  8. // License: Code Project Open License (CPOL)
  9. //
  10. // *********************************************************************
  11. using System.Activities.Presentation;
  12. using System.Activities.Presentation.Model;
  13. using System.Activities.Presentation.View;
  14. using System.ComponentModel;
  15. using System.Reflection;
  16. using System.Windows.Data;
  17. namespace System.Windows.Controls
  18. {
  19. public enum PropertySort
  20. {
  21. NoSort = 0,
  22. Alphabetical = 1,
  23. Categorized = 2,
  24. CategorizedAlphabetical = 3
  25. };
  26. /// <summary>WPF Native PropertyGrid class, uses Workflow Foundation's PropertyInspector</summary>
  27. public class WpfPropertyGrid : Grid
  28. {
  29. #region Private fields
  30. private WorkflowDesigner Designer;
  31. private MethodInfo RefreshMethod;
  32. private MethodInfo OnSelectionChangedMethod;
  33. private MethodInfo IsInAlphaViewMethod;
  34. private TextBlock SelectionTypeLabel;
  35. private Control PropertyToolBar;
  36. private Border HelpText;
  37. private GridSplitter Splitter;
  38. private double HelpTextHeight = 60;
  39. #endregion
  40. #region Public properties
  41. /// <summary>Get or sets the selected object. Can be null.</summary>
  42. public object SelectedObject
  43. {
  44. get { return GetValue(SelectedObjectProperty); }
  45. set { SetValue(SelectedObjectProperty, value); }
  46. }
  47. /// <summary>Get or sets the selected object collection. Returns empty array by default.</summary>
  48. public object[] SelectedObjects
  49. {
  50. get { return GetValue(SelectedObjectsProperty) as object[]; }
  51. set { SetValue(SelectedObjectsProperty, value); }
  52. }
  53. /// <summary>XAML information with PropertyGrid's font and color information</summary>
  54. /// <seealso>Documentation for WorkflowDesigner.PropertyInspectorFontAndColorData</seealso>
  55. public string FontAndColorData
  56. {
  57. set
  58. {
  59. Designer.PropertyInspectorFontAndColorData = value;
  60. }
  61. }
  62. /// <summary>Shows the description area on the top of the control</summary>
  63. public bool HelpVisible
  64. {
  65. get { return (bool)GetValue(HelpVisibleProperty); }
  66. set { SetValue(HelpVisibleProperty, value); }
  67. }
  68. /// <summary>Shows the tolbar on the top of the control</summary>
  69. public bool ToolbarVisible
  70. {
  71. get { return (bool)GetValue(ToolbarVisibleProperty); }
  72. set { SetValue(ToolbarVisibleProperty, value); }
  73. }
  74. public PropertySort PropertySort
  75. {
  76. get { return (PropertySort)GetValue(PropertySortProperty); }
  77. set { SetValue(PropertySortProperty, value); }
  78. }
  79. #endregion
  80. #region Dependency properties registration
  81. public static readonly DependencyProperty SelectedObjectProperty =
  82. DependencyProperty.Register("SelectedObject", typeof(object), typeof(WpfPropertyGrid),
  83. new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedObjectPropertyChanged));
  84. public static readonly DependencyProperty SelectedObjectsProperty =
  85. DependencyProperty.Register("SelectedObjects", typeof(object[]), typeof(WpfPropertyGrid),
  86. new FrameworkPropertyMetadata(new object[0], FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedObjectsPropertyChanged, CoerceSelectedObjects));
  87. public static readonly DependencyProperty HelpVisibleProperty =
  88. DependencyProperty.Register("HelpVisible", typeof(bool), typeof(WpfPropertyGrid),
  89. new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, HelpVisiblePropertyChanged));
  90. public static readonly DependencyProperty ToolbarVisibleProperty =
  91. DependencyProperty.Register("ToolbarVisible", typeof(bool), typeof(WpfPropertyGrid),
  92. new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ToolbarVisiblePropertyChanged));
  93. public static readonly DependencyProperty PropertySortProperty =
  94. DependencyProperty.Register("PropertySort", typeof(PropertySort), typeof(WpfPropertyGrid),
  95. new FrameworkPropertyMetadata(PropertySort.CategorizedAlphabetical, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PropertySortPropertyChanged));
  96. #endregion
  97. #region Dependency properties events
  98. private static object CoerceSelectedObject(DependencyObject d, object value)
  99. {
  100. WpfPropertyGrid pg = d as WpfPropertyGrid;
  101. object[] collection = pg.GetValue(SelectedObjectsProperty) as object[];
  102. return collection.Length == 0 ? null : value;
  103. }
  104. private static object CoerceSelectedObjects(DependencyObject d, object value)
  105. {
  106. WpfPropertyGrid pg = d as WpfPropertyGrid;
  107. object single = pg.GetValue(SelectedObjectsProperty);
  108. return single == null ? new object[0] : value;
  109. }
  110. private static void SelectedObjectPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
  111. {
  112. WpfPropertyGrid pg = source as WpfPropertyGrid;
  113. pg.CoerceValue(SelectedObjectsProperty);
  114. if (e.NewValue == null)
  115. {
  116. pg.OnSelectionChangedMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { null });
  117. pg.SelectionTypeLabel.Text = string.Empty;
  118. }
  119. else
  120. {
  121. var context = new EditingContext();
  122. var mtm = new ModelTreeManager(context);
  123. mtm.Load(e.NewValue);
  124. Selection selection = Selection.Select(context, mtm.Root);
  125. pg.OnSelectionChangedMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { selection });
  126. pg.SelectionTypeLabel.Text = e.NewValue.GetType().Name;
  127. }
  128. pg.ChangeHelpText(string.Empty, string.Empty);
  129. }
  130. private static void SelectedObjectsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
  131. {
  132. WpfPropertyGrid pg = source as WpfPropertyGrid;
  133. pg.CoerceValue(SelectedObjectsProperty);
  134. object[] collection = e.NewValue as object[];
  135. if (collection.Length == 0)
  136. {
  137. pg.OnSelectionChangedMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { null });
  138. pg.SelectionTypeLabel.Text = string.Empty;
  139. }
  140. else
  141. {
  142. bool same = true;
  143. Type first = null;
  144. var context = new EditingContext();
  145. var mtm = new ModelTreeManager(context);
  146. Selection selection = null;
  147. // Accumulates the selection and determines the type to be shown in the top of the PG
  148. for (int i = 0; i < collection.Length; i++)
  149. {
  150. mtm.Load(collection[i]);
  151. if (i == 0)
  152. {
  153. selection = Selection.Select(context, mtm.Root);
  154. first = collection[0].GetType();
  155. }
  156. else
  157. {
  158. selection = Selection.Union(context, mtm.Root);
  159. if (!collection[i].GetType().Equals(first))
  160. same = false;
  161. }
  162. }
  163. pg.OnSelectionChangedMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { selection });
  164. pg.SelectionTypeLabel.Text = same ? first.Name + " <multiple>" : "Object <multiple>";
  165. }
  166. pg.ChangeHelpText(string.Empty, string.Empty);
  167. }
  168. private static void HelpVisiblePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
  169. {
  170. WpfPropertyGrid pg = source as WpfPropertyGrid;
  171. if (e.NewValue != e.OldValue)
  172. {
  173. if (e.NewValue.Equals(true))
  174. {
  175. pg.RowDefinitions[1].Height = new GridLength(5);
  176. pg.RowDefinitions[2].Height = new GridLength(pg.HelpTextHeight);
  177. }
  178. else
  179. {
  180. pg.HelpTextHeight = pg.RowDefinitions[2].Height.Value;
  181. pg.RowDefinitions[1].Height = new GridLength(0);
  182. pg.RowDefinitions[2].Height = new GridLength(0);
  183. }
  184. }
  185. }
  186. private static void ToolbarVisiblePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
  187. {
  188. WpfPropertyGrid pg = source as WpfPropertyGrid;
  189. pg.PropertyToolBar.Visibility = e.NewValue.Equals(true) ? Visibility.Visible : Visibility.Collapsed;
  190. }
  191. private static void PropertySortPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
  192. {
  193. WpfPropertyGrid pg = source as WpfPropertyGrid;
  194. PropertySort sort = (PropertySort)e.NewValue;
  195. bool isAlpha = (sort == PropertySort.Alphabetical || sort == PropertySort.NoSort);
  196. pg.IsInAlphaViewMethod.Invoke(pg.Designer.PropertyInspectorView, new object[] { isAlpha });
  197. }
  198. #endregion
  199. /// <summary>Default constructor, creates the UIElements including a PropertyInspector</summary>
  200. public WpfPropertyGrid()
  201. {
  202. this.ColumnDefinitions.Add(new ColumnDefinition());
  203. this.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
  204. this.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0) });
  205. this.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(0) });
  206. this.Designer = new WorkflowDesigner();
  207. TextBlock title = new TextBlock()
  208. {
  209. Visibility = Windows.Visibility.Visible,
  210. TextWrapping = TextWrapping.NoWrap,
  211. TextTrimming = TextTrimming.CharacterEllipsis,
  212. FontWeight = FontWeights.Bold
  213. };
  214. TextBlock descrip = new TextBlock()
  215. {
  216. Visibility = Windows.Visibility.Visible,
  217. TextWrapping = TextWrapping.Wrap,
  218. TextTrimming = TextTrimming.CharacterEllipsis
  219. };
  220. DockPanel dock = new DockPanel()
  221. {
  222. Visibility = Windows.Visibility.Visible,
  223. LastChildFill = true,
  224. Margin = new Thickness(3,0,3,0)
  225. };
  226. title.SetValue(DockPanel.DockProperty, Dock.Top);
  227. dock.Children.Add(title);
  228. dock.Children.Add(descrip);
  229. this.HelpText = new Border()
  230. {
  231. Visibility = Windows.Visibility.Visible,
  232. BorderBrush = SystemColors.ActiveBorderBrush,
  233. Background = SystemColors.ControlBrush,
  234. BorderThickness = new Thickness(1),
  235. Child = dock
  236. };
  237. this.Splitter = new GridSplitter()
  238. {
  239. Visibility = Windows.Visibility.Visible,
  240. ResizeDirection = GridResizeDirection.Rows,
  241. Height=5,
  242. HorizontalAlignment= Windows.HorizontalAlignment.Stretch
  243. };
  244. var inspector = Designer.PropertyInspectorView;
  245. inspector.Visibility = Visibility.Visible;
  246. inspector.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Stretch);
  247. this.Splitter.SetValue(Grid.RowProperty, 1);
  248. this.Splitter.SetValue(Grid.ColumnProperty, 0);
  249. this.HelpText.SetValue(Grid.RowProperty, 2);
  250. this.HelpText.SetValue(Grid.ColumnProperty, 0);
  251. Binding binding = new Binding("Parent.Background");
  252. title.SetBinding(BackgroundProperty, binding);
  253. descrip.SetBinding(BackgroundProperty, binding);
  254. this.Children.Add(inspector);
  255. this.Children.Add(this.Splitter);
  256. this.Children.Add(this.HelpText);
  257. Type inspectorType = inspector.GetType();
  258. var props = inspectorType.GetProperties(Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance |
  259. Reflection.BindingFlags.DeclaredOnly);
  260. var methods = inspectorType.GetMethods(Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance |
  261. Reflection.BindingFlags.DeclaredOnly);
  262. this.RefreshMethod = inspectorType.GetMethod("RefreshPropertyList",
  263. Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance | Reflection.BindingFlags.DeclaredOnly);
  264. this.IsInAlphaViewMethod = inspectorType.GetMethod("set_IsInAlphaView",
  265. Reflection.BindingFlags.Public | Reflection.BindingFlags.Instance | Reflection.BindingFlags.DeclaredOnly);
  266. this.OnSelectionChangedMethod = inspectorType.GetMethod("OnSelectionChanged",
  267. Reflection.BindingFlags.Public | Reflection.BindingFlags.Instance | Reflection.BindingFlags.DeclaredOnly);
  268. this.SelectionTypeLabel = inspectorType.GetMethod("get_SelectionTypeLabel",
  269. Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance |
  270. Reflection.BindingFlags.DeclaredOnly).Invoke(inspector, new object[0]) as TextBlock;
  271. this.PropertyToolBar = inspectorType.GetMethod("get_PropertyToolBar",
  272. Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance |
  273. Reflection.BindingFlags.DeclaredOnly).Invoke(inspector, new object[0]) as Control;
  274. inspectorType.GetEvent("GotFocus").AddEventHandler(this,
  275. Delegate.CreateDelegate(typeof(RoutedEventHandler), this, "GotFocusHandler", false));
  276. this.SelectionTypeLabel.Text = string.Empty;
  277. }
  278. /// <summary>Updates the PropertyGrid's properties</summary>
  279. public void RefreshPropertyList()
  280. {
  281. RefreshMethod.Invoke(Designer.PropertyInspectorView, new object[] { false });
  282. }
  283. /// <summary>Traps the change of focused property and updates the help text</summary>
  284. /// <param name="sender">Not used</param>
  285. /// <param name="args">Points to the source control containing the selected property</param>
  286. private void GotFocusHandler(object sender, RoutedEventArgs args)
  287. {
  288. //if (args.OriginalSource is TextBlock)
  289. //{
  290. string title = string.Empty;
  291. string descrip = string.Empty;
  292. var theSelectedObjects = this.GetValue(SelectedObjectsProperty) as object[];
  293. if (theSelectedObjects != null && theSelectedObjects.Length > 0)
  294. {
  295. Type first = theSelectedObjects[0].GetType();
  296. for (int i = 1; i < theSelectedObjects.Length; i++)
  297. {
  298. if (!theSelectedObjects[i].GetType().Equals(first))
  299. {
  300. ChangeHelpText(title, descrip);
  301. return;
  302. }
  303. }
  304. object data = (args.OriginalSource as FrameworkElement).DataContext;
  305. PropertyInfo propEntry = data.GetType().GetProperty("PropertyEntry");
  306. if (propEntry == null)
  307. {
  308. propEntry = data.GetType().GetProperty("ParentProperty");
  309. }
  310. if (propEntry != null)
  311. {
  312. object propEntryValue = propEntry.GetValue(data, null);
  313. string propName = propEntryValue.GetType().GetProperty("PropertyName").GetValue(propEntryValue, null) as string;
  314. title = propEntryValue.GetType().GetProperty("DisplayName").GetValue(propEntryValue, null) as string;
  315. PropertyInfo property = theSelectedObjects[0].GetType().GetProperty(propName);
  316. object[] attrs = property.GetCustomAttributes(typeof(DescriptionAttribute), true);
  317. if (attrs != null && attrs.Length > 0)
  318. descrip = (attrs[0] as DescriptionAttribute).Description;
  319. }
  320. ChangeHelpText(title, descrip);
  321. }
  322. //}
  323. }
  324. /// <summary>Changes the text help area contents</summary>
  325. /// <param name="title">Title in bold</param>
  326. /// <param name="descrip">Description with ellipsis</param>
  327. private void ChangeHelpText(string title, string descrip)
  328. {
  329. DockPanel dock = this.HelpText.Child as DockPanel;
  330. (dock.Children[0] as TextBlock).Text = title;
  331. (dock.Children[1] as TextBlock).Text = descrip;
  332. }
  333. }
  334. }