PropertyGridLib 1.2.0

dotnet add package PropertyGridLib --version 1.2.0
                    
NuGet\Install-Package PropertyGridLib -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PropertyGridLib" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PropertyGridLib" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="PropertyGridLib" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PropertyGridLib --version 1.2.0
                    
#r "nuget: PropertyGridLib, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PropertyGridLib@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PropertyGridLib&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=PropertyGridLib&version=1.2.0
                    
Install as a Cake Tool

PropertyGridLib

WPF PropertyGrid 控件库 — 仿 WinForms PropertyGrid,基于 HandyControl UI 库。

版本 1.2.0 | .NET Framework 4.8+ / .NET 6/7/8-windows | HandyControl 3.5.1 | 作者:WangShuo

📦 相关仓库

仓库 地址
本库(PropertyGridLib) NuGet:Install-Package PropertyGridLib -Version 1.2.0
Demo 项目 https://github.com/1wangshuo/PropertyGridDemo

✨ v1.2.0 新特性

  • 🔌 宿主自定义编辑器扩展机制PropertyGrid.RegisterEditor<TAttr>(DataTemplate) 一行注册,宿主侧定义 Attribute + DataTemplate 即可让 PropertyGrid 渲染自定义编辑器;框架对此 Attribute 一无所知。内置 CustomEditors 静态字典 + PropertyItem.CustomEditorTemplate / CustomEditorAttribute 属性,EditorTemplateSelector 优先返回宿主模板,完全覆盖框架内置编辑器的渲染路径
  • 🎨 颜色编辑器 — 自动识别 Color / Color? / SolidColorBrush 类型,显示为颜色预览块 + ... 按钮(紧凑居左,与 FilePath/Dictionary 编辑器风格统一);点击弹出 HandyControl ColorPicker(StaysOpen=true + 手动外部点击检测,支持拖动 Slider 不丢失焦点,点"确定"才应用值);预览块显示 #RRGGBB(不透明时省略 Alpha 前缀)
  • 📁 字典嵌套字典自动弹窗 — 字典值为集合(IDictionary / IList)时不再内联,改为显示"N 项 + ..."按钮,点击弹出二级编辑器(模态覆盖父级,UI 不会无限拉长);字典值为复杂对象时仍内嵌 PropertyGrid 就地编辑
  • 🌳 FormulaTree 类型提示FormulaTreeNode 新增 FormulaType / TypeHint 属性,TreeView 弹窗叶子节点右侧显示淡蓝色斜体类型名(如 (int) (DateTime)),宿主可通过 ShowTypeHint=False 关闭;Category 前缀改为罗马数字(I. II. III.),切换中英文时分类顺序稳定
  • 🔤 全局字体同步PropertyGrid.FontSize / FontFamily 动态变化时通过 DependencyPropertyDescriptor 实时更新 GlobalFontSize / GlobalFontFamily,所有 Dialog(字典、集合、文件夹、颜色)打开时自动读取最新值
  • 🌓 HandyControl 主题切换修复SetAppSkin 改为直接改 Theme.Skin 属性(HC 官方推荐方式),白→黑→白双向切换均正常,不再因替换 MergedDictionaries 导致重复 key 冲突

✨ v1.1.2 新特性

  • 命令按钮编辑器 [Button] — 属性标记后渲染为按钮,点击可反射调用宿主方法(Action)并触发 ButtonClicked 事件;新增静态 PropertyGrid.GlobalButtonClicked 全局事件,主网格与集合/字典内嵌网格中的按钮点击都能被宿主统一捕获
  • 字典编辑器(IDictionary) — 自动识别字典属性;新增 DictionaryEditorDialog(弹窗)与 DictionaryEditorControl(内联,当 SelectedObject 直接是字典时);键、值均可为简单类型或复杂对象(内嵌 PropertyGrid,可无限嵌套),两者都是对象时左右各占 50%
  • 修复字典 "..." 按钮无响应 的历史缺陷(字典曾被当作集合但 EditCollection 只处理 IList)
  • 修复嵌套网格中按钮"失效"(内嵌 PropertyGrid 的按钮事件无法上抵宿主、且不刷新)
  • 新增 .NET Core 3.0 / 3.1 目标框架(连同 net48 / net6 / net7 / net8-windows)

✨ v1.1.1 新特性

  • 集合编辑器内联显示 — 当 PropertyGrid.SelectedObject 设为 IList 类型时,自动内联显示集合编辑器(无需弹出对话框)
  • 集合编辑器 UserControl 化CollectionEditorControl / SimpleCollectionEditorControl 可独立作为 UserControl 嵌入任意容器
  • FormulaBindingControl 独立使用 — 新增 TreeItemsSource / FormulaTreeProvider 依赖属性,支持脱离 PropertyGrid 直接使用
  • 简单类型自动检测 — 内联模式自动区分简单类型(string/int/double 等)和复杂对象,选择对应编辑器

✨ v1.1.0 新特性

  • 完全移除 System.Windows.Forms 依赖 - 纯 WPF 实现
  • 多框架支持 - .NET Framework 4.8 / .NET 6/7/8-windows
  • HandyControl 主题集成 - 所有对话框自动跟随深色/浅色主题
  • 自定义文件夹浏览器 - 纯 WPF 实现,树形目录浏览
  • 文件夹浏览器支持手动输入路径自动定位 - 输入合法路径自动在树上选中
  • 完整中英文多语言 - 所有新增组件支持语言切换
  • 现代化序列化 - JSON 替代 BinaryFormatter
  • 零依赖冲突 - System.Text.Json 向上兼容到 16.0+

目录


安装

NuGet 包管理器

Install-Package PropertyGridLib -Version 1.2.0

.NET CLI

dotnet add package PropertyGridLib --version 1.2.0

PackageReference (PackageReference)

<PackageReference Include="PropertyGridLib" Version="1.2.0" />

安装后,NuGet 会自动拉取依赖项 HandyControl 3.5.1

🎯 单 DLL 部署

PropertyGridLib 使用 Costura.FodyHandyControl.dllSystem.Text.Json.dll 等依赖嵌入合并到主 DLL 内。

PropertyGridLib.dll  ← 唯一需要部署的 DLL(内部已嵌入 HandyControl 等)

宿主项目只需引用这一个 DLL,无需额外附带 HandyControl.dll。运行时通过 AssemblyResolve 事件自动从内嵌资源加载依赖。

⚠️ 若宿主项目也直接引用了 HandyControl(版本可能不同),两个内嵌的 HandyControl 会并存。建议宿主统一使用 PropertyGridLib 内嵌的版本,或在宿主 csproj 中显式指定 HandyControl 版本以覆盖内嵌。


快速开始

1. 添加命名空间

xmlns:pg="clr-namespace:PropertyGridLib;assembly=PropertyGridLib"

2. 在 XAML 中使用

<pg:PropertyGrid x:Name="propertyGrid"
                 Width="400"
                 Height="600"
                 ShowSearchBar="True"
                 ShowDescription="True"/>

3. 绑定对象

propertyGrid.SelectedObject = new MyConfig();

4. 重置与刷新

propertyGrid.ResetSelectedToDefault();  // 重置当前选中属性
propertyGrid.ResetAllToDefault();       // 重置所有属性
propertyGrid.RefreshProperties();        // 刷新属性列表

命名空间

命名空间 内容
PropertyGridLib PropertyGrid 控件、IPropertyLocalization 接口
PropertyGridLib.Controls PropertyItem、IPropertyItem、PropertyCategory、EditorTemplateSelector、FormulaBound<T>、FormulaTreeNode、IFormulaTreeProvider、IMultiSelectProvider、MultiSelectControl、FormulaBindingControl、CollectionEditorControl、SimpleCollectionEditorControl、DictionaryEditorControl、ColorEditControl、PropertyButtonClickEventArgs、CustomEditorType 枚举
PropertyGridLib.Attributes FilePathAttribute、DirectoryPathAttribute、CollectionEditorAttribute、NumberSliderAttribute、FormulaEditorAttribute、MultiSelectAttribute、ButtonAttribute
PropertyGridLib.Dialogs FolderBrowserDialog、CollectionEditorDialog、SimpleCollectionEditorDialog、DictionaryEditorDialog
PropertyGridLib.Localization LocalizationManager、LocalizationProxy、LocalizedExtension、Language 枚举
PropertyGridLib.Converters BoolToVisibilityConverter、InverseBoolConverter、ObjectToTypeConverter、ColorToBrushConverter 等值转换器

标准 .NET 特性

PropertyGrid 自动识别以下标准 .NET 特性:

特性 说明 示例
[Category("分类名")] 属性分组显示 [Category("外观")]
[DisplayName("显示名")] 自定义属性显示名称 [DisplayName("字体大小")]
[Description("描述")] 底部描述栏显示 [Description("文字的字体大小")]
[DefaultValue(value)] 默认值,控制重置按钮显示 [DefaultValue(14)]
[ReadOnly(true)] 只读属性,不可编辑 [ReadOnly(true)]
[Browsable(false)] 隐藏属性,不在列表中显示 [Browsable(false)]

自定义特性

FilePathAttribute — 文件路径选择

标记属性为文件路径,显示浏览按钮,点击弹出文件选择对话框。支持多后缀筛选。

using PropertyGridLib.Attributes;

// 单后缀
[FilePath("*.log")]
public string LogFilePath { get; set; } = "";

// 多后缀
[FilePath("*.json", "*.xml", "*.txt")]
public string ConfigFilePath { get; set; } = "";

可选属性:

属性 类型 说明
Filters string[] 文件后缀列表(构造函数参数)
Filter string 完整过滤器字符串(如 "*.json;*.xml"
InitialDirectory string 初始目录
Title string 对话框标题
Multiselect bool 是否允许多选

DirectoryPathAttribute — 目录路径选择

标记属性为目录路径,显示浏览按钮,点击弹出文件夹选择对话框(纯 WPF 实现,支持 HandyControl 主题)。

[DirectoryPath]
public string DataDirectory { get; set; } = "";

可选属性:

属性 类型 说明
InitialDirectory string 初始目录
Title string 对话框标题

特性:

  • 树形目录浏览
  • 支持路径直接输入
  • 自动跟随 HandyControl 深色/浅色主题
  • 完整的中英文多语言支持

CollectionEditorAttribute — 集合编辑器

标记集合类型属性使用编辑器对话框。简单类型(string、int 等)使用简易编辑器,复杂对象使用完整编辑器(含属性面板)。

// 简单类型集合 — 弹出简易编辑器(输入框 + 列表 + 增删排序)
[CollectionEditor]
public List<string> Tags { get; set; } = new List<string> { "A", "B" };

// 复杂对象集合 — 弹出完整编辑器(列表 + 属性面板 + 增删复制排序)
[CollectionEditor]
public List<RobotConfig> Robots { get; set; } = new List<RobotConfig>();

可选属性:

属性 类型 默认值 说明
CanAdd bool true 是否允许添加新项
CanRemove bool true 是否允许删除项
CanCopy bool true 是否允许复制项
CanSort bool true 是否允许上移/下移排序

集合编辑器自动区分简单类型和复杂对象。无需手动指定,控件会根据 List<T>T 类型自动选择合适的编辑器。


NumberSliderAttribute — 数值滑块

将数值属性显示为滑块编辑器,支持 int、double、float、decimal 等数值类型。

// 整数滑块
[NumberSlider(0, 100, 5)]  // min, max, step
public int Volume { get; set; } = 50;

// 小数滑块(默认显示只读数值标签)
[NumberSlider(0.0, 1.0, 0.1)]
public double Brightness { get; set; } = 0.8;

// 滑块 + 可编辑数字框(hc:NumericUpDown,可键入小数等精确值;min/max/step 与滑块共用,
// 双向同步;显示后取代只读数值标签)
[NumberSlider(0, 100, 5, ShowNumberBox = true)]
public double Opacity { get; set; } = 50;

参数说明:

参数 类型 说明
minimum double 最小值
maximum double 最大值
step double 步长(默认 1)
ShowNumberBox bool 是否显示可编辑数字输入框(默认 false;启用后与滑块双向同步并取代数值标签)

FormulaEditorAttribute — 公式绑定

标记属性支持公式绑定编辑器。标记后,属性编辑器下方会额外显示一行公式绑定区(链接图标 + 公式文本框 + Popup 树选择器)。

// 需要实现 IFormulaTreeProvider 接口
[FormulaEditor(typeof(MyFormulaTreeProvider))]
public FormulaBound<string> NameFormula { get; set; } = new FormulaBound<string> { Value = "默认值" };

IFormulaTreeProvider 接口:

using PropertyGridLib.Controls;

public class MyFormulaTreeProvider : IFormulaTreeProvider
{
    public List<FormulaTreeNode> GetFormulaTree(PropertyItem propertyItem)
    {
        return new List<FormulaTreeNode>
        {
            new FormulaTreeNode
            {
                Header = "流程A",
                Children = new List<FormulaTreeNode>
                {
                    new FormulaTreeNode
                    {
                        Header = "任务1",
                        Children = new List<FormulaTreeNode>
                        {
                            new FormulaTreeNode { Header = "属性X", Formula = "&{流程A.任务1.属性X}" }
                        }
                    }
                }
            }
        };
    }
}

FormulaTreeNode 属性:

属性 类型 说明
Header string 节点显示文本
Formula string 选中后填入的公式字符串(仅叶子节点需要设置)
Children List<FormulaTreeNode> 子节点列表
FormulaType Type? 叶子节点的数值类型(如 typeof(int)),用于 TreeView 弹窗显示类型提示
TypeHint string 自动从 FormulaType 生成的类型提示(如 (int)(string)

MultiSelectAttribute — 多选列表

标记属性为多选列表编辑器,弹出 CheckBox 列表供用户多选。属性类型应为 List<string>,Provider 动态返回可选项。

// 需要实现 IMultiSelectProvider 接口
[MultiSelect(typeof(MyMultiSelectProvider))]
public List<string> SelectedOptions { get; set; } = new List<string>();

IMultiSelectProvider 接口:

using PropertyGridLib.Controls;

public class MyMultiSelectProvider : IMultiSelectProvider
{
    public List<string> GetAvailableItems(PropertyItem propertyItem)
    {
        return new List<string> { "选项A", "选项B", "选项C", "选项D" };
    }
}

ButtonAttribute — 命令按钮

标记属性为命令按钮。被标记的属性不再显示值编辑器,而是渲染为一个按钮;点击后:① 通过反射调用宿主对象上 Action 指定的方法(若指定);② 触发 PropertyItem.ButtonClickedPropertyGrid.ButtonClicked / 静态 PropertyGrid.GlobalButtonClicked 事件。

using PropertyGridLib.Attributes;

// 仅触发事件(文本回退到 DisplayName,随多语言切换)
[Button]
public object GreetButton { get; set; }

// 指定按钮文本 + 点击时反射调用的宿主方法(无参或单参)
[Category("操作")]
[DisplayName("测试连接")]
[Button("测试连接", nameof(TestConnection))]
public object TestConnectionButton { get; set; }

public void TestConnection() { /* 点击后由库反射调用 */ }

宿主侧统一响应(推荐订阅静态全局事件,可捕获集合/字典内嵌网格中的按钮):

PropertyGrid.GlobalButtonClicked += (s, e) =>
{
    // e.PropertyName / e.PropertyItem / e.Owner
    Console.WriteLine($"{e.PropertyName} 被点击");
};
// 静态事件,窗口关闭时记得取消订阅:PropertyGrid.GlobalButtonClicked -= handler;

参数说明:

参数 类型 说明
text string 按钮文本;为空时回退到 DisplayName
action string 点击时反射调用的宿主方法名(无参或单参);为空时仅触发事件

建议将按钮属性声明为 object/string 占位类型(值可为 null)。按钮默认始终可点,仅当属性显式标注 [ReadOnly(true)] 时禁用。


🎨 颜色编辑器

自动检测

PropertyGrid 自动识别以下类型,无需任何特性:

类型 处理方式
Color 值类型,双向编辑
Color? 可空,支持 null
SolidColorBrush Brush.Color 编辑,写回时重建 SolidColorBrush

UI 布局

┌──────────────────┐ ┌──────┐
│ ■ #FF0000        │ │  ... │
└──────────────────┘ └──────┘
   颜色预览块 + HEX      编辑按钮
  • 居左占满宽度,与 FilePath/Dictionary 编辑器风格统一
  • 预览块:16×16 颜色方块 + #AARRGGBB HEX 文本
  • 不透明颜色(Alpha=255)时省略 Alpha 前缀,显示 #RRGGBB
  • 深色/浅色主题自动适配

交互流程

点击 ... 按钮弹出 HandyControl ColorPicker

操作 行为
拖动 Slider / 选色块 ColorPicker 内部预览,不修改 PropertyItem.Value
点"确定" 写入 PropertyItem.Value → 关闭弹窗
点"取消" 不改值 → 关闭弹窗
点弹窗外面 不改值 → 关闭弹窗

关键技术点

  • StaysOpen=true + 手动 Window.PreviewMouseDown 检测外部点击(避免 HC ColorPicker 内部 Slider 拖动导致 Popup 误判关闭)
  • IsInVisualTree() 从 OriginalSource 向上遍历 VisualTree 判定点击来源
  • Freeze() 创建 SolidColorBrush 防止绑定循环警告
  • DependencyPropertyDescriptor 监听 FontSize/FontFamily 变化,ColorPicker 弹窗自动同步宿主字体

FormulaBound 属性的颜色提示

当属性带 [FormulaEditor]ShowTypeHint=True 时,公式绑定区右侧显示淡蓝色斜体类型提示(如 (string)(int)),颜色与公式输入框区分。


📁 字典编辑器(IDictionary)

字典属性(Dictionary<TKey,TValue>IDictionary)会被自动识别,显示为 "N 项 + ..." 按钮,点击弹出 DictionaryEditorDialog。无需任何特性;若把 [CollectionEditor] 标在字典属性上,也会路由到字典编辑器。

// 简单键值:键、值都用文本框编辑(自动类型转换)
public Dictionary<string, int> ScoreMap { get; set; } = new Dictionary<string, int>();

// 复杂值:值区显示内嵌 PropertyGrid,可继续编辑其内部的集合/字典(无限嵌套)
public Dictionary<string, RobotConfig> Robots { get; set; } = new Dictionary<string, RobotConfig>();

// 复杂键:键区也显示内嵌 PropertyGrid(键类型需重写 Equals/GetHashCode)
public Dictionary<ServerEndpoint, string> Endpoints { get; set; } = new Dictionary<ServerEndpoint, string>();

// ===== 嵌套字典:值为字典时自动弹窗 =====
// 框架检测到值类型是 IDictionary → 显示 "N 项 ..." 按钮,点击弹出二级字典编辑器
public Dictionary<string, Dictionary<string, int>> NestedConfig { get; set; } = new()
{
    { "白天模式", new Dictionary<string, int> { { "人员检测", 6 }, { "车辆检测", 5 } } },
    { "夜间模式", new Dictionary<string, int> { { "人员检测", 8 }, { "车辆检测", 7 } } }
};

// ===== 字典值为列表:同样弹窗 =====
public Dictionary<string, List<string>> ZoneAlerts { get; set; } = new()
{
    { "大门", new List<string> { "2024-03-15 检测到人员" } }
};

嵌套值的三种渲染模式

值类型 检测 UI 表现 编辑方式
简单类型(string/int/double 等) IsSimpleType TextBox 就地文本编辑
集合类型(IDictionary / IList) IsDictionaryType / IsListType "N 项 ..."按钮 点击弹窗(模态覆盖父级)
其他复杂对象 排除以上两种 内嵌 PropertyGrid 就地编辑(可继续嵌套其属性)

内联模式

PropertyGrid.SelectedObject 直接设为字典时,自动内联显示 DictionaryEditorControl(就地编辑、实时写回,无弹窗),与集合内联模式一致。

propertyGrid.SelectedObject = new Dictionary<string, int> { { "A", 1 }, { "B", 2 } };

独立使用

DictionaryEditorControlPropertyGridLib.Controls)可作为 UserControl 嵌入任意容器,ItemsIDictionary,直接操作源字典。

⚠️ 复杂键为可变对象时,就地编辑会改变其哈希——编辑器在"确定/切换条目"时会 Clear + 重新 Add 重建哈希;复杂键类型务必重写 Equals/GetHashCode。另注意 System.Text.Json 不支持非字符串字典键,故复杂键字典不宜作为会被深拷贝(Reset)的 [Serializable] 模型属性。


🔌 宿主自定义编辑器扩展机制

PropertyGridLib 提供通用扩展点,宿主只需 3 步即可让自定义编辑器生效,框架对此 Attribute 一无所知。

完整流程

宿主侧: [MyUnitEditor("GB,MB,KB", "MB")] 属性
  → PropertyGrid.RegisterEditor<MyUnitEditorAttribute>(template)  // 注册
    → PropertyItem.DetectCustomEditor 发现 Attribute
      → CustomEditorTemplate = template, CustomEditorAttribute = attr  // 存起来
        → EditorTemplateSelector.SelectTemplate 优先返回 CustomEditorTemplate  // 渲染
          → DataTemplate 里 NumericUpDown + ComboBox
            → ComboBox Loaded 从 CustomEditorAttribute 读参数  // 宿主解析参数

Step 1 — 宿主定义 Attribute

// 纯宿主侧,框架对此类型一无所知
[AttributeUsage(AttributeTargets.Property)]
public class MyUnitEditorAttribute : Attribute
{
    public string Units { get; }
    public string DefaultUnit { get; set; }

    public MyUnitEditorAttribute(string units) => Units = units;
}

Step 2 — 宿主定义 DataTemplate


<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            
            <ResourceDictionary Source="pack://application:,,,/PropertyGridLib;component/Themes/Generic.xaml"/>
            <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
            <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
        </ResourceDictionary.MergedDictionaries>

        <DataTemplate x:Key="MyUnitEditorTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <hc:NumericUpDown Grid.Column="0"
                                  Value="{Binding ValueString, ...}"
                                  Margin="0,0,4,0"/>
                <ComboBox Grid.Column="1"
                          Loaded="UnitCombo_Loaded"
                          SelectionChanged="UnitCombo_SelectionChanged"/>
            </Grid>
        </DataTemplate>
    </ResourceDictionary>
</Application.Resources>

Step 3 — 宿主注册

// MainWindow 构造函数中(必须在 PropertyGrid.SelectedObject 赋值前注册)
PropertyGridLib.PropertyGrid.RegisterEditor<MyUnitEditorAttribute>(
    (DataTemplate)FindResource("MyUnitEditorTemplate"));

Step 4 — 使用

[MyUnitEditor("GB,MB,KB", "MB")]
public double FileSize { get; set; } = 256;

模板内读 Attribute 参数

宿主在 DataTemplate 的 code-behind 中,从 PropertyItem.CustomEditorAttribute 拿到宿主 Attribute 实例,再读参数:

private void UnitCombo_Loaded(object sender, RoutedEventArgs e)
{
    if (sender is ComboBox combo && combo.DataContext is PropertyItem pi)
    {
        var attr = pi.CustomEditorAttribute as MyUnitEditorAttribute;
        if (attr == null) return;

        var units = (attr.Units ?? "").Split(',').Select(s => s.Trim()).ToList();
        combo.ItemsSource = units;
        combo.SelectedItem = !string.IsNullOrEmpty(attr.DefaultUnit)
            ? attr.DefaultUnit
            : units.FirstOrDefault();
    }
}

API 参考

成员 类型 说明
PropertyGrid.CustomEditors Dictionary<Type, DataTemplate> 静态注册字典,宿主可直接操作或通过 RegisterEditor 泛型方法
PropertyGrid.RegisterEditor<TAttribute>(template) static void 泛型注册方法,一行搞定
PropertyItem.CustomEditorTemplate DataTemplate? 宿主注册的编辑器模板,非 null 时 EditorTemplateSelector 优先使用
PropertyItem.CustomEditorAttribute Attribute? 宿主 Attribute 实例,模板 code-behind 可读取参数

优先级

宿主注册 高于 框架内置。DetectCustomEditor 最前面先查 PropertyGrid.CustomEditors,找到就返回,内置的 Slider/Color/FilePath/Dictionary 都排在后面。宿主甚至可以覆盖框架内置编辑器的行为 — 比如给带 [NumberSlider] 的属性也注册一个自己的模板。


🌳 FormulaTree 类型提示

FormulaTreeNode 支持在 TreeView 弹窗中显示叶子节点的类型信息,帮助用户判断绑定源的类型是否匹配目标属性。

宿主设置类型

var provider = new DemoFormulaTreeProvider();
// 叶子节点设置 FormulaType
new FormulaTreeNode
{
    Header = "任务A.值",
    Formula = "&{Flow2,TaskA,Value}",
    FormulaType = typeof(int)  // ← 宿主指定类型
}

PropertyGrid 控制显示

<pg:PropertyGrid ShowTypeHint="False" .../>
属性 类型 默认值 说明
ShowTypeHint bool true 是否显示类型提示(FormulaTree 弹窗 + 公式绑定区)

效果

├─ ▼ 流程2
│    任务A.值          (int)    ← 淡蓝色斜体,靠右
│    任务B.计数        (double)
│    任务C.启用状态    (bool)
  • 非叶子节点(分类容器)不填 FormulaType,不显示类型提示
  • TypeHint 自动从 FormulaType 生成,宿主也可直接覆盖 TypeHint 属性
  • 颜色 InfoBrush + Italic + 比正常小 2 号,与节点 Header 区分

编辑器类型总览

控件根据属性类型和特性自动选择编辑器模板:

编辑器 触发条件 显示效果
TextBox string 类型 文本输入框
CheckBox bool / bool? 复选框开关
NumericUpDown int, double, float, decimal, long, short, byte 数字输入框(带增减按钮)
ComboBox enum 枚举下拉选择
DateTimePicker DateTime / DateTime? 日期时间选择器
Slider [NumberSlider] 特性 滑块 + 数值标签
Color Color / Color? / SolidColorBrush 颜色预览块 + "..." 按钮(弹出 HC ColorPicker)
FilePath [FilePath] 特性 文本框 + 浏览按钮
DirectoryPath [DirectoryPath] 特性 文本框 + 浏览按钮
Collection [CollectionEditor] 特性或 IList 类型 文本框 + 编辑按钮(弹出对话框)
Dictionary IDictionary 类型(自动识别) "N 项" + 编辑按钮;值为集合时显示 "N 项 ..." 弹窗
DropDown TypeConverter 标准值 下拉列表(支持排他/可编辑模式)
MultiSelect [MultiSelect] 特性 摘要文本 + 弹出 CheckBox 列表
Button [Button] 特性 命令按钮(点击触发宿主方法/事件)
Expandable 复杂对象(非简单类型、非集合) 可展开子属性
Formula [FormulaEditor] 特性或 FormulaBound<T> 值编辑器 + 公式绑定区
宿主自定义 PropertyGrid.CustomEditors 注册的 Attribute 宿主提供的 DataTemplate

PropertyGrid 集合模式(内联显示)

PropertyGrid.SelectedObject 设为 IList 类型(如 List<string>List<CustomType>)时,PropertyGrid 自动切换为集合编辑模式,内联显示集合编辑器,无需弹出对话框。

// 简单类型列表 — 自动显示 SimpleCollectionEditorControl
propertyGrid.SelectedObject = new List<string> { "A", "B", "C" };

// 复杂对象列表 — 自动显示 CollectionEditorControl(含内嵌 PropertyGrid)
propertyGrid.SelectedObject = new List<MyConfig> { new MyConfig() };

自动类型检测:

  • 简单类型(stringintdoublefloatdecimallongboolDateTimeenum 等)→ SimpleCollectionEditorControl
  • 复杂对象类型 → CollectionEditorControl(左侧列表 + 右侧属性面板)
  • string 不会被识别为集合;IDictionary 会走独立的字典编辑器(见上节)

集合编辑器 UserControl 独立使用

CollectionEditorControlSimpleCollectionEditorControl 现在可以作为 UserControl 独立嵌入任意容器,不再仅限于对话框弹出。

CollectionEditorControl(复杂对象列表)

xmlns:controls="clr-namespace:PropertyGridLib.Controls;assembly=PropertyGridLib"

<controls:CollectionEditorControl x:Name="editor" Items="{Binding MyList}" />
editor.Items = new List<MyConfig>
{
    new MyConfig { Name = "Item1", Value = 100 },
    new MyConfig { Name = "Item2", Value = 200 }
};

SimpleCollectionEditorControl(简单类型列表)

<controls:SimpleCollectionEditorControl x:Name="simpleEditor" Items="{Binding MyStringList}" />
simpleEditor.Items = new List<string> { "Apple", "Banana", "Cherry" };
// ElementType 会自动从集合泛型参数推断,也可手动指定
simpleEditor.ElementType = typeof(int);
simpleEditor.Items = new List<int> { 1, 2, 3 };

两个控件都直接操作传入的 IList 实例,每次增删改操作后立即同步回源集合。


FormulaBindingControl 独立使用

FormulaBindingControl 支持脱离 PropertyGrid 独立使用,通过设置 TreeItemsSourceFormulaTreeProvider 提供公式树数据。

方式一:FormulaTreeProvider(推荐)


<Window.Resources>
    <local:MyFormulaTreeProvider x:Key="myProvider"/>
</Window.Resources>

<controls:FormulaBindingControl
    FormulaTreeProvider="{StaticResource myProvider}"
    FormulaString="{Binding MyFormula, Mode=TwoWay}" />

方式二:TreeItemsSource(直接绑定树节点)

<controls:FormulaBindingControl
    TreeItemsSource="{Binding MyTreeNodes}"
    FormulaString="{Binding MyFormula, Mode=TwoWay}" />
public List<FormulaTreeNode> MyTreeNodes { get; } = new List<FormulaTreeNode>
{
    new FormulaTreeNode
    {
        Header = "传感器",
        Children =
        {
            new FormulaTreeNode { Header = "温度", Formula = "&{Sensors,Temperature,Value}" },
            new FormulaTreeNode { Header = "湿度", Formula = "&{Sensors,Humidity,Value}" }
        }
    }
};

数据源优先级: TreeItemsSource > FormulaTreeProvider > PropertyItem.DataContext(PropertyGrid 内嵌用法)


FormulaBound<T> — 公式绑定泛型类型

FormulaBound<T> 是一个包装类型,同时持有值和公式字符串。用于数据绑定场景,让属性既能存储实际值,又能记录绑定公式。

using PropertyGridLib.Controls;

public class MyConfig
{
    // string 类型公式绑定
    [FormulaEditor(typeof(MyProvider))]
    public FormulaBound<string> Name { get; set; } = new FormulaBound<string> { Value = "默认", Formula = "" };

    // int 类型也支持公式绑定
    [FormulaEditor(typeof(MyProvider))]
    public FormulaBound<int> Count { get; set; } = new FormulaBound<int> { Value = 42 };
}

FormulaBound<T> 属性:

属性 类型 说明
Value T 实际值
Formula string 绑定公式字符串(如 &{流程.任务.属性}

PropertyGrid 自动检测 FormulaBound<T> 类型,使用内部类型 T 选择编辑器,同时显示公式绑定区。即使不标记 [FormulaEditor]FormulaBound<T> 也会自动启用公式绑定输入框。


TypeConverter 下拉列表

当属性的 TypeConverter 提供标准值(GetStandardValuesSupported 返回 true),PropertyGrid 自动显示为下拉列表。

using System.ComponentModel;

// 自定义 TypeConverter 提供标准值
public class ComPortListConverter : TypeConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;

    public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true;  // true=排他(不可手动输入),false=可输入

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        return new StandardValuesCollection(new[] { "COM1", "COM2", "COM3", "COM4" });
    }
}

// 使用
[TypeConverter(typeof(ComPortListConverter))]
public string ComPort { get; set; } = "COM1";

多语言 / 本地化系统

PropertyGridLib 内置中英文双语支持,提供三个层面的本地化能力:

1. 类库内部字符串

通过 LocalizationManager 管理搜索框占位符、对话框标题、按钮文本等 47 个内置字符串。

using PropertyGridLib.Localization;

// 切换语言
LocalizationManager.CurrentLanguage = Language.EnUS;   // 英文
LocalizationManager.CurrentLanguage = Language.ZhCN;   // 中文(默认)

// 获取本地化字符串
var text = LocalizationManager.GetString("SearchPlaceholder");
var count = LocalizationManager.GetString("SelectedCount", 5);

在 XAML 中使用标记扩展绑定:

xmlns:loc="clr-namespace:PropertyGridLib.Localization;assembly=PropertyGridLib"

<TextBlock Text="{loc:Localized SearchPlaceholder}"/>

2. 属性名 / 描述 / 分类动态翻译

实现 IPropertyLocalization 接口,让数据对象提供动态的多语言翻译。语言切换时,PropertyGrid 自动刷新所有属性。

using PropertyGridLib;
using PropertyGridLib.Localization;

public class MyConfig : IPropertyLocalization
{
    [Category("外观")]
    [DisplayName("名称")]
    [Description("对象的显示名称")]
    public string Name { get; set; } = "Test";

    // 返回 null 时回退到 [DisplayName] 特性值
    public string GetDisplayName(string propertyName, Language language)
    {
        if (language == Language.ZhCN) return null;  // 中文用特性值
        return propertyName switch
        {
            "Name" => "Name",
            _ => null
        };
    }

    // 返回 null 时回退到 [Description] 特性值
    public string GetDescription(string propertyName, Language language)
    {
        if (language == Language.ZhCN) return null;
        return propertyName switch
        {
            "Name" => "Display name of the object",
            _ => null
        };
    }

    // 返回 null 时回退到 [Category] 特性值
    public string GetCategory(string propertyName, Language language)
    {
        if (language == Language.ZhCN) return null;
        return propertyName switch
        {
            "Name" => "Appearance",
            _ => null
        };
    }
}

3. 语言切换流程

用户调用 LocalizationManager.CurrentLanguage = Language.EnUS
    ↓
触发 LanguageChanged 事件
    ↓
PropertyGrid.OnLanguageChanged() 遍历所有属性
    ↓
PropertyItem.UpdateLocalization() 递归更新 DisplayName/Description/Category
    ↓
刷新描述栏 + 重新分组过滤

内置本地化 Key 列表(部分常用,共 47 个):

Key 中文 English
SearchPlaceholder 搜索属性... Search properties...
SelectBindingSource 选择绑定源 Select Binding Source
SelectItems 选择项 Select Items
SelectMultiple 选择多项 Select Multiple
ResetToDefault 重置为初始值 Reset to default
FormulaPlaceholder 公式绑定... Formula binding...
SelectFile 选择文件 Select File
SelectFolder 选择文件夹 Select Folder
AllFiles 所有文件 All Files
Files 文件 Files
ItemsCount [{0} 项] [{0} items]
Misc 杂项 Miscellaneous
NotSelected (未选择) (None)
SelectedCount 已选 {0} 项 {0} items selected
CollectionEditorTitle 集合编辑器 Collection Editor
CollectionItems 集合项 Collection Items
Properties 属性 Properties
Add 添加 Add
Copy 复制 Copy
Remove 删除 Remove
OK 确定 OK
Cancel 取消 Cancel
EditCollectionTitle 编辑集合 Edit Collection
InputPlaceholder 输入新值... Enter new value...
MoveUp 上移 Move Up
MoveDown 下移 Move Down
Tip 提示 Tip
Error 错误 Error
AddFailed 添加失败:{0} Add failed: {0}
CopyFailed 复制失败:{0} Copy failed: {0}
ConvertFailed 转换失败:{0} Convert failed: {0}
SelectItemToDelete 请先选择要删除的项 Please select an item to delete first
SelectItemToCopy 请先选择要复制的项 Please select an item to copy first
NoDefaultConstructor 类型 {0} 没有无参构造函数 Type {0} has no parameterless constructor
DictionaryEditorTitle 字典编辑器 Dictionary Editor
DictEntries 字典条目 Entries
DictKey 键 (Key) Key
DictValue 值 (Value) Value
DuplicateKey 键 "{0}" 已存在,请使用唯一的键 Key "{0}" already exists. Keys must be unique.

API 参考

PropertyGrid 类

命名空间: PropertyGridLib

依赖属性
属性 类型 默认值 说明
SelectedObject object null 要编辑的目标对象
SearchText string "" 搜索文本(自动过滤)
ShowDescription bool true 是否显示底部描述栏
ShowSearchBar bool true 是否显示搜索栏
SelectedPropertyItem IPropertyItem null 当前选中的属性项
GroupedCategories IEnumerable null 分组后的分类列表(供绑定)
IsLoading bool false 是否正在加载(切换对象时显示动画)
FormulaTreeProvider IFormulaTreeProvider null 全局公式树提供者(可选)
ShowTypeHint bool true 是否显示 FormulaTree 类型提示和公式绑定区类型提示
静态成员
成员 类型 说明
GlobalFontFamily FontFamily 全局字体(默认 Consolas),宿主可设置;PropertyGrid.FontFamily 变化时自动同步
GlobalFontSize double 全局字号(默认 12),宿主可设置;PropertyGrid.FontSize 变化时自动同步
CustomEditors Dictionary<Type, DataTemplate> 宿主自定义编辑器注册字典,AttributeType → DataTemplate
RegisterEditor<TAttribute>(template) static void 泛型注册方法,一行搞定宿主自定义编辑器
公共方法
方法 说明
RefreshProperties() 刷新属性列表(重新反射对象)
ResetSelectedToDefault() 重置当前选中属性到初始值
ResetAllToDefault() 重置所有属性到初始值
事件
事件 说明
ButtonClicked(实例) 本网格内 [Button] 属性被点击时触发(参数 PropertyButtonClickEventArgs
GlobalButtonClicked(静态) 任意 PropertyGrid 实例(含集合/字典内嵌网格、弹窗网格)的 [Button] 被点击时触发;宿主订阅一次即可全局响应,窗口关闭时应取消订阅

PropertyGrid 在内部监听 LocalizationManager.LanguageChanged 事件,语言切换时自动刷新所有属性的本地化文本。

集合/字典模式行为:SelectedObjectIList(排除 string)时内联显示集合编辑器;为 IDictionary 时内联显示字典编辑器;两者均自动隐藏正常属性列表。


ColorEditControl 类

命名空间: PropertyGridLib.Controls

颜色编辑器 UserControl,内嵌 HandyControl ColorPicker 弹窗。PropertyGrid 自动检测 Color / Color? / SolidColorBrush 类型,宿主无需手动使用此类。

成员 类型 说明
PART_ColorPreview Border 颜色预览块(16×16),颜色随 ColorPicker.SelectedColor 同步
PART_ColorHexText TextBlock HEX 文本,显示 #RRGGBB#AARRGGBB
PART_EditButton Button 点击弹出 ColorPicker 弹窗
PART_ColorPicker hc:ColorPicker 弹窗内的 ColorPicker(StaysOpen=true,Confirmed/Canceled 事件处理)

CollectionEditorControl 类

命名空间: PropertyGridLib.Controls

复杂对象列表编辑器 UserControl。左侧列表 + 右侧内嵌 PropertyGrid + 增删复制排序按钮。

依赖属性 类型 默认值 说明
Items IList null 绑定的集合数据(直接操作源集合)

SimpleCollectionEditorControl 类

命名空间: PropertyGridLib.Controls

简单类型列表编辑器 UserControl。列表 + 输入框 + 增删排序按钮。

依赖属性 类型 默认值 说明
Items IList null 绑定的集合数据(直接操作源集合)
ElementType Type typeof(string) 元素类型(通常自动从集合泛型参数推断)

DictionaryEditorControl 类

命名空间: PropertyGridLib.Controls

字典编辑器 UserControl。左侧条目列表 + 右侧键/值编辑区(键、值各自可为文本框或内嵌 PropertyGrid)。直接操作传入的 IDictionary 实例,增删改即时写回。

依赖属性 类型 默认值 说明
Items IDictionary null 绑定的字典数据(直接操作源字典)

弹窗版为 PropertyGridLib.Dialogs.DictionaryEditorDialog(构造传入 IDictionaryShowDialog() 返回 true 表示写回)。


FormulaBindingControl 独立使用属性

命名空间: PropertyGridLib.Controls

除 PropertyGrid 内嵌用法外,新增以下依赖属性支持独立使用:

依赖属性 类型 默认值 说明
TreeItemsSource IEnumerable null 直接设置公式树节点(FormulaTreeNode 集合),优先级最高
FormulaTreeProvider IFormulaTreeProvider null 设置公式树提供者,优先级次之
FormulaString string "" 公式字符串(支持 TwoWay 绑定)

PropertyItem 类

命名空间: PropertyGridLib.Controls

属性 类型 说明
Name string 属性名(代码中的名称)
DisplayName string 显示名称
Category string 分类
Description string 描述
Value object 属性值(可读写)
ValueString string 属性值字符串表示
PropertyType Type 属性类型
EffectivePropertyType Type 有效类型(FormulaBound<T> 时返回 T;Color 类型时省略 Alpha 显示)
IsReadOnly bool 是否只读
IsBrowsable bool 是否可浏览
IsExpandable bool 是否可展开子属性
IsExpanded bool 是否已展开
HasDefaultValue bool 是否有默认值
IsDefault bool 当前值是否等于初始值
IsModified bool 是否已修改
CustomEditor CustomEditorType 自定义编辑器类型(仅框架内置编辑器设置,宿主扩展为 null)
CustomEditorTemplate DataTemplate? 宿主注册的自定义编辑器模板,非 null 时 EditorTemplateSelector 优先使用
CustomEditorAttribute Attribute? 宿主自定义编辑器对应的 Attribute 实例,模板 code-behind 可读取参数
FileFilter string 文件过滤器
EnumValues Array 枚举值列表
StandardValues List<object> 下拉列表标准值
IsStandardValuesExclusive bool 下拉列表是否排他
SliderMinimum/Maximum/Step double 滑块参数
SliderShowNumberBox bool 滑块旁是否显示可编辑数字框(取代数值标签)
IsFormulaEnabled bool 是否启用公式绑定
FormulaString string 公式字符串
FormulaTreeProvider IFormulaTreeProvider 公式树提供者
MultiSelectProvider IMultiSelectProvider 多选列表提供者
ChildProperties List<PropertyItem> 子属性列表
ResetCommand ICommand 重置命令
BrowseFileCommand ICommand 浏览文件命令
BrowseDirectoryCommand ICommand 浏览目录命令
EditCollectionCommand ICommand 编辑集合命令
ButtonText string 按钮文本([Button];为空时回退到 DisplayName)
ButtonClickCommand ICommand 按钮点击命令([Button]
CustomEditorType 枚举
说明
None 无自定义编辑器(按类型自动选择)
FilePath 文件路径选择器
DirectoryPath 目录路径选择器
Collection 集合编辑器
Expandable 可展开对象
Slider 数值滑块
DropDown TypeConverter 下拉列表
Formula 公式绑定
MultiSelect 多选列表
Button 命令按钮([Button]
Dictionary 字典编辑器(IDictionary
Color 颜色编辑器(Color / SolidColorBrush

FormulaTreeNode 类

命名空间: PropertyGridLib.Controls

属性 类型 说明
Header string 节点显示文本
Formula string 选中后填入的公式字符串(仅叶子节点需要设置)
Children List<FormulaTreeNode> 子节点列表
FormulaType Type? 叶子节点的数值类型(如 typeof(int)),用于 TreeView 弹窗显示类型提示
TypeHint string 自动从 FormulaType 生成的类型提示(如 (int)(string)),宿主也可直接覆盖

LocalizationManager 类

命名空间: PropertyGridLib.Localization

成员 类型 说明
CurrentLanguage Language 当前语言(默认 ZhCN
LanguageChanged event EventHandler 语言切换事件
GetString(key) string 获取本地化字符串
GetString(key, args) string 获取本地化字符串(带格式化参数)

依赖项

依赖 版本 说明
.NET Framework 4.8+ 支持的目标框架之一
.NET 6.0/7.0/8.0-windows 支持的现代框架
HandyControl 3.5.1 官方 HandyControl UI 控件库
System.Text.Json 6.0.0+ 仅 .NET Framework 4.8 需要(.NET 6+ 内置)

版本兼容性:

  • System.Text.Json 向上兼容到 16.0+
  • 你的项目可以使用更高版本的 System.Text.Json,不会产生冲突
  • .NET 6/7/8 使用内置版本,无需额外引用

更新日志

v1.2.0(2026-09-07)

✨ 新增功能
  • 🔌 宿主自定义编辑器扩展机制PropertyGrid.CustomEditors 静态字典 + RegisterEditor<TAttr>(template) 泛型方法;宿主只需定义 Attribute + DataTemplate + 一行注册,PropertyGrid 即可渲染自定义编辑器;框架对此 Attribute 一无所知。新增 PropertyItem.CustomEditorTemplate / CustomEditorAttribute 属性,EditorTemplateSelector 优先返回宿主模板,宿主甚至可覆盖框架内置编辑器
  • 🎨 颜色编辑器:自动识别 Color / Color? / SolidColorBrush 类型(内置 IsColorType 检测在 CanExpand 之前拦截,防止 Color struct 被误判为可展开对象);显示为颜色预览块(16×16 + #RRGGBB HEX 文本)+ "..." 按钮(紧凑居左,与 FilePath/Dictionary 编辑器风格统一);点击弹出 HandyControl ColorPickerStaysOpen=true + 手动 Window.PreviewMouseDown + IsInVisualTree 外部点击检测,支持拖动 Slider 不丢失焦点;"确定"才写入值并关闭,"取消"不改值);HEX 显示:不透明时省略 Alpha 前缀
  • 📁 字典嵌套字典/列表自动弹窗:字典值为集合(IDictionary / IList)时不再内联显示内嵌 PropertyGrid,改为显示 "N 项 + ..." 按钮,点击弹出二级 DictionaryEditorDialog / SimpleCollectionEditorDialog(模态覆盖父级,UI 不会无限拉长);字典值为复杂对象时仍内嵌 PropertyGrid 就地编辑(三种模式互斥)
  • 🌳 FormulaTree 类型提示FormulaTreeNode 新增 FormulaType / TypeHint 属性,TreeView 弹窗叶子节点右侧显示淡蓝色斜体类型名(如 (int)(DateTime));PropertyGrid 新增 ShowTypeHint 依赖属性(默认 true)控制是否显示;公式绑定区同样显示类型提示
  • 🔤 全局字体动态同步PropertyGrid.FontSize / FontFamily 通过 DependencyPropertyDescriptor 实时监听,变化时自动更新 GlobalFontSize / GlobalFontFamily;所有 Dialog(字典、集合、文件夹、颜色)打开时读取最新全局值,FontSize/FontFamily 跟随宿主变化
  • 📁 FolderBrowserDialog 主题修复:所有文字控件显式指定 Foreground="{DynamicResource PrimaryTextBrush}" + Background="{DynamicResource RegionBrush}",深色主题下不再出现白字黑底问题;TreeView/TreeViewItem 选中/悬停触发器适配深浅色主题
  • 🎨 ColorPicker 弹窗图标适配:FolderBrowserDialog 文件夹图标从 emoji 改为 Material Design 风格 Path 图标(Fill="{DynamicResource PrimaryBrush}"),选中时 Foreground 自动变白
🐛 缺陷修复
  • HandyControl 主题切换失效:原 SetAppSkinis HandyControl.Themes.Theme 匹配不到 App.xaml 里通过 URI 加载的 Theme.xaml(那是 ResourceDictionary,不是 Theme 实例),导致替换 MergedDictionaries 时 idx=-1 每次 Add 新 Theme 到末尾,旧的还留着,第二次切换资源冲突——现改为直接改 Theme.Skin 属性(HC 官方推荐方式),白→黑→白双向切换均正常
  • 颜色编辑器选完值不生效:原 XAML 双向绑定 ValueStringColorToBrushConverter,但 WPF 调 ConvertBacktargetType=object(因为 PropertyItem.Value 是 object),类型检查全挂;现改 code-behind,在 Confirmed 事件里根据 EffectivePropertyType 做正确类型转换(Color/SolidColorBrush/Color?
  • ColorPicker 弹窗 Slider 拖动时消失StaysOpen=false 时 HC ColorPicker 内部 Slider 的 MouseCapture 状态变化导致 Popup 误判"鼠标在外部"——现改为 StaysOpen=true + 手动 Window.PreviewMouseDown + IsInVisualTree 检测外部点击
📦 新增公共类型
  • PropertyGridLib.Controls.ColorEditControl — 颜色编辑器 UserControl(弹出 HC ColorPicker)
  • PropertyGridLib.Controls.ColorToBrushConverter — Color ↔ SolidColorBrush 双向转换器
  • PropertyGridLib.Controls.FormulaTreeNode.FormulaType / TypeHint — FormulaTree 类型提示
🔄 框架侧删除
  • PropertyGridLib.Attributes.UnitEditorAttribute(框架内置的单位编辑器特性)
  • CustomEditorType.Unit 枚举值
  • PropertyItem.UnitOptions / SelectedUnit / UnitDisplayValue 属性
  • ❌ Generic.xaml 里的 UnitTemplate
  • 原因:宿主扩展机制 RegisterEditor<TAttr>(template) 可完全替代,框架内置编辑器与宿主扩展功能重复
🔄 向后兼容性
  • ✅ 现有公共 API 全部保留(新增成员不影响旧代码)
  • ⚠️ PropertyGrid.FontSize / FontFamily 现在会自动同步到全局值(之前只在 OnApplyTemplate 同步一次),若宿主依赖"全局值独立于实例值"的行为需注意
  • ⚠️ 字典编辑器嵌套值的渲染模式变化:集合类型值从"内嵌 PropertyGrid"改为"弹窗",UI 更紧凑但编辑交互从就地改为弹窗;复杂对象值仍保持内嵌模式

v1.1.2(2026-09-04)

✨ 新增功能
  • 命令按钮编辑器 [Button]:属性标记 [Button("文本", nameof(方法))] 后渲染为按钮;点击时①反射调用宿主对象上的 Action 方法(支持无参或单参),②触发 PropertyItem.ButtonClickedPropertyGrid.ButtonClicked 事件;Text 为空时回退到 DisplayName(随多语言切换)。按钮默认始终可点,仅显式 [ReadOnly(true)] 时禁用
  • 全局按钮事件 PropertyGrid.GlobalButtonClicked(静态):任何 PropertyGrid 实例(包括集合/字典编辑器内嵌的网格、弹窗内的网格)触发 [Button] 都会引发,宿主订阅一次即可跨窗口/跨层级统一响应
  • 字典编辑器(IDictionary):自动识别 IDictionary / IDictionary<,> 属性(无需特性,[CollectionEditor] 标在字典上也会路由到字典编辑器);提供弹窗 DictionaryEditorDialog 与内联 DictionaryEditorControl(当 SelectedObject 直接是字典时自动内联);键、值各自支持简单类型(文本框 + 类型转换)或复杂对象(内嵌 PropertyGrid,可继续向下嵌套集合/字典);键值均为对象时左右各占 50%;支持增/删/改、重复键校验、实时写回原字典
  • 多框架:新增 .NET Core 3.0 / 3.1 目标(实际目标:net48 / netcoreapp3.0 / netcoreapp3.1 / net6.0-windows / net7.0-windows / net8.0-windows)
🐛 缺陷修复
  • 字典 "..." 按钮无响应:此前字典被 ICollection 识别为集合,但 EditCollection 仅处理 IList,导致点击编辑按钮无反应——现按 IDictionary 路由到字典编辑器
  • 嵌套网格中按钮失效:集合/字典编辑器内嵌 PropertyGrid 的按钮点击无法上抵宿主、且 Action 修改不刷新——现通过全局事件 + 点击后网格自刷新解决
  • 字典编辑器描述栏白条:内嵌网格 ShowDescription 不一致导致底部描述栏折叠后露出 80px 白色空行——现键/值网格统一显示描述栏
📦 新增公共类型
  • PropertyGridLib.Attributes.ButtonAttribute
  • PropertyGridLib.Controls.PropertyButtonClickEventArgs
  • PropertyGridLib.Controls.DictionaryEditorControl(内联字典编辑器 UserControl,ItemsIDictionary
  • PropertyGridLib.Dialogs.DictionaryEditorDialog
  • CustomEditorType 新增枚举值:ButtonDictionary
🔄 向后兼容性
  • ✅ 现有公共 API 与行为全部保留,仅新增成员
  • ⚠️ IPropertyItem 接口新增了 ButtonText / ButtonClickCommand / ButtonClicked(库内仅 PropertyItem 实现该接口;若宿主自行实现过该接口,需补充这几个成员)
  • ⚠️ .NET Core 3.0/3.1 目标仍为 Windows 专用(WPF 不支持 Linux/macOS);因其 TFM 无 -windows 后缀,跨平台项目误装后运行时才会报错,请注意

v1.1.1(2026-09-01)

✨ 新增功能
  • PropertyGrid 集合模式(内联显示):当 SelectedObjectIList 类型时,自动切换为集合编辑模式,内联显示编辑器(无需弹出对话框)
  • 集合编辑器 UserControl 化CollectionEditorControl(复杂对象列表)和 SimpleCollectionEditorControl(简单类型列表)可作为独立 UserControl 嵌入任意容器
  • FormulaBindingControl 独立使用:新增 TreeItemsSource / FormulaTreeProvider 依赖属性,支持脱离 PropertyGrid 直接设置公式树数据源
🔧 技术改进
  • PropertyGrid 模板新增 PART_CollectionEditorHost / PART_NormalHost 双区域切换机制
  • 集合元素类型自动检测:简单类型(primitives、enum、string、decimal、DateTime、TimeSpan、Guid)→ SimpleCollectionEditor,复杂类型 → CollectionEditor
  • FormulaBindingControl.LoadTreeItems() 三级优先级:TreeItemsSource → FormulaTreeProvider → PropertyItem DataContext
  • 集合编辑器控件直接操作源 IList 实例,每次操作后立即同步
📦 新增公共类型
  • PropertyGridLib.Controls.CollectionEditorControl — 复杂对象列表编辑器 UserControl
  • PropertyGridLib.Controls.SimpleCollectionEditorControl — 简单类型列表编辑器 UserControl
🔄 向后兼容性
  • ✅ 所有现有公共 API 保持不变
  • ✅ PropertyGrid 正常对象绑定行为不受影响
  • ✅ FormulaBindingControl 在 PropertyGrid 内嵌用法不受影响

许可证

MIT License

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net6.0-windows7.0 is compatible.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net7.0-windows7.0 is compatible.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net8.0-windows7.0 is compatible.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 is compatible.  netcoreapp3.1 is compatible. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 41 9/7/2026
1.1.2 46 9/4/2026
1.1.0 96 8/28/2026
1.0.9.1 98 8/28/2026 1.0.9.1 is deprecated because it has critical bugs.
1.0.9 99 8/28/2026 1.0.9 is deprecated because it has critical bugs.
1.0.8-official-hc.1 77 8/28/2026 1.0.8-official-hc.1 is deprecated because it has critical bugs.
1.0.7 90 8/27/2026
1.0.6 111 8/15/2026
1.0.5 100 8/5/2026
1.0.3 114 7/29/2026