OpenRobot.Framework.FormControl
1.2.2
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
<PackageReference Include="OpenRobot.Framework.FormControl" Version="1.2.2" />
<PackageVersion Include="OpenRobot.Framework.FormControl" Version="1.2.2" />
<PackageReference Include="OpenRobot.Framework.FormControl" />
paket add OpenRobot.Framework.FormControl --version 1.2.2
#r "nuget: OpenRobot.Framework.FormControl, 1.2.2"
#:package OpenRobot.Framework.FormControl@1.2.2
#addin nuget:?package=OpenRobot.Framework.FormControl&version=1.2.2
#tool nuget:?package=OpenRobot.Framework.FormControl&version=1.2.2
OpenRobot.Framework.FormControl
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,一键切换全局配色,所有控件自动适配 - 国际化 I18N —
I18N.Key|fallback格式自动解析,Label / ErrorMessage / 校验消息全链路支持 - 三段式工厂 —
Init → MakeReadOnly → Builder清晰的初始化流程 - EAV 模式集成 — 与
OpenRobot.Framework.DynamicModel配合,支持动态属性 CRUD - 表达式引擎 — 内置 DynamicExpresso,支持
VisibleWhen、EnableWhen、ValidateExpression
快速开始
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/ClearError,ValidateForm不再触发控件级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/InputForeground、PopupBackground、ItemHoverBackground/ItemSelectedBackground、SwitchOnBrush/SwitchOffBrush/SwitchThumbBrush、ButtonGroupNormalBg/ButtonGroupSelectedBg/ButtonGroupHoverBg、ArrowColorBrush、DropShadowColor等
- 所有
- XAML 控件颜色规范化:
- 11 个 XAML 控件文件全部改为
x:Static引用FormDefaults,免除硬编码颜色 FormTextBoxControl— Background/Foreground/BorderBrush/CaretBrushFormComboBoxControl— 主背景/文字/下拉面板/ComboBoxItem 触发器(悬停/选中)FormSwitchButtonControl— 开关轨道/滑块/标签文字FormCheckBoxControl— 前景色FormNumTextBoxControl/FormTextAreaControl— 背景/边框- 7 个控件的 ErrorLabel Foreground →
FormDefaults.DangerBrush
- 11 个 XAML 控件文件全部改为
- Code-behind 主题适配:
FormSwitchButtonControl.ApplyState()— 硬编码颜色改为FormDefaults.SwitchOnBrush/SwitchOffBrushFormButtonGroupControl— 7 个static readonly字段改为引用FormDefaults,Brushes.White→FormDefaults.InputBackgroundFormRadioButtonGroupControl.SetItems()— 新增Foreground = FormDefaults.InputForegroundFormTextBoxControl.SetInvalid()— 硬编码红色改为FormDefaults.DangerBrush
- LabelForeground 显式赋值:
FormEngine.CreateFormItemRow构建时设置LabelForeground = FormDefaults.LabelForeground,避免 DP 静态注册捕获陈旧主题值 - 国际化完善:
FormEngineFactory.Init两个重载的Label和ErrorMessage统一过I18NHelper.ResolveBaseFormBuilder.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 值字符串转为 SwitchButtonInit(IDynamicTemplateDto)— DTO 反射模式,根据属性特性和类型推断控件GetFormValues()— 获取Dictionary<string, string>GetCreateOrUpdateDto<T>()— 结构化 DTO 转换,支持[CustomPropMap]映射和PropDetailNode元数据填充GetChangeUpdateProps()— 与初始值差异对比,返回GeneralPropUpdateDtoIsVaild()/GetValidationResult()— 表单校验UserCustomControlEvent— 自定义控件类型和选项的事件钩子
- 三段式流程
- FormSchema 模型简化:统一为
PropControls单列表,移除Items+CustomControls冗余双列表结构 - 级联式可见性构建:依赖字段值变化时自动构建并插入从未渲染的控件,
_rowControls字典缓存行控件保证插入顺序正确 - 重入保护:
_isRendering标志防止 Render 递归/重入
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0-windows7.0 is compatible. net9.0-windows was computed. net10.0-windows was computed. |
-
net8.0-windows7.0
- DynamicExpresso.Core (>= 2.16.0)
- OpenRobot.Framework.DynamicModel (>= 1.3.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.