From 1be50e54fa1e70de21ac9e00267c39d7cbb0bdd8 Mon Sep 17 00:00:00 2001 From: SKharchi Date: Mon, 10 May 2021 08:49:23 +0200 Subject: [PATCH 01/17] Introduces flattening of types without a known type editor. Types which do not have a known type editor are browsed and flattened into the highest level as if their properties belong in the class that is currently handled by the PropertyGrid. Furthermore some flatting options are available to decide in which category to place the flattened properties and how to name that category. While the flattening is off by default to keep the default behavior, the demo has been extented to do apply flattening. --- .../Data/Model/PropertyGridDemoModel.cs | 4 + .../Controls/PropertyGridDemoCtl.xaml | 2 +- .../Controls/PropertyGrid/PropertyGrid.cs | 101 +++++++++++++++--- .../Controls/PropertyGrid/PropertyResolver.cs | 2 + .../Data/Enum/Flattening.cs | 14 +++ .../HandyControl_Shared.projitems | 1 + 6 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 src/Shared/HandyControl_Shared/Data/Enum/Flattening.cs diff --git a/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs b/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs index a6c48a915..70805a130 100644 --- a/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs +++ b/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs @@ -1,6 +1,7 @@ using System.ComponentModel; using System.Windows; using System.Windows.Media; +using HandyControl.Controls; namespace HandyControlDemo.Data { @@ -18,6 +19,9 @@ public class PropertyGridDemoModel [Category("Category1")] public Gender Enum { get; set; } + [Category("Category2")] + public DemoDataModel FlattenedType { get; set; } + public HorizontalAlignment HorizontalAlignment { get; set; } public VerticalAlignment VerticalAlignment { get; set; } diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml index f803c23e4..baec242fd 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml @@ -10,7 +10,7 @@ - + diff --git a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyGrid.cs b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyGrid.cs index 3888b91cb..0160de723 100644 --- a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyGrid.cs +++ b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyGrid.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows; @@ -6,6 +7,7 @@ using System.Windows.Data; using System.Windows.Input; using HandyControl.Data; +using HandyControl.Data.Enum; using HandyControl.Interactivity; using HandyControl.Tools.Extension; @@ -102,6 +104,15 @@ public bool ShowSortButton set => SetValue(ShowSortButtonProperty, value); } + public static readonly DependencyProperty FlattenChildPropertiesProperty = DependencyProperty.Register( + "FlattenChildProperties", typeof(Flattening), typeof(PropertyGrid), new PropertyMetadata(Flattening.Off)); + + public Flattening FlattenChildProperties + { + get => (Flattening) GetValue(FlattenChildPropertiesProperty); + set => SetValue(FlattenChildPropertiesProperty, value); + } + public override void OnApplyTemplate() { if (_searchBar != null) @@ -122,15 +133,72 @@ public override void OnApplyTemplate() UpdateItems(SelectedObject); } + /// + /// Algorithmic helper class to temporarily link parent data to a PropertyDescriptorCollection + /// + private class ParentPropertyDescriptorCollection + { + public ParentPropertyDescriptorCollection(PropertyDescriptorCollection properties, string category) + { + Properties = properties; + Category = category; + } + + public PropertyDescriptorCollection Properties { get; } + public string Category { get; } + } + + private IEnumerable FlattenUnknownProperties(PropertyDescriptorCollection propertiesToFlatten, string parentCategory) + { + var browsableProperties = propertiesToFlatten.OfType() + .Where(item => PropertyResolver.ResolveIsBrowsable(item)).ToList(); + + var knownProperties = browsableProperties.Where(item => PropertyResolver.IsKnownEditorType(item.PropertyType)) + .Select(item => CreatePropertyItem(item, parentCategory)) + .Do(item => item.InitElement()); + + var unknownPropertiesCollections = browsableProperties.Where(item => !PropertyResolver.IsKnownEditorType(item.PropertyType)) + .Select(GetCategorizedChildProperties); + + return unknownPropertiesCollections + .Select(coll => FlattenUnknownProperties(coll.Properties, coll.Category)) + .Aggregate(knownProperties, (current, flattenedChildProperties) => current.Concat(flattenedChildProperties)); + } + + private ParentPropertyDescriptorCollection GetCategorizedChildProperties(PropertyDescriptor parentItem) + { + string category = null; + switch (FlattenChildProperties) + { + case Flattening.ParentCategory: + category = PropertyResolver.ResolveCategory(parentItem); + break; + case Flattening.ParentNameAsCategory: + category = parentItem.DisplayName; + break; + } + return new ParentPropertyDescriptorCollection(parentItem.GetChildProperties(), category); + } + private void UpdateItems(object obj) { if (obj == null || _itemsControl == null) return; - _dataView = CollectionViewSource.GetDefaultView(TypeDescriptor.GetProperties(obj.GetType()).OfType() - .Where(item => PropertyResolver.ResolveIsBrowsable(item)).Select(CreatePropertyItem) - .Do(item => item.InitElement())); + if (FlattenChildProperties == Flattening.Off) + { + _dataView = CollectionViewSource.GetDefaultView(TypeDescriptor.GetProperties(obj.GetType()) + .OfType() + .Where(item => PropertyResolver.ResolveIsBrowsable(item)) + .Select(item => CreatePropertyItem(item, null)) + .Do(item => item.InitElement())); + } + else + { + _dataView = CollectionViewSource.GetDefaultView(FlattenUnknownProperties(TypeDescriptor.GetProperties(obj.GetType()), null)); + } SortByCategory(null, null); + _itemsControl.ItemsSource = _dataView; } @@ -181,19 +249,20 @@ private void SearchBar_SearchStarted(object sender, FunctionEventArgs e) } } - protected virtual PropertyItem CreatePropertyItem(PropertyDescriptor propertyDescriptor) => new PropertyItem - { - Category = PropertyResolver.ResolveCategory(propertyDescriptor), - DisplayName = PropertyResolver.ResolveDisplayName(propertyDescriptor), - Description = PropertyResolver.ResolveDescription(propertyDescriptor), - IsReadOnly = PropertyResolver.ResolveIsReadOnly(propertyDescriptor), - DefaultValue = PropertyResolver.ResolveDefaultValue(propertyDescriptor), - Editor = PropertyResolver.ResolveEditor(propertyDescriptor), - Value = SelectedObject, - PropertyName = propertyDescriptor.Name, - PropertyType = propertyDescriptor.PropertyType, - PropertyTypeName = $"{propertyDescriptor.PropertyType.Namespace}.{propertyDescriptor.PropertyType.Name}" - }; + protected virtual PropertyItem CreatePropertyItem(PropertyDescriptor propertyDescriptor, string category) => + new PropertyItem + { + Category = category ?? PropertyResolver.ResolveCategory(propertyDescriptor), + DisplayName = PropertyResolver.ResolveDisplayName(propertyDescriptor), + Description = PropertyResolver.ResolveDescription(propertyDescriptor), + IsReadOnly = PropertyResolver.ResolveIsReadOnly(propertyDescriptor), + DefaultValue = PropertyResolver.ResolveDefaultValue(propertyDescriptor), + Editor = PropertyResolver.ResolveEditor(propertyDescriptor), + Value = SelectedObject, + PropertyName = propertyDescriptor.Name, + PropertyType = propertyDescriptor.PropertyType, + PropertyTypeName = $"{propertyDescriptor.PropertyType.Namespace}.{propertyDescriptor.PropertyType.Name}" + }; protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { diff --git a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyResolver.cs b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyResolver.cs index d28031fbb..6bd833443 100644 --- a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyResolver.cs +++ b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyResolver.cs @@ -104,6 +104,8 @@ public virtual PropertyEditorBase CreateDefaultEditor(Type type) => public virtual PropertyEditorBase CreateEditor(Type type) => new ReadOnlyTextPropertyEditor(); + public static bool IsKnownEditorType(Type type) => TypeCodeDic.ContainsKey(type) || type.IsSubclassOf(typeof(Enum)); + private enum EditorTypeCode { PlainText, diff --git a/src/Shared/HandyControl_Shared/Data/Enum/Flattening.cs b/src/Shared/HandyControl_Shared/Data/Enum/Flattening.cs new file mode 100644 index 000000000..1d9627a22 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/Enum/Flattening.cs @@ -0,0 +1,14 @@ +// ----------------------------- +// Copyright (c) XION GmbH +// ----------------------------- + +namespace HandyControl.Data.Enum +{ + public enum Flattening : byte + { + Off, + Uncategorized, + ParentCategory, + ParentNameAsCategory + } +} diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index e7b9e0523..00eb0c4e1 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -80,6 +80,7 @@ + From 628397713dbd1da80eace2659d9804ed7f5b974d Mon Sep 17 00:00:00 2001 From: SKharchi Date: Mon, 10 May 2021 08:54:59 +0200 Subject: [PATCH 02/17] removed unnecessary dependency --- .../HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs b/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs index 70805a130..d5a367c98 100644 --- a/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs +++ b/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs @@ -1,7 +1,6 @@ using System.ComponentModel; using System.Windows; using System.Windows.Media; -using HandyControl.Controls; namespace HandyControlDemo.Data { From b30c1c6eb75ad69be894624789993b2764d59f2d Mon Sep 17 00:00:00 2001 From: SKharchi Date: Mon, 10 May 2021 11:38:09 +0200 Subject: [PATCH 03/17] Hides the search bar element. Introduces a dependency property ShowSearchBar in order to hide the search bar. The search bar XAML mark up has been updated to adjust the visibility accordingly. Furthermore, multibinding is used to collapse the dockpanel visbility if the search bar and the sort buttons are both hidden. To support that the BooleanArr2VisibilityConverter has been extented to optionally return a visible state if any of the bindings are visible (instead of all). For that the ConverterParameter can be set to 'UseAny'. --- .../HandyControl_Net_40/Themes/Theme.xaml | 34 ++++++++++++------- .../Controls/PropertyGrid/PropertyGrid.cs | 9 +++++ .../Styles/Base/PropertyGridBaseStyle.xaml | 10 +++++- .../HandyControl_Shared/Themes/Theme.xaml | 8 ++++- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/Net_40/HandyControl_Net_40/Themes/Theme.xaml b/src/Net_40/HandyControl_Net_40/Themes/Theme.xaml index da65710e7..c0c2456e0 100644 --- a/src/Net_40/HandyControl_Net_40/Themes/Theme.xaml +++ b/src/Net_40/HandyControl_Net_40/Themes/Theme.xaml @@ -9677,18 +9677,26 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -13299,4 +13307,4 @@ + + + - From ff10175b9f18a35e5721900aeb36d044d27c18ae Mon Sep 17 00:00:00 2001 From: Samir Kharchi Date: Fri, 2 Jul 2021 13:17:57 +0200 Subject: [PATCH 14/17] revert unnecessary commit --- .../Controls/Attach/WindowAttach.cs | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/Shared/HandyControl_Shared/Controls/Attach/WindowAttach.cs b/src/Shared/HandyControl_Shared/Controls/Attach/WindowAttach.cs index 1f952075f..14441d401 100644 --- a/src/Shared/HandyControl_Shared/Controls/Attach/WindowAttach.cs +++ b/src/Shared/HandyControl_Shared/Controls/Attach/WindowAttach.cs @@ -145,39 +145,5 @@ public static void SetHideWhenClosing(DependencyObject element, bool value) public static bool GetHideWhenClosing(DependencyObject element) => (bool) element.GetValue(HideWhenClosingProperty); - - public static readonly DependencyProperty KeepCenterOnSizeChangedProperty = - DependencyProperty.RegisterAttached("KeepCenterOnSizeChanged", typeof(bool), typeof(WindowAttach), - new PropertyMetadata(ValueBoxes.FalseBox, KeepCenterOnSizeChangedPropertyChanged)); - - [AttachedPropertyBrowsableForType(typeof(System.Windows.Window))] - [AttachedPropertyBrowsableForType(typeof(Window))] - public static bool GetKeepCenterOnSizeChanged(DependencyObject obj) => (bool) obj.GetValue(KeepCenterOnSizeChangedProperty); - - public static void SetKeepCenterOnSizeChanged(DependencyObject obj, bool value) => obj.SetValue(KeepCenterOnSizeChangedProperty, value); - - private static void KeepCenterOnSizeChangedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is Window window) - { - if ((bool) e.NewValue) - window.SizeChanged += Window_SizeChanged; - else - window.SizeChanged -= Window_SizeChanged; - } - - static void Window_SizeChanged(object sender, SizeChangedEventArgs e) - { - var window = (Window) sender; - if (window.WindowStartupLocation == WindowStartupLocation.CenterOwner || - window.WindowStartupLocation == WindowStartupLocation.CenterScreen) - { - if (e.WidthChanged) - window.Left += (e.PreviousSize.Width - e.NewSize.Width) / 2; - if (e.HeightChanged) - window.Top += (e.PreviousSize.Height - e.NewSize.Height) / 2; - } - } - } } } From 8c580cd0af91d4fb0dad83f7fdf1136e448c597d Mon Sep 17 00:00:00 2001 From: Samir Kharchi Date: Fri, 2 Jul 2021 13:28:34 +0200 Subject: [PATCH 15/17] Revert wrong sync changes --- .../Editors/HorizontalAlignmentPropertyEditor.cs | 12 ++++++------ .../Editors/ReadOnlyTextPropertyEditor.cs | 2 +- .../PropertyGrid/Editors/SwitchPropertyEditor.cs | 2 +- .../Editors/VerticalAlignmentPropertyEditor.cs | 12 ++++++------ .../Controls/PropertyGrid/PropertyGrid.cs | 4 ++-- .../HandyControl_Shared.projitems | 5 +++++ 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/HorizontalAlignmentPropertyEditor.cs b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/HorizontalAlignmentPropertyEditor.cs index b532a1731..21adcf1a6 100644 --- a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/HorizontalAlignmentPropertyEditor.cs +++ b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/HorizontalAlignmentPropertyEditor.cs @@ -14,9 +14,9 @@ public class HorizontalAlignmentPropertyEditor : PropertyEditorBase { public override FrameworkElement CreateElement(PropertyItem propertyItem) => new System.Windows.Controls.ComboBox { - Style = ResourceHelper.GetResource + -