PropertyGrid.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using System;
  2. using System.ComponentModel;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Data;
  7. using System.Windows.Input;
  8. using HandyControl.Data;
  9. using HandyControl.Interactivity;
  10. using HandyControl.Tools.Extension;
  11. namespace HandyControl.Controls;
  12. [TemplatePart(Name = ElementItemsControl, Type = typeof(ItemsControl))]
  13. [TemplatePart(Name = ElementSearchBar, Type = typeof(SearchBar))]
  14. public class PropertyGrid : Control
  15. {
  16. private const string ElementItemsControl = "PART_ItemsControl";
  17. private const string ElementSearchBar = "PART_SearchBar";
  18. private ItemsControl _itemsControl;
  19. private ICollectionView _dataView;
  20. private SearchBar _searchBar;
  21. private string _searchKey;
  22. public PropertyGrid()
  23. {
  24. CommandBindings.Add(new CommandBinding(ControlCommands.SortByCategory, SortByCategory, (s, e) => e.CanExecute = ShowSortButton));
  25. CommandBindings.Add(new CommandBinding(ControlCommands.SortByName, SortByName, (s, e) => e.CanExecute = ShowSortButton));
  26. }
  27. public virtual PropertyResolver PropertyResolver { get; } = new();
  28. public static readonly RoutedEvent SelectedObjectChangedEvent =
  29. EventManager.RegisterRoutedEvent("SelectedObjectChanged", RoutingStrategy.Bubble,
  30. typeof(RoutedPropertyChangedEventHandler<object>), typeof(PropertyGrid));
  31. public event RoutedPropertyChangedEventHandler<object> SelectedObjectChanged
  32. {
  33. add => AddHandler(SelectedObjectChangedEvent, value);
  34. remove => RemoveHandler(SelectedObjectChangedEvent, value);
  35. }
  36. public static readonly DependencyProperty SelectedObjectProperty = DependencyProperty.Register(
  37. nameof(SelectedObject), typeof(object), typeof(PropertyGrid), new PropertyMetadata(default, OnSelectedObjectChanged));
  38. private static void OnSelectedObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  39. {
  40. var ctl = (PropertyGrid) d;
  41. ctl.OnSelectedObjectChanged(e.OldValue, e.NewValue);
  42. }
  43. public object SelectedObject
  44. {
  45. get => GetValue(SelectedObjectProperty);
  46. set => SetValue(SelectedObjectProperty, value);
  47. }
  48. protected virtual void OnSelectedObjectChanged(object oldValue, object newValue)
  49. {
  50. UpdateItems(newValue);
  51. RaiseEvent(new RoutedPropertyChangedEventArgs<object>(oldValue, newValue, SelectedObjectChangedEvent));
  52. }
  53. public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
  54. nameof(Description), typeof(string), typeof(PropertyGrid), new PropertyMetadata(default(string)));
  55. public string Description
  56. {
  57. get => (string) GetValue(DescriptionProperty);
  58. set => SetValue(DescriptionProperty, value);
  59. }
  60. public static readonly DependencyProperty MaxTitleWidthProperty = DependencyProperty.Register(
  61. nameof(MaxTitleWidth), typeof(double), typeof(PropertyGrid), new PropertyMetadata(ValueBoxes.Double0Box));
  62. public double MaxTitleWidth
  63. {
  64. get => (double) GetValue(MaxTitleWidthProperty);
  65. set => SetValue(MaxTitleWidthProperty, value);
  66. }
  67. public static readonly DependencyProperty MinTitleWidthProperty = DependencyProperty.Register(
  68. nameof(MinTitleWidth), typeof(double), typeof(PropertyGrid), new PropertyMetadata(ValueBoxes.Double0Box));
  69. public double MinTitleWidth
  70. {
  71. get => (double) GetValue(MinTitleWidthProperty);
  72. set => SetValue(MinTitleWidthProperty, value);
  73. }
  74. public static readonly DependencyProperty ShowSortButtonProperty = DependencyProperty.Register(
  75. nameof(ShowSortButton), typeof(bool), typeof(PropertyGrid), new PropertyMetadata(ValueBoxes.TrueBox));
  76. public bool ShowSortButton
  77. {
  78. get => (bool) GetValue(ShowSortButtonProperty);
  79. set => SetValue(ShowSortButtonProperty, ValueBoxes.BooleanBox(value));
  80. }
  81. public override void OnApplyTemplate()
  82. {
  83. if (_searchBar != null)
  84. {
  85. _searchBar.SearchStarted -= SearchBar_SearchStarted;
  86. }
  87. base.OnApplyTemplate();
  88. _itemsControl = GetTemplateChild(ElementItemsControl) as ItemsControl;
  89. _searchBar = GetTemplateChild(ElementSearchBar) as SearchBar;
  90. if (_searchBar != null)
  91. {
  92. _searchBar.SearchStarted += SearchBar_SearchStarted;
  93. }
  94. UpdateItems(SelectedObject);
  95. }
  96. private void UpdateItems(object obj)
  97. {
  98. if (obj == null || _itemsControl == null) return;
  99. _dataView = CollectionViewSource.GetDefaultView(TypeDescriptor.GetProperties(obj.GetType()).OfType<PropertyDescriptor>()
  100. .Where(item => PropertyResolver.ResolveIsBrowsable(item)).Select(CreatePropertyItem)
  101. .Do(item => item.InitElement()));
  102. SortByCategory(null, null);
  103. _itemsControl.ItemsSource = _dataView;
  104. }
  105. private void SortByCategory(object sender, ExecutedRoutedEventArgs e)
  106. {
  107. if (_dataView == null) return;
  108. using (_dataView.DeferRefresh())
  109. {
  110. _dataView.GroupDescriptions.Clear();
  111. _dataView.SortDescriptions.Clear();
  112. _dataView.SortDescriptions.Add(new SortDescription(PropertyItem.CategoryProperty.Name, ListSortDirection.Ascending));
  113. _dataView.SortDescriptions.Add(new SortDescription(PropertyItem.DisplayNameProperty.Name, ListSortDirection.Ascending));
  114. _dataView.GroupDescriptions.Add(new PropertyGroupDescription(PropertyItem.CategoryProperty.Name));
  115. }
  116. }
  117. private void SortByName(object sender, ExecutedRoutedEventArgs e)
  118. {
  119. if (_dataView == null) return;
  120. using (_dataView.DeferRefresh())
  121. {
  122. _dataView.GroupDescriptions.Clear();
  123. _dataView.SortDescriptions.Clear();
  124. _dataView.SortDescriptions.Add(new SortDescription(PropertyItem.PropertyNameProperty.Name, ListSortDirection.Ascending));
  125. }
  126. }
  127. private void SearchBar_SearchStarted(object sender, FunctionEventArgs<string> e)
  128. {
  129. if (_dataView == null) return;
  130. _searchKey = e.Info;
  131. if (string.IsNullOrEmpty(_searchKey))
  132. {
  133. foreach (UIElement item in _dataView)
  134. {
  135. item.Show();
  136. }
  137. }
  138. else
  139. {
  140. foreach (PropertyItem item in _dataView)
  141. {
  142. item.Show(item.PropertyName.ToLower().Contains(_searchKey) || item.DisplayName.ToLower().Contains(_searchKey));
  143. }
  144. }
  145. }
  146. protected virtual PropertyItem CreatePropertyItem(PropertyDescriptor propertyDescriptor) => new()
  147. {
  148. Category = PropertyResolver.ResolveCategory(propertyDescriptor),
  149. DisplayName = PropertyResolver.ResolveDisplayName(propertyDescriptor),
  150. Description = PropertyResolver.ResolveDescription(propertyDescriptor),
  151. IsReadOnly = PropertyResolver.ResolveIsReadOnly(propertyDescriptor),
  152. DefaultValue = PropertyResolver.ResolveDefaultValue(propertyDescriptor),
  153. Editor = PropertyResolver.ResolveEditor(propertyDescriptor),
  154. Value = SelectedObject,
  155. PropertyName = propertyDescriptor.Name,
  156. PropertyType = propertyDescriptor.PropertyType,
  157. PropertyTypeName = $"{propertyDescriptor.PropertyType.Namespace}.{propertyDescriptor.PropertyType.Name}"
  158. };
  159. protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
  160. {
  161. base.OnRenderSizeChanged(sizeInfo);
  162. TitleElement.SetTitleWidth(this, new GridLength(Math.Max(MinTitleWidth, Math.Min(MaxTitleWidth, ActualWidth / 3))));
  163. }
  164. }