diff --git a/src/Net_40/HandyControl_Net_40/Themes/Theme.xaml b/src/Net_40/HandyControl_Net_40/Themes/Theme.xaml
index b6fc1f796..a788746c7 100644
--- a/src/Net_40/HandyControl_Net_40/Themes/Theme.xaml
+++ b/src/Net_40/HandyControl_Net_40/Themes/Theme.xaml
@@ -699,8 +699,16 @@
-
-
+
+
+
+
+
+
+
+
+
+
@@ -9975,18 +9983,29 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -13728,4 +13747,4 @@
-
\ No newline at end of file
+
diff --git a/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs b/src/Shared/HandyControlDemo_Shared/Data/Model/PropertyGridDemoModel.cs
index a6c48a915..841101241 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
{
@@ -12,12 +13,15 @@ public class PropertyGridDemoModel
[Category("Category2")]
public int Integer { get; set; }
- [Category("Category2")]
+ [Category("Category1"), Priority(3)]
public bool Boolean { get; set; }
- [Category("Category1")]
+ [Category("Category1"), Priority(5), Necessary]
public Gender Enum { get; set; }
+ [Category("Category1"), Priority(4)]
+ 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..f96e33015 100644
--- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml
@@ -10,7 +10,19 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml.cs b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml.cs
index 54d153223..c93d29e3a 100644
--- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml.cs
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/PropertyGridDemoCtl.xaml.cs
@@ -11,11 +11,12 @@ public PropertyGridDemoCtl()
DemoModel = new PropertyGridDemoModel
{
- String = "TestString",
- Enum = Gender.Female,
- Boolean = true,
- Integer = 98,
- VerticalAlignment = VerticalAlignment.Stretch
+ String = "TestString",
+ Enum = Gender.Female,
+ Boolean = true,
+ Integer = 98,
+ VerticalAlignment = VerticalAlignment.Stretch,
+ FlattenedType = new DemoDataModel {Index = 0, IsSelected = true, Name = "Flattened", Remark = "Remark", Type = DemoType.Type4}
};
}
diff --git a/src/Shared/HandyControl_Shared/Controls/Carousel/Carousel.cs b/src/Shared/HandyControl_Shared/Controls/Carousel/Carousel.cs
index b37a2e62e..d63eec5f9 100644
--- a/src/Shared/HandyControl_Shared/Controls/Carousel/Carousel.cs
+++ b/src/Shared/HandyControl_Shared/Controls/Carousel/Carousel.cs
@@ -268,9 +268,16 @@ public void UpdatePageButtons(int index = -1)
///
private void UpdateItemsPosition()
{
- if (!CheckNull()) return;
- if (!_appliedTemplate) return;
- if (Items.Count == 0) return;
+ if (!CheckNull() || !_appliedTemplate || Items.Count == 0)
+ {
+ return;
+ }
+
+ if (PageIndex == -1)
+ {
+ PageIndex = 0;
+ }
+
if (!IsCenter)
{
ItemsHost.BeginAnimation(MarginProperty,
diff --git a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/EnumPropertyEditor.cs b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/EnumPropertyEditor.cs
index 3187a0fdd..af380b43c 100644
--- a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/EnumPropertyEditor.cs
+++ b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/EnumPropertyEditor.cs
@@ -1,17 +1,66 @@
using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
using System.Windows;
using System.Windows.Controls.Primitives;
+using System.Windows.Data;
namespace HandyControl.Controls
{
public class EnumPropertyEditor : PropertyEditorBase
{
- public override FrameworkElement CreateElement(PropertyItem propertyItem) => new System.Windows.Controls.ComboBox
- {
- IsEnabled = !propertyItem.IsReadOnly,
- ItemsSource = Enum.GetValues(propertyItem.PropertyType)
- };
+ public override FrameworkElement CreateElement(PropertyItem propertyItem) =>
+ new System.Windows.Controls.ComboBox
+ {
+ IsEnabled = !propertyItem.IsReadOnly,
+ ItemsSource = EnumPropertyEditor.GetEnumDescription(propertyItem.PropertyType)
+ };
public override DependencyProperty GetDependencyProperty() => Selector.SelectedValueProperty;
+
+ protected override IValueConverter GetConverter(PropertyItem propertyItem) => new EnumDescriptionConverter(propertyItem.PropertyType);
+
+ private static IEnumerable GetEnumDescription(Type enumType)
+ {
+ var values = Enum.GetValues(enumType);
+ var enumDescs = new List(values.Length);
+
+ enumDescs.AddRange(from object value in values
+ select enumType.GetField(value.ToString())
+ into field
+ let attributes = field.GetCustomAttributes(typeof(DescriptionAttribute), false)
+ select (attributes.Length == 0 ? field.Name : ((DescriptionAttribute) attributes[0]).Description));
+
+ return enumDescs;
+ }
+
+ private class EnumDescriptionConverter : IValueConverter
+ {
+ private readonly List> cache;
+
+ public EnumDescriptionConverter(Type enumType)
+ {
+ var values = Enum.GetValues(enumType);
+
+ cache = new List>(values.Length);
+
+ foreach (var value in values)
+ {
+ var field = enumType.GetField(value.ToString());
+ var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute), false);
+ var fieldValue = (attributes.Length == 0 ? field.Name : ((DescriptionAttribute) attributes[0]).Description);
+
+ cache.Add(new KeyValuePair((Enum) value, fieldValue));
+ }
+ }
+
+ object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
+ cache.FirstOrDefault(x => x.Key.Equals((Enum) value)).Value;
+
+ object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
+ cache.FirstOrDefault(x => x.Value == (string) value).Key;
+ }
}
}
diff --git a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/PropertyEditorBase.cs b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/PropertyEditorBase.cs
index 9c2a64423..7d452be2f 100644
--- a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/PropertyEditorBase.cs
+++ b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/Editors/PropertyEditorBase.cs
@@ -1,21 +1,28 @@
-using System.Windows;
+using System;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Controls.Primitives;
using System.Windows.Data;
+using HandyControl.Controls;
namespace HandyControl.Controls
{
+
public abstract class PropertyEditorBase : DependencyObject
{
public abstract FrameworkElement CreateElement(PropertyItem propertyItem);
- public virtual void CreateBinding(PropertyItem propertyItem, DependencyObject element) =>
+ public virtual void CreateBinding(PropertyItem propertyItem, DependencyObject element)
+ {
BindingOperations.SetBinding(element, GetDependencyProperty(),
- new Binding($"{propertyItem.PropertyName}")
- {
- Source = propertyItem.Value,
- Mode = GetBindingMode(propertyItem),
- UpdateSourceTrigger = GetUpdateSourceTrigger(propertyItem),
- Converter = GetConverter(propertyItem)
- });
+ new Binding($"{propertyItem.PropertyName}")
+ {
+ Source = propertyItem.Value,
+ Mode = GetBindingMode(propertyItem),
+ UpdateSourceTrigger = GetUpdateSourceTrigger(propertyItem),
+ Converter = GetConverter(propertyItem)
+ });
+ }
public abstract DependencyProperty GetDependencyProperty();
diff --git a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyGrid.cs b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyGrid.cs
index 2d6c2e7d2..5d708e238 100644
--- a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyGrid.cs
+++ b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyGrid.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
@@ -6,6 +8,7 @@
using System.Windows.Data;
using System.Windows.Input;
using HandyControl.Data;
+using HandyControl.Data.Enum;
using HandyControl.Interactivity;
using HandyControl.Tools.Extension;
@@ -31,6 +34,7 @@ public PropertyGrid()
{
CommandBindings.Add(new CommandBinding(ControlCommands.SortByCategory, SortByCategory, (s, e) => e.CanExecute = ShowSortButton));
CommandBindings.Add(new CommandBinding(ControlCommands.SortByName, SortByName, (s, e) => e.CanExecute = ShowSortButton));
+ CommandBindings.Add(new CommandBinding(ControlCommands.SortByHierarchyLevel, SortByHierarchyLevel, (s, e) => e.CanExecute = ShowSortButton));
}
public virtual PropertyResolver PropertyResolver { get; } = new();
@@ -102,6 +106,61 @@ public bool ShowSortButton
set => SetValue(ShowSortButtonProperty, ValueBoxes.BooleanBox(value));
}
+ public static readonly DependencyProperty ShowSearchBarProperty = DependencyProperty.Register(
+ "ShowSearchBar", typeof(bool), typeof(PropertyGrid), new PropertyMetadata(ValueBoxes.TrueBox));
+
+ public bool ShowSearchBar
+ {
+ get => (bool) GetValue(ShowSearchBarProperty);
+ set => SetValue(ShowSearchBarProperty, 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 static readonly DependencyProperty DefaultSortingProperty = DependencyProperty.Register(
+ "DefaultSorting", typeof(SortingMode), typeof(PropertyGrid), new PropertyMetadata(SortingMode.Category));
+
+ public SortingMode DefaultSorting
+ {
+ get => (SortingMode) GetValue(DefaultSortingProperty);
+ set => SetValue(DefaultSortingProperty, value);
+ }
+
+ public static readonly DependencyProperty GroupHeaderTemplateProperty = DependencyProperty.Register(
+ nameof(GroupHeaderTemplate), typeof(DataTemplate), typeof(PropertyGrid), new PropertyMetadata(default(DataTemplate)));
+
+ public DataTemplate GroupHeaderTemplate
+ {
+ get => (DataTemplate) GetValue(GroupHeaderTemplateProperty);
+ set => SetValue(GroupHeaderTemplateProperty, value);
+ }
+
+ public static readonly DependencyProperty GroupHeaderMarginProperty = DependencyProperty.Register(
+ nameof(GroupHeaderMargin), typeof(Thickness), typeof(PropertyGrid),
+ new FrameworkPropertyMetadata(new Thickness(0, 0, 0, 6), FrameworkPropertyMetadataOptions.AffectsMeasure));
+
+ public Thickness GroupHeaderMargin
+ {
+ get => (Thickness) GetValue(GroupHeaderMarginProperty);
+ set => SetValue(GroupHeaderMarginProperty, value);
+ }
+
+ public static readonly DependencyProperty SortByPriorityProperty = DependencyProperty.Register(
+ "SortByPriority", typeof(bool), typeof(PropertyGrid), new PropertyMetadata(ValueBoxes.TrueBox));
+
+ public bool SortByPriority
+ {
+ get => (bool) GetValue(SortByPriorityProperty);
+ set => SetValue(SortByPriorityProperty, value);
+ }
+
public override void OnApplyTemplate()
{
if (_searchBar != null)
@@ -122,15 +181,87 @@ public override void OnApplyTemplate()
UpdateItems(SelectedObject);
}
+ ///
+ /// Algorithmic helper class to temporarily link parent data to a PropertyDescriptorCollection
+ ///
+ private class ParentPropertyDescriptorCollection
+ {
+ public ParentPropertyDescriptorCollection(PropertyDescriptorCollection properties, object parent, string category)
+ {
+ Properties = properties;
+ Category = category;
+ ParentComponent = parent;
+ }
+
+ public PropertyDescriptorCollection Properties { get; }
+ public string Category { get; }
+ public object ParentComponent { get; }
+ }
+
+ private IEnumerable FlattenUnknownProperties(ParentPropertyDescriptorCollection collection, int hierarchyLevel) =>
+ FlattenUnknownProperties(collection.Properties, collection.ParentComponent, collection.Category, hierarchyLevel);
+
+ private IEnumerable FlattenUnknownProperties(PropertyDescriptorCollection propertiesToFlatten, object component, string parentCategory, int hierarchyLevel = 1)
+ {
+ var browsableProperties = propertiesToFlatten.OfType()
+ .Where(item => PropertyResolver.ResolveIsBrowsable(item)).ToList();
+
+ var knownProperties = browsableProperties.Where(item => PropertyResolver.IsKnownEditorType(item.PropertyType) || PropertyResolver.HasEditorType(item))
+ .Select(item => CreatePropertyItem(item, component, parentCategory, hierarchyLevel))
+ .Do(item => item.InitElement());
+
+ var unknownPropertiesCollections = browsableProperties.Where(item => !PropertyResolver.IsKnownEditorType(item.PropertyType) && !PropertyResolver.HasEditorType(item))
+ .Select(item => GetCategorizedChildProperties(item, component));
+
+ return unknownPropertiesCollections.Select(coll => FlattenUnknownProperties(coll, hierarchyLevel + 1))
+ .Aggregate(knownProperties, (current, flattenedChildProperties) => current.Concat(flattenedChildProperties));
+ }
+
+ private ParentPropertyDescriptorCollection GetCategorizedChildProperties(PropertyDescriptor parentItem, object parentComponent)
+ {
+ 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(), parentItem.GetValue(parentComponent), 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, obj, null, 0))
+ .Do(item => item.InitElement()));
+ }
+ else
+ {
+ _dataView = CollectionViewSource.GetDefaultView(FlattenUnknownProperties(TypeDescriptor.GetProperties(obj.GetType()), obj, null));
+ }
+
+ switch (DefaultSorting)
+ {
+ case SortingMode.Name:
+ SortByName(null, null);
+ break;
+ case SortingMode.Hierarchy:
+ SortByHierarchyLevel(null, null);
+ break;
+ default:
+ SortByCategory(null, null);
+ break;
+ }
- SortByCategory(null, null);
_itemsControl.ItemsSource = _dataView;
}
@@ -142,6 +273,10 @@ private void SortByCategory(object sender, ExecutedRoutedEventArgs e)
{
_dataView.GroupDescriptions.Clear();
_dataView.SortDescriptions.Clear();
+ if (SortByPriority)
+ {
+ _dataView.SortDescriptions.Add(new SortDescription(PropertyItem.PriorityProperty.Name, ListSortDirection.Descending));
+ }
_dataView.SortDescriptions.Add(new SortDescription(PropertyItem.CategoryProperty.Name, ListSortDirection.Ascending));
_dataView.SortDescriptions.Add(new SortDescription(PropertyItem.DisplayNameProperty.Name, ListSortDirection.Ascending));
_dataView.GroupDescriptions.Add(new PropertyGroupDescription(PropertyItem.CategoryProperty.Name));
@@ -156,15 +291,36 @@ private void SortByName(object sender, ExecutedRoutedEventArgs e)
{
_dataView.GroupDescriptions.Clear();
_dataView.SortDescriptions.Clear();
+ if (SortByPriority)
+ {
+ _dataView.SortDescriptions.Add(new SortDescription(PropertyItem.PriorityProperty.Name, ListSortDirection.Descending));
+ }
_dataView.SortDescriptions.Add(new SortDescription(PropertyItem.PropertyNameProperty.Name, ListSortDirection.Ascending));
}
}
+ private void SortByHierarchyLevel(object sender, ExecutedRoutedEventArgs e)
+ {
+ if (_dataView == null) return;
+
+ using (_dataView.DeferRefresh())
+ {
+ _dataView.GroupDescriptions.Clear();
+ _dataView.SortDescriptions.Clear();
+ if (SortByPriority)
+ {
+ _dataView.SortDescriptions.Add(new SortDescription(PropertyItem.PriorityProperty.Name, ListSortDirection.Descending));
+ }
+ _dataView.SortDescriptions.Add(new SortDescription(PropertyItem.HierarchyLevelProperty.Name, ListSortDirection.Ascending));
+ _dataView.GroupDescriptions.Add(new PropertyGroupDescription(PropertyItem.CategoryProperty.Name));
+ }
+ }
+
private void SearchBar_SearchStarted(object sender, FunctionEventArgs e)
{
if (_dataView == null) return;
- _searchKey = e.Info;
+ _searchKey = e.Info.ToLower();
if (string.IsNullOrEmpty(_searchKey))
{
foreach (UIElement item in _dataView)
@@ -181,19 +337,23 @@ private void SearchBar_SearchStarted(object sender, FunctionEventArgs e)
}
}
- protected virtual PropertyItem CreatePropertyItem(PropertyDescriptor propertyDescriptor) => new()
- {
- 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, object component, string category, int hierarchyLevel) =>
+ 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),
+ HierarchyLevel = PropertyResolver.ResolveHierarchyLevel(propertyDescriptor) ?? hierarchyLevel,
+ Priority = PropertyResolver.ResolvePriority(propertyDescriptor),
+ IsNecessary = PropertyResolver.ResolveIsNecessary(propertyDescriptor),
+ Value = component,
+ 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/PropertyItem.cs b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyItem.cs
index b749992cf..ac7e65900 100644
--- a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyItem.cs
+++ b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyItem.cs
@@ -8,8 +8,8 @@ namespace HandyControl.Controls
{
public class PropertyItem : ListBoxItem
{
- public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
- "Value", typeof(object), typeof(PropertyItem), new PropertyMetadata(default(object)));
+ public static readonly DependencyProperty ValueProperty =
+ DependencyProperty.Register("Value", typeof(object), typeof(PropertyItem), new PropertyMetadata(default(object)));
public object Value
{
@@ -17,8 +17,8 @@ public object Value
set => SetValue(ValueProperty, value);
}
- public static readonly DependencyProperty DisplayNameProperty = DependencyProperty.Register(
- "DisplayName", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
+ public static readonly DependencyProperty DisplayNameProperty =
+ DependencyProperty.Register("DisplayName", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
public string DisplayName
{
@@ -26,8 +26,8 @@ public string DisplayName
set => SetValue(DisplayNameProperty, value);
}
- public static readonly DependencyProperty PropertyNameProperty = DependencyProperty.Register(
- "PropertyName", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
+ public static readonly DependencyProperty PropertyNameProperty =
+ DependencyProperty.Register("PropertyName", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
public string PropertyName
{
@@ -35,8 +35,8 @@ public string PropertyName
set => SetValue(PropertyNameProperty, value);
}
- public static readonly DependencyProperty PropertyTypeProperty = DependencyProperty.Register(
- "PropertyType", typeof(Type), typeof(PropertyItem), new PropertyMetadata(default(Type)));
+ public static readonly DependencyProperty PropertyTypeProperty =
+ DependencyProperty.Register("PropertyType", typeof(Type), typeof(PropertyItem), new PropertyMetadata(default(Type)));
public Type PropertyType
{
@@ -44,8 +44,8 @@ public Type PropertyType
set => SetValue(PropertyTypeProperty, value);
}
- public static readonly DependencyProperty PropertyTypeNameProperty = DependencyProperty.Register(
- "PropertyTypeName", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
+ public static readonly DependencyProperty PropertyTypeNameProperty =
+ DependencyProperty.Register("PropertyTypeName", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
public string PropertyTypeName
{
@@ -53,8 +53,8 @@ public string PropertyTypeName
set => SetValue(PropertyTypeNameProperty, value);
}
- public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
- "Description", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
+ public static readonly DependencyProperty DescriptionProperty =
+ DependencyProperty.Register("Description", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
public string Description
{
@@ -62,8 +62,8 @@ public string Description
set => SetValue(DescriptionProperty, value);
}
- public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(
- "IsReadOnly", typeof(bool), typeof(PropertyItem), new PropertyMetadata(ValueBoxes.FalseBox));
+ public static readonly DependencyProperty IsReadOnlyProperty =
+ DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(PropertyItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsReadOnly
{
@@ -71,8 +71,8 @@ public bool IsReadOnly
set => SetValue(IsReadOnlyProperty, ValueBoxes.BooleanBox(value));
}
- public static readonly DependencyProperty DefaultValueProperty = DependencyProperty.Register(
- "DefaultValue", typeof(object), typeof(PropertyItem), new PropertyMetadata(default(object)));
+ public static readonly DependencyProperty DefaultValueProperty =
+ DependencyProperty.Register("DefaultValue", typeof(object), typeof(PropertyItem), new PropertyMetadata(default(object)));
public object DefaultValue
{
@@ -80,8 +80,8 @@ public object DefaultValue
set => SetValue(DefaultValueProperty, value);
}
- public static readonly DependencyProperty CategoryProperty = DependencyProperty.Register(
- "Category", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
+ public static readonly DependencyProperty CategoryProperty =
+ DependencyProperty.Register("Category", typeof(string), typeof(PropertyItem), new PropertyMetadata(default(string)));
public string Category
{
@@ -89,8 +89,9 @@ public string Category
set => SetValue(CategoryProperty, value);
}
- public static readonly DependencyProperty EditorProperty = DependencyProperty.Register(
- "Editor", typeof(PropertyEditorBase), typeof(PropertyItem), new PropertyMetadata(default(PropertyEditorBase)));
+ public static readonly DependencyProperty EditorProperty =
+ DependencyProperty.Register("Editor", typeof(PropertyEditorBase), typeof(PropertyItem),
+ new PropertyMetadata(default(PropertyEditorBase)));
public PropertyEditorBase Editor
{
@@ -98,8 +99,9 @@ public PropertyEditorBase Editor
set => SetValue(EditorProperty, value);
}
- public static readonly DependencyProperty EditorElementProperty = DependencyProperty.Register(
- "EditorElement", typeof(FrameworkElement), typeof(PropertyItem), new PropertyMetadata(default(FrameworkElement)));
+ public static readonly DependencyProperty EditorElementProperty =
+ DependencyProperty.Register("EditorElement", typeof(FrameworkElement), typeof(PropertyItem),
+ new PropertyMetadata(default(FrameworkElement)));
public FrameworkElement EditorElement
{
@@ -107,8 +109,8 @@ public FrameworkElement EditorElement
set => SetValue(EditorElementProperty, value);
}
- public static readonly DependencyProperty IsExpandedEnabledProperty = DependencyProperty.Register(
- "IsExpandedEnabled", typeof(bool), typeof(PropertyItem), new PropertyMetadata(ValueBoxes.FalseBox));
+ public static readonly DependencyProperty IsExpandedEnabledProperty =
+ DependencyProperty.Register("IsExpandedEnabled", typeof(bool), typeof(PropertyItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsExpandedEnabled
{
@@ -116,6 +118,33 @@ public bool IsExpandedEnabled
set => SetValue(IsExpandedEnabledProperty, ValueBoxes.BooleanBox(value));
}
+ public static readonly DependencyProperty HierarchyLevelProperty =
+ DependencyProperty.Register("HierarchyLevel", typeof(int?), typeof(PropertyItem), new PropertyMetadata(default(int?)));
+
+ public int? HierarchyLevel
+ {
+ get => (int?) GetValue(HierarchyLevelProperty);
+ set => SetValue(HierarchyLevelProperty, value);
+ }
+
+ public static readonly DependencyProperty PriorityProperty =
+ DependencyProperty.Register("Priority", typeof(int), typeof(PropertyItem), new PropertyMetadata(default(int)));
+
+ public int Priority
+ {
+ get => (int) GetValue(PriorityProperty);
+ set => SetValue(PriorityProperty, value);
+ }
+
+ public static readonly DependencyProperty IsNecessaryProperty =
+ DependencyProperty.Register("IsNecessary", typeof(bool), typeof(PropertyItem), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ public bool IsNecessary
+ {
+ get => (bool) GetValue(IsNecessaryProperty);
+ set => SetValue(IsNecessaryProperty, ValueBoxes.BooleanBox(value));
+ }
+
public PropertyDescriptor PropertyDescriptor { get; set; }
public virtual void InitElement()
@@ -123,6 +152,15 @@ public virtual void InitElement()
if (Editor == null) return;
EditorElement = Editor.CreateElement(this);
Editor.CreateBinding(this, EditorElement);
+
+ if (!IsNecessary)
+ {
+ return;
+ }
+
+ InfoElement.SetNecessary(this, true);
+ TitleElement.SetTitle(EditorElement, DisplayName);
+ TitleElement.SetTitlePlacement(EditorElement, TitlePlacementType.Left);
}
}
}
diff --git a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyResolver.cs b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyResolver.cs
index 3ce20d4e1..973d01999 100644
--- a/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyResolver.cs
+++ b/src/Shared/HandyControl_Shared/Controls/PropertyGrid/PropertyResolver.cs
@@ -12,33 +12,51 @@ public class PropertyResolver
{
private static readonly Dictionary TypeCodeDic = new()
{
- [typeof(string)] = EditorTypeCode.PlainText,
- [typeof(sbyte)] = EditorTypeCode.SByteNumber,
- [typeof(byte)] = EditorTypeCode.ByteNumber,
- [typeof(short)] = EditorTypeCode.Int16Number,
- [typeof(ushort)] = EditorTypeCode.UInt16Number,
- [typeof(int)] = EditorTypeCode.Int32Number,
- [typeof(uint)] = EditorTypeCode.UInt32Number,
- [typeof(long)] = EditorTypeCode.Int64Number,
- [typeof(ulong)] = EditorTypeCode.UInt64Number,
- [typeof(float)] = EditorTypeCode.SingleNumber,
- [typeof(double)] = EditorTypeCode.DoubleNumber,
- [typeof(bool)] = EditorTypeCode.Switch,
- [typeof(DateTime)] = EditorTypeCode.DateTime,
+ [typeof(string)] = EditorTypeCode.PlainText,
+ [typeof(sbyte)] = EditorTypeCode.SByteNumber,
+ [typeof(byte)] = EditorTypeCode.ByteNumber,
+ [typeof(short)] = EditorTypeCode.Int16Number,
+ [typeof(ushort)] = EditorTypeCode.UInt16Number,
+ [typeof(int)] = EditorTypeCode.Int32Number,
+ [typeof(uint)] = EditorTypeCode.UInt32Number,
+ [typeof(long)] = EditorTypeCode.Int64Number,
+ [typeof(ulong)] = EditorTypeCode.UInt64Number,
+ [typeof(float)] = EditorTypeCode.SingleNumber,
+ [typeof(double)] = EditorTypeCode.DoubleNumber,
+ [typeof(bool)] = EditorTypeCode.Switch,
+ [typeof(DateTime)] = EditorTypeCode.DateTime,
[typeof(HorizontalAlignment)] = EditorTypeCode.HorizontalAlignment,
- [typeof(VerticalAlignment)] = EditorTypeCode.VerticalAlignment,
- [typeof(ImageSource)] = EditorTypeCode.ImageSource
+ [typeof(VerticalAlignment)] = EditorTypeCode.VerticalAlignment,
+ [typeof(ImageSource)] = EditorTypeCode.ImageSource
};
public string ResolveCategory(PropertyDescriptor propertyDescriptor)
{
var categoryAttribute = propertyDescriptor.Attributes.OfType().FirstOrDefault();
- return categoryAttribute == null ?
- Lang.Miscellaneous :
- string.IsNullOrEmpty(categoryAttribute.Category) ?
- Lang.Miscellaneous :
- categoryAttribute.Category;
+ if (categoryAttribute == null || string.IsNullOrEmpty(categoryAttribute.Category))
+ {
+ return Lang.Miscellaneous;
+ }
+ return categoryAttribute.Category;
+ }
+
+ public int? ResolveHierarchyLevel(PropertyDescriptor propertyDescriptor)
+ {
+ var hierarchyLevelAttribute = propertyDescriptor.Attributes.OfType().FirstOrDefault();
+ return hierarchyLevelAttribute?.Value;
+ }
+
+ public int ResolvePriority(PropertyDescriptor propertyDescriptor)
+ {
+ var priorityAttribute = propertyDescriptor.Attributes.OfType().FirstOrDefault();
+ return priorityAttribute?.Value ?? 0;
+ }
+
+ public bool ResolveIsNecessary(PropertyDescriptor propertyDescriptor)
+ {
+ var isRequiredAttribute = propertyDescriptor.Attributes.OfType().FirstOrDefault();
+ return isRequiredAttribute is not null;
}
public string ResolveDisplayName(PropertyDescriptor propertyDescriptor)
@@ -76,33 +94,60 @@ public PropertyEditorBase ResolveEditor(PropertyDescriptor propertyDescriptor)
return editor;
}
- public virtual PropertyEditorBase CreateDefaultEditor(Type type) =>
- TypeCodeDic.TryGetValue(type, out var editorType)
- ? editorType switch
- {
- EditorTypeCode.PlainText => new PlainTextPropertyEditor(),
- EditorTypeCode.SByteNumber => new NumberPropertyEditor(sbyte.MinValue, sbyte.MaxValue),
- EditorTypeCode.ByteNumber => new NumberPropertyEditor(byte.MinValue, byte.MaxValue),
- EditorTypeCode.Int16Number => new NumberPropertyEditor(short.MinValue, short.MaxValue),
- EditorTypeCode.UInt16Number => new NumberPropertyEditor(ushort.MinValue, ushort.MaxValue),
- EditorTypeCode.Int32Number => new NumberPropertyEditor(int.MinValue, int.MaxValue),
- EditorTypeCode.UInt32Number => new NumberPropertyEditor(uint.MinValue, uint.MaxValue),
- EditorTypeCode.Int64Number => new NumberPropertyEditor(long.MinValue, long.MaxValue),
- EditorTypeCode.UInt64Number => new NumberPropertyEditor(ulong.MinValue, ulong.MaxValue),
- EditorTypeCode.SingleNumber => new NumberPropertyEditor(float.MinValue, float.MaxValue),
- EditorTypeCode.DoubleNumber => new NumberPropertyEditor(double.MinValue, double.MaxValue),
- EditorTypeCode.Switch => new SwitchPropertyEditor(),
- EditorTypeCode.DateTime => new DateTimePropertyEditor(),
- EditorTypeCode.HorizontalAlignment => new HorizontalAlignmentPropertyEditor(),
- EditorTypeCode.VerticalAlignment => new VerticalAlignmentPropertyEditor(),
- EditorTypeCode.ImageSource => new ImagePropertyEditor(),
- _ => new ReadOnlyTextPropertyEditor()
- }
- : type.IsSubclassOf(typeof(Enum))
- ? new EnumPropertyEditor()
- : new ReadOnlyTextPropertyEditor();
-
- public virtual PropertyEditorBase CreateEditor(Type type) => Activator.CreateInstance(type) as PropertyEditorBase ?? new ReadOnlyTextPropertyEditor();
+ public virtual PropertyEditorBase CreateDefaultEditor(Type type)
+ {
+ if (!IsKnownEditorType(ref type))
+ {
+ return new ReadOnlyTextPropertyEditor();
+ }
+ if (type.IsSubclassOf(typeof(Enum)))
+ {
+ return (PropertyEditorBase) new EnumPropertyEditor();
+ }
+
+ return TypeCodeDic[type] switch
+ {
+ EditorTypeCode.PlainText => new PlainTextPropertyEditor(),
+ EditorTypeCode.SByteNumber => new NumberPropertyEditor(sbyte.MinValue, sbyte.MaxValue),
+ EditorTypeCode.ByteNumber => new NumberPropertyEditor(byte.MinValue, byte.MaxValue),
+ EditorTypeCode.Int16Number => new NumberPropertyEditor(short.MinValue, short.MaxValue),
+ EditorTypeCode.UInt16Number => new NumberPropertyEditor(ushort.MinValue, ushort.MaxValue),
+ EditorTypeCode.Int32Number => new NumberPropertyEditor(int.MinValue, int.MaxValue),
+ EditorTypeCode.UInt32Number => new NumberPropertyEditor(uint.MinValue, uint.MaxValue),
+ EditorTypeCode.Int64Number => new NumberPropertyEditor(long.MinValue, long.MaxValue),
+ EditorTypeCode.UInt64Number => new NumberPropertyEditor(ulong.MinValue, ulong.MaxValue),
+ EditorTypeCode.SingleNumber => new NumberPropertyEditor(float.MinValue, float.MaxValue),
+ EditorTypeCode.DoubleNumber => new NumberPropertyEditor(double.MinValue, double.MaxValue),
+ EditorTypeCode.Switch => new SwitchPropertyEditor(),
+ EditorTypeCode.DateTime => new DateTimePropertyEditor(),
+ EditorTypeCode.HorizontalAlignment => new HorizontalAlignmentPropertyEditor(),
+ EditorTypeCode.VerticalAlignment => new VerticalAlignmentPropertyEditor(),
+ EditorTypeCode.ImageSource => new ImagePropertyEditor(),
+ _ => new ReadOnlyTextPropertyEditor()
+ };
+ }
+
+ public virtual PropertyEditorBase CreateEditor(Type type) =>
+ Activator.CreateInstance(type) as PropertyEditorBase ?? new ReadOnlyTextPropertyEditor();
+
+ public bool IsKnownEditorType(ref Type type)
+ {
+ var underlyingType = Nullable.GetUnderlyingType(type);
+ if (underlyingType is not null)
+ {
+ type = underlyingType;
+ }
+
+ return TypeCodeDic.ContainsKey(type) || type.IsSubclassOf(typeof(Enum));
+ }
+
+ public virtual bool IsKnownEditorType(Type type) => IsKnownEditorType(ref type);
+
+ public static bool HasEditorType(PropertyDescriptor propertyDescriptor)
+ {
+ var editorAttribute = propertyDescriptor.Attributes.OfType().FirstOrDefault();
+ return editorAttribute != null && !string.IsNullOrEmpty(editorAttribute.EditorTypeName);
+ }
private enum EditorTypeCode
{
diff --git a/src/Shared/HandyControl_Shared/Data/Annotations/HierarchyLevelAttribute.cs b/src/Shared/HandyControl_Shared/Data/Annotations/HierarchyLevelAttribute.cs
new file mode 100644
index 000000000..ec01c6080
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Data/Annotations/HierarchyLevelAttribute.cs
@@ -0,0 +1,91 @@
+#nullable enable
+using System;
+
+namespace HandyControl.Controls
+{
+ [AttributeUsage(AttributeTargets.All)]
+ public class HierarchyLevelAttribute : Attribute
+ {
+ ///
+ /// This is the hierarchy level value.
+ ///
+ private int? _value;
+
+ ///
+ /// Initializes a new instance of the
+ /// class using a Unicode character.
+ ///
+ public HierarchyLevelAttribute(char value)
+ {
+ _value = value;
+ }
+
+ ///
+ /// Initializes a new instance of the
+ /// class using an 8-bit unsigned integer.
+ ///
+ public HierarchyLevelAttribute(byte value)
+ {
+ _value = value;
+ }
+
+ ///
+ /// Initializes a new instance of the
+ /// class using a 16-bit signed integer.
+ ///
+ public HierarchyLevelAttribute(short value)
+ {
+ _value = value;
+ }
+
+ ///
+ /// Initializes a new instance of the
+ /// class using a 32-bit signed integer.
+ ///
+ public HierarchyLevelAttribute(int value)
+ {
+ _value = value;
+ }
+
+ ///
+ /// Initializes a new instance of the
+ /// class using a .
+ ///
+ public HierarchyLevelAttribute(string? value)
+ {
+ if (value is null || !int.TryParse(value, out var val))
+ {
+ return;
+ }
+ _value = val;
+ }
+
+ ///
+ /// Gets the default value of the property this attribute is bound to.
+ ///
+ public virtual int? Value => _value;
+
+ public override bool Equals(object? obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+ if (!(obj is HierarchyLevelAttribute other))
+ {
+ return false;
+ }
+
+ if (Value == null)
+ {
+ return other.Value == null;
+ }
+
+ return Value.Equals(other.Value);
+ }
+
+ public override int GetHashCode() => base.GetHashCode();
+
+ protected void SetValue(int? value) => _value = value;
+ }
+}
diff --git a/src/Shared/HandyControl_Shared/Data/Annotations/NecessaryAttribute.cs b/src/Shared/HandyControl_Shared/Data/Annotations/NecessaryAttribute.cs
new file mode 100644
index 000000000..f8e379cb5
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Data/Annotations/NecessaryAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace HandyControl.Controls
+{
+ [AttributeUsage(AttributeTargets.Property)]
+ public class NecessaryAttribute : Attribute
+ {
+ public override bool Equals(object obj)
+ {
+ if (object.ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+ if (object.ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+ return obj.GetType() == this.GetType() && Equals((NecessaryAttribute) obj);
+ }
+
+ public override int GetHashCode() => base.GetHashCode();
+
+ protected bool Equals(NecessaryAttribute other) => base.Equals(other);
+ }
+}
diff --git a/src/Shared/HandyControl_Shared/Data/Annotations/PriorityAttribute.cs b/src/Shared/HandyControl_Shared/Data/Annotations/PriorityAttribute.cs
new file mode 100644
index 000000000..599f9e328
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Data/Annotations/PriorityAttribute.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace HandyControl.Controls
+{
+ [AttributeUsage(AttributeTargets.Property)]
+ public class PriorityAttribute : Attribute
+ {
+ private int mValue = 0;
+
+ public PriorityAttribute(int value)
+ {
+ mValue = value;
+ }
+
+ ///
+ /// Gets the default value of the property this attribute is bound to.
+ ///
+ public virtual int Value => mValue;
+
+ public override bool Equals(object obj)
+ {
+ if (object.ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+ if (object.ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+ return obj.GetType() == this.GetType() && Equals((PriorityAttribute) obj);
+ }
+
+ public override int GetHashCode() => base.GetHashCode();
+
+ protected void SetValue(int value) => mValue = value;
+
+ protected bool Equals(PriorityAttribute other) => base.Equals(other) && mValue == other.mValue;
+ }
+}
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..931dcec82
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Data/Enum/Flattening.cs
@@ -0,0 +1,10 @@
+namespace HandyControl.Data.Enum
+{
+ public enum Flattening : byte
+ {
+ Off,
+ Uncategorized,
+ ParentCategory,
+ ParentNameAsCategory
+ }
+}
diff --git a/src/Shared/HandyControl_Shared/Data/Enum/SortingDefault.cs b/src/Shared/HandyControl_Shared/Data/Enum/SortingDefault.cs
new file mode 100644
index 000000000..29b6c8457
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Data/Enum/SortingDefault.cs
@@ -0,0 +1,9 @@
+namespace HandyControl.Data.Enum
+{
+ public enum SortingMode : byte
+ {
+ Category,
+ Name,
+ Hierarchy
+ }
+}
diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
index 0bffa1eea..00659327b 100644
--- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
+++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
@@ -86,6 +86,11 @@
+
+
+
+
+
@@ -384,7 +389,7 @@
-
+
diff --git a/src/Shared/HandyControl_Shared/Interactivity/Commands/ControlCommands.cs b/src/Shared/HandyControl_Shared/Interactivity/Commands/ControlCommands.cs
index a949d26ee..13663d9f2 100644
--- a/src/Shared/HandyControl_Shared/Interactivity/Commands/ControlCommands.cs
+++ b/src/Shared/HandyControl_Shared/Interactivity/Commands/ControlCommands.cs
@@ -181,5 +181,10 @@ public static class ControlCommands
/// 按照名称排序
///
public static RoutedCommand SortByName { get; } = new(nameof(SortByName), typeof(ControlCommands));
+
+ ///
+ ///
+ ///
+ public static RoutedCommand SortByHierarchyLevel { get; } = new(nameof(SortByHierarchyLevel), typeof(ControlCommands));
}
}
diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/Base/PropertyGridBaseStyle.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/Base/PropertyGridBaseStyle.xaml
index bb56a2951..6d443263d 100644
--- a/src/Shared/HandyControl_Shared/Themes/Styles/Base/PropertyGridBaseStyle.xaml
+++ b/src/Shared/HandyControl_Shared/Themes/Styles/Base/PropertyGridBaseStyle.xaml
@@ -28,8 +28,17 @@
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/src/Shared/HandyControl_Shared/Themes/Theme.xaml b/src/Shared/HandyControl_Shared/Themes/Theme.xaml
index 60e4ebf81..bff493586 100644
--- a/src/Shared/HandyControl_Shared/Themes/Theme.xaml
+++ b/src/Shared/HandyControl_Shared/Themes/Theme.xaml
@@ -700,6 +700,13 @@
+
+
+
+
+
+
+
@@ -9996,6 +10003,7 @@
+
-