OpenRobot.Framework.FormControl 1.2.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package OpenRobot.Framework.FormControl --version 1.2.2
                    
NuGet\Install-Package OpenRobot.Framework.FormControl -Version 1.2.2
                    
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="OpenRobot.Framework.FormControl" Version="1.2.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OpenRobot.Framework.FormControl" Version="1.2.2" />
                    
Directory.Packages.props
<PackageReference Include="OpenRobot.Framework.FormControl" />
                    
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 OpenRobot.Framework.FormControl --version 1.2.2
                    
#r "nuget: OpenRobot.Framework.FormControl, 1.2.2"
                    
#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 OpenRobot.Framework.FormControl@1.2.2
                    
#: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=OpenRobot.Framework.FormControl&version=1.2.2
                    
Install as a Cake Addin
#tool nuget:?package=OpenRobot.Framework.FormControl&version=1.2.2
                    
Install as a Cake Tool

OpenRobot.Framework.FormControl

.NET NuGet OpenRobot.Framework.FormControl 是一个基于 .NET 8.0 WPF 的动态表单构建与渲染引擎。通过数据驱动的方式,将 FormSchema 定义自动转换为完整的 WPF 表单界面,支持 12 种控件类型、级联下拉、条件显示、跨字段校验和多列布局。

特性

  • 数据驱动 — 通过 FormSchema 数据定义自动生成表单,无需手写 XAML
  • 12 种控件类型 — 从基础 TextBox 到树形下拉 ComboxTree,覆盖常见输入场景
  • 级联下拉 — 支持 N 层级联,两种数据模式(静态映射 CascadingOptions / 动态接口 IDataSourceProvider)
  • 条件显示/启用 — 基于 DynamicExpresso 表达式的动态可见性与启用控制
  • 跨字段校验 — 支持 Password == ConfirmPassword 等跨字段规则
  • 多列布局 — 灵活的 Grid 布局,支持 ColSpan/RowSpan 自定义
  • Bootstrap 风格 — 默认 Bootstrap .form-control/.form-select 风格的 UI 呈现,浅色/深色双主题
  • 深色主题 — 内置 DarkBootstrapStyle,一键切换全局配色,所有控件自动适配
  • 国际化 I18NI18N.Key|fallback 格式自动解析,Label / ErrorMessage / 校验消息全链路支持
  • 三段式工厂Init → MakeReadOnly → Builder 清晰的初始化流程
  • EAV 模式集成 — 与 OpenRobot.Framework.DynamicModel 配合,支持动态属性 CRUD
  • 表达式引擎 — 内置 DynamicExpresso,支持 VisibleWhenEnableWhenValidateExpression

快速开始

1. 定义表单 Schema

using OpenRobot.Framework.FormControl.Model;

var schema = new FormSchema
{
    Title = "用户注册",
    Columns = 2,
    PropControls = new List<FormItemControlNode>
    {
        new FormItemControlNode
        {
            FormItem = new FormItemNode { PropName = "Name", Label = "姓名", Value = "" },
            AdvNode = new FormItemAdvNode { ElementType = FormItemElementType.TextBox, IsRequired = true, Placeholder = "请输入姓名" }
        },
        new FormItemControlNode
        {
            FormItem = new FormItemNode { PropName = "Age", Label = "年龄", Value = "" },
            AdvNode = new FormItemAdvNode
            {
                ElementType = FormItemElementType.NumTextBox,
                IsRequired = true,
                ValidateExpression = "int.Parse(Value) >= 0 && int.Parse(Value) <= 150",
                ValidateErrorMessage = "年龄必须在 0-150 之间"
            }
        },
        new FormItemControlNode
        {
            FormItem = new FormItemNode { PropName = "Gender", Label = "性别", Value = "male" },
            AdvNode = new FormItemAdvNode { ElementType = FormItemElementType.RadioButton },
            Options = new List<FormOptionItem>
            {
                new FormOptionItem { Key = "male", Value = "男" },
                new FormOptionItem { Key = "female", Value = "女" }
            }
        },
        new FormItemControlNode
        {
            FormItem = new FormItemNode { PropName = "IsActive", Label = "是否激活", Value = "true" },
            AdvNode = new FormItemAdvNode { ElementType = FormItemElementType.SwitchButton }
        }
    }
};

2. 渲染表单

using OpenRobot.Framework.FormControl.Core;

var engine = new FormEngine();
engine.LoadSchema(schema);
engine.Render(hostPanel);  // hostPanel 是 WPF Panel 容器

3. 获取表单值

var values = engine.GetFormValues();
// { "Name": "张三", "Age": "28", "Gender": "male", "IsActive": "true" }

// 校验
var result = engine.ValidateForm();
if (!result.IsValid) { /* 处理错误 */ }

4. 使用工厂类(EAV 模式)

// 三段式
var factory = new FormEngineFactory();
factory.Init(propDetails);   // 准备数据,自动识别 bool→SwitchButton
factory.MakeReadOnly();      //(可选)标记全部只读
factory.Builder();           // 构建引擎
factory.Engine!.Render(panel);

// 获取值
var values = factory.GetFormValues();
var dto = factory.GetCreateOrUpdateDto<MyDto>();
var changes = factory.GetChangeUpdateProps();  // 差异对比

级联下拉示例

模式 A:静态映射(栏目→子栏目)

new FormItemControlNode
{
    FormItem = new FormItemNode { PropName = "Category", Label = "栏目", Value = "" },
    AdvNode = new FormItemAdvNode { ElementType = FormItemElementType.ComboBox, Placeholder = "请选择栏目" },
    Options = new List<FormOptionItem>
    {
        new FormOptionItem { Key = "tech", Value = "技术" },
        new FormOptionItem { Key = "life", Value = "生活" }
    }
},
new FormItemControlNode
{
    FormItem = new FormItemNode { PropName = "SubCategory", Label = "子栏目", Value = "" },
    AdvNode = new FormItemAdvNode
    {
        ElementType = FormItemElementType.ComboBox,
        DependsOn = { "Category" },
        VisibleWhen = "Category != \"\""
    },
    CascadingOptions = new Dictionary<string, List<FormOptionItem>>
    {
        ["tech"] = { new FormOptionItem { Key = "prog", Value = "编程语言" }, new FormOptionItem { Key = "db", Value = "数据库" } },
        ["life"] = { new FormOptionItem { Key = "food", Value = "美食" }, new FormOptionItem { Key = "travel", Value = "旅行" } }
    }
}

模式 C:动态数据源

// 注入自定义 Provider
engine.DataSourceProvider = new MyDataSourceProvider();

// Schema 中设置服务标识符
new FormItemControlNode
{
    FormItem = new FormItemNode { PropName = "City", Source = "GetCitiesByProvince" },
    AdvNode = new FormItemAdvNode
    {
        ElementType = FormItemElementType.ComboBox,
        DependsOn = { "Province" }
    }
}

// 自定义 Provider
public class MyDataSourceProvider : IDataSourceProvider
{
    public async Task<List<FormOptionItem>> FetchAsync(string source, Dictionary<string, object> p)
    {
        return source switch
        {
            "GetCitiesByProvince" => await _db.Cities
                .Where(c => c.ProvinceCode == p["Province"]?.ToString())
                .Select(c => new FormOptionItem { Key = c.Code, Value = c.Name })
                .ToListAsync(),
            _ => new List<FormOptionItem>()
        };
    }
}

条件显示/隐藏

new FormItemControlNode
{
    FormItem = new FormItemNode { PropName = "HasCompany", Label = "是否有公司", Value = "false" },
    AdvNode = new FormItemAdvNode { ElementType = FormItemElementType.SwitchButton }
},
new FormItemControlNode
{
    FormItem = new FormItemNode { PropName = "CompanyName", Label = "公司名称", Value = "" },
    AdvNode = new FormItemAdvNode
    {
        ElementType = FormItemElementType.TextBox,
        DependsOn = { "HasCompany" },
        VisibleWhen = "HasCompany == true"
    }
}

跨字段校验

var schema = new FormSchema
{
    PropControls = new List<FormItemControlNode>
    {
        new FormItemControlNode
        {
            FormItem = new FormItemNode { PropName = "Password", Label = "密码" },
            AdvNode = new FormItemAdvNode { ElementType = FormItemElementType.Password, IsRequired = true }
        },
        new FormItemControlNode
        {
            FormItem = new FormItemNode { PropName = "ConfirmPassword", Label = "确认密码" },
            AdvNode = new FormItemAdvNode { ElementType = FormItemElementType.Password, IsRequired = true }
        }
    },
    CrossValidations = new List<FormCrossValidation>
    {
        new FormCrossValidation
        {
            Fields = { "ConfirmPassword", "Password" },
            Expression = "Password == ConfirmPassword",
            ErrorMessage = "两次输入的密码不一致"
        }
    }
};

控件类型一览

控件 ElementType 特性
文本框 TextBox Bootstrap 风格,支持只读/启用/占位符
数字框 NumTextBox 数字输入限制,自定义表达式校验
密码框 Password 密码掩码
下拉框 ComboBox Bootstrap form-select,级联选项刷新
日期选择器 DatePicker 日历面板
复选框 CheckBox 必填勾选校验
单选按钮组 RadioButton 横向流式排列
开关按钮 SwitchButton iOS 风格滑块动画,OnText/OffText 可配
按钮组 ButtonGroup btn-group 风格,首尾圆角
树形下拉 ComboxTree ComboBox+Popup+TreeView
自定义 Custom 反射加载外部控件
多行文本 TextArea Meta["Rows"] 控制行数

CheckBox 多选组 (1.2.2)

CheckBox 支持三种模式,由 Options 数量自动切换:

单选模式(0~1 个 Option):

Options = { new FormOptionItem { Key = "agree", Value = "我已阅读", IsSelected = true } }
// → 单个 CheckBox,值 "true"/"false"

多选横向(> 1 个 Option,默认):

Options = {
    new() { Key = "sport", Value = "运动", IsSelected = true },
    new() { Key = "music", Value = "音乐", IsSelected = false },
    new() { Key = "read", Value = "阅读", IsSelected = true }
}
// → 多个 CheckBox,WrapPanel 横向流式排版,值 "sport,read"

多选纵向(> 1 个 Option + Meta["CheckBoxLayout"] = "Vertical"):

Options = {
    new() { Key = "email", Value = "邮件通知", IsSelected = true },
    new() { Key = "sms", Value = "短信通知", IsSelected = false }
},
Meta = { ["CheckBoxLayout"] = "Vertical" }
// → StackPanel 竖向排列

必填校验:单选需值为 "true",多选至少勾选一项(值非空)。

表达式语法

所有条件表达式基于 DynamicExpresso.Core

"Province != \"\""
"Category == \"tech\" && SubCategory != \"\""
"HasCompany == true"
"Password == ConfirmPassword"
"int.Parse(Value) >= 0 && int.Parse(Value) <= 150"
"String.IsNullOrEmpty(Province) == false"

变量名自动绑定到所有字段的当前值,"true"/"false" 字符串自动转为布尔类型。

布局

new FormSchema { Columns = 2 };  // 全局两列

// 单项控制布局
new FormItemControlNode
{
    CustomLayout = new FormItemCustomLayout
    {
        ColSpan = 2,                              // 跨两列
        LabelPosition = FormLabelPosition.Top,     // 标签在控件上方
        ControlWidth = 200                         // 固定控件宽度
    }
}

国际化(I18N)

所有标签、校验消息、开关文本支持 I18N.Key|fallback 格式自动解析:

// 定义时使用 i18n 格式
new PropDetailNode(1, "ECode", "50")
{
    Title = "I18N.CuttingDataTemplate.ECode|错误码",
    ErrorMessage = "I18N.CuttingDataTemplate.ENoError|范围:1-1000"
}

// 运行时切换语言
FormEngineFactory.SetI18nResources(new Dictionary<string, string>
{
    ["CuttingDataTemplate.ECode"] = "Error Code",
    ["CuttingDataTemplate.ENoError"] = "Range: 1-1000"
});

解析优先级:字典命中 → | 后的回退文本 → 原始字符串。默认加载中文资源(校验消息、开关文本等),详见 I18NHelper

深色主题

内置浅色/深色双主题,一键切换所有控件全局配色:

// 深色主题
FormEngineFactory.SetDarkTheme();

// 浅色主题(默认)
FormEngineFactory.SetLightTheme();

FormDefaults 提供了 30+ 设计令牌(背景、文字、边框、悬停、选中、阴影等),所有 XAML 控件通过 x:Static 引用,切换后重建引擎即可生效。

自定义主题:

// 继承 BaseFormElementBuilder,重载颜色令牌
public class CustomTheme : BaseFormElementBuilder
{
    public override Color PrimaryColor => Color.FromRgb(99, 153, 255);
    // ...
}

// 注入引擎
engine.StyleProvider = new CustomTheme();

系统要求

  • .NET 8.0 或更高版本(Windows)
  • WPF 运行时

依赖项

包名 版本 用途
DynamicExpresso.Core 2.16.0 表达式求值引擎
OpenRobot.Framework.DynamicModel >= 1.3.7 EAV 属性模型

构建

dotnet build common/OpenRobot.Framework.FormControl/OpenRobot.Framework.FormControl.csproj

设计文档

  • docs/form-control/desing-custom.md — 完整设计与实施规范
  • docs/superpowers/specs/2026-06-23-cascading-dropdown-design.md — 级联下拉设计

许可证

Copyright © OpenRobot

版本历史

1.2.1 (2026-06-25)

  • 表单行垂直居中:Label 与控件改为居中对齐(原顶部对齐)
  • 错误消息模式FormEngineFactory.SetErrorMessageMode() 支持 Inline(占高度,默认)和 Compact(无错误时高度为 0)两种模式,11 个控件 SetValid 统一调用 GetErrorHiddenVisibility()

1.2.2 (2026-07-02)

  • CheckBox 多选组:Options > 1 时自动切换为多选模式,IsSelected 控制每项状态,值格式 "key1,key3"
  • CheckBox 布局控制Meta["CheckBoxLayout"] 支持 "Horizontal"(默认,WrapPanel 流式)和 "Vertical"(StackPanel 纵向)
  • 多选校验:必填时只要勾选至少一项即通过,不再要求值等于 "true"

1.2.1 (2026-07-01)

  • 表单行纵向结构重构FormItemRow 拆为 Left/Top 各自含独立 ErrorLabel,Tag 改用 Run 嵌入 TextBlock
  • 行级错误提示:错误显示统一到 FormItemRow.ShowError/ClearErrorValidateForm 不再触发控件级 UpdateState
  • ErrorMessageMode 生效:默认 Inline(Hidden 占高度),构造时 ClearError() 同步模式

1.2.0 (2026-06-25)

  • 深色主题
    • 新增 Element/DarkBootstrapStyle.cs,重载 PrimaryColor/DangerColor/BorderColor 等全部设计令牌
    • FormEngineFactory.SetDarkTheme() / SetLightTheme() 静态方法一键切换,静态构造默认浅色
    • Builder() 自动将当前主题注入 engine.StyleProvider
  • FormDefaults 重构
    • 所有 static readonly 改为 static,支持运行时替换
    • 令牌从 8 个扩展到 30+:InputBackground/InputForegroundPopupBackgroundItemHoverBackground/ItemSelectedBackgroundSwitchOnBrush/SwitchOffBrush/SwitchThumbBrushButtonGroupNormalBg/ButtonGroupSelectedBg/ButtonGroupHoverBgArrowColorBrushDropShadowColor
  • XAML 控件颜色规范化
    • 11 个 XAML 控件文件全部改为 x:Static 引用 FormDefaults,免除硬编码颜色
    • FormTextBoxControl — Background/Foreground/BorderBrush/CaretBrush
    • FormComboBoxControl — 主背景/文字/下拉面板/ComboBoxItem 触发器(悬停/选中)
    • FormSwitchButtonControl — 开关轨道/滑块/标签文字
    • FormCheckBoxControl — 前景色
    • FormNumTextBoxControl / FormTextAreaControl — 背景/边框
    • 7 个控件的 ErrorLabel Foreground → FormDefaults.DangerBrush
  • Code-behind 主题适配
    • FormSwitchButtonControl.ApplyState() — 硬编码颜色改为 FormDefaults.SwitchOnBrush/SwitchOffBrush
    • FormButtonGroupControl — 7 个 static readonly 字段改为引用 FormDefaultsBrushes.WhiteFormDefaults.InputBackground
    • FormRadioButtonGroupControl.SetItems() — 新增 Foreground = FormDefaults.InputForeground
    • FormTextBoxControl.SetInvalid() — 硬编码红色改为 FormDefaults.DangerBrush
  • LabelForeground 显式赋值FormEngine.CreateFormItemRow 构建时设置 LabelForeground = FormDefaults.LabelForeground,避免 DP 静态注册捕获陈旧主题值
  • 国际化完善
    • FormEngineFactory.Init 两个重载的 LabelErrorMessage 统一过 I18NHelper.Resolve
    • BaseFormBuilder.IsVail() 校验消息(必填/正则/表达式异常)全部通过 I18NHelper.Resolve 解析
  • FormItemNode 模型清理:移除 SourceParamTemplate 字段

1.1.0 (2026-06-24)

  • 国际化(I18N)支持
    • 新增 I18N/I18NHelper.cs 全局静态类,支持 I18N.Key|fallback 格式的字符串解析
    • Label 标签自动通过 I18NHelper.Resolve 解析,如 "I18N.User.Name|用户名" 优先查字典
    • 校验消息(必填/格式错误)通过 i18n 通道解析,字典未命中时回退 | 后的默认文本
  • 默认中文资源I18NHelper 静态构造自动加载中文资源(校验消息、开关文本、占位符),使用者无需手动初始化即可工作
  • 全局资源覆写FormEngineFactory.SetI18nResources(dict) 静态方法可随时替换字典,支持运行时切换语言
  • 管理入口FormEngineFactory.AddI18nResource(key, value) 静态方法支持逐条添加/更新资源

1.0.0 (2026-06-23)

  • 首次发布:基于数据驱动的 WPF 动态表单构建与渲染引擎
  • 12 种控件类型:TextBox、NumTextBox、Password、ComboBox、DatePicker、CheckBox、RadioButton、SwitchButton、ButtonGroup、ComboxTree、Custom、TextArea,均采用 Bootstrap 表单风格 UI
  • 级联下拉:支持 N 层级联,模式 A(CascadingOptions 静态字典映射)+ 模式 C(IDataSourceProvider 动态接口),ComboBox 在依赖字段值变化时自动刷新选项
  • 条件显示/启用:基于 DynamicExpresso 表达式引擎的 VisibleWhen / EnableWhen 条件控制,字符串 "true"/"false" 自动大小写不敏感转为布尔类型
  • 跨字段校验:FormSchema.CrossValidations 支持 Password == ConfirmPassword 等跨字段规则,错误信息挂到首个字段并更新控件视觉状态
  • 多列布局:Grid 布局,支持 ColSpan/RowSpan/LabelPosition 自定义
  • Bootstrap 表单风格:ComboBox 自定义 ControlTemplate(SVG 箭头、聚焦蓝色外发光、悬停边框加深、禁用灰背景)、行间距紧凑化
  • 表单引擎工厂 FormEngineFactory
    • 三段式流程 Init → MakeReadOnly → Builder,数据准备与引擎构建分离
    • Init(PropDetailNode[]) — EAV 模式,自动识别 bool 值字符串转为 SwitchButton
    • Init(IDynamicTemplateDto) — DTO 反射模式,根据属性特性和类型推断控件
    • GetFormValues() — 获取 Dictionary<string, string>
    • GetCreateOrUpdateDto<T>() — 结构化 DTO 转换,支持 [CustomPropMap] 映射和 PropDetailNode 元数据填充
    • GetChangeUpdateProps() — 与初始值差异对比,返回 GeneralPropUpdateDto
    • IsVaild() / GetValidationResult() — 表单校验
    • UserCustomControlEvent — 自定义控件类型和选项的事件钩子
  • FormSchema 模型简化:统一为 PropControls 单列表,移除 Items + CustomControls 冗余双列表结构
  • 级联式可见性构建:依赖字段值变化时自动构建并插入从未渲染的控件,_rowControls 字典缓存行控件保证插入顺序正确
  • 重入保护_isRendering 标志防止 Render 递归/重入
Product Compatible and additional computed target framework versions.
.NET net8.0-windows7.0 is compatible.  net9.0-windows was computed.  net10.0-windows 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.3.1 122 7/9/2026
1.3.0 108 7/8/2026
1.2.2 109 7/6/2026
1.2.1 109 6/26/2026
1.2.0 113 6/25/2026
1.1.0 109 6/24/2026
1.0.0 126 6/23/2026