# WebForm **Repository Path**: xa87/web-form ## Basic Information - **Project Name**: WebForm - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-23 - **Last Updated**: 2026-08-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # NetForms **用 WinForm 的方式开发 Web 应用**:拖控件、改属性、双击写 C# 事件、F5 运行——产出物是浏览器里的 B/S 系统,而不是桌面 exe。 - 后端:C# / .NET 10(ASP.NET Core),事件处理器 100% 写 C# - 前端:Bootstrap 5(仅 CSS,本地打包)+ 原生手写 JS,**不引入任何 JS 框架** - 通信:原生 WebSocket(事件回发 + Patch 增量更新,无 ViewState) > **当前进度**:M1 内核骨架 ✅ + M2 运行时闭环 ✅ + M3 设计器 ✅ 均已完成;设计器 5 项诉求 ✅(按钮圆角/矩形样式与边线开关、未编译预览确认、JSON 中文不转义、PaintBox 字段、控件帮助文档)。 > M2 含三项稳定性补完:**会话空闲超时回收**、**断线重连 seq 增量补发**、**Patch 失败整窗体兜底刷新**。 > M3 设计器:三栏可视化开发(工具箱/画布/属性窗口)、拖拽 + 8 手柄缩放 + 吸附参考线、**单文件模型**(`.form_json` + 主 `.cs` 哨兵注释包裹设计器区块)、设计器中 F5 直接运行。 > VS2026 集成骨架 `NetForms.VSExt` 已落地(双击 `.form_json` 在 VS 内开设计器,运行时自动弹 WebView2 预览窗)。 --- ## 一、快速开始 ### 环境要求 1. .NET SDK **10.0.302+**(`dotnet --version` 验证) 2. 桌面 Chrome / Edge(设计器与运行预览推荐) ### 命令行运行(浏览器手动模式,Win / Linux 通用) ```bash cd samples/NetForms.Demo dotnet run ``` 浏览器打开:**http://localhost:5200/run/MainForm** ### 设计模式宿主(NetForms.Studio) `NetForms.Studio` 是独立的设计模式宿主程序(干净设计器环境,不带示例窗体)。可直接启动后浏览器打开首页进入设计器: ```bash # 端口可覆盖;--rootdir 指定「工程根」(含 Forms/ 的目录),--ns 指定默认命名空间 dotnet run --project src/NetForms.Studio --rootdir ./samples/NetForms.Demo http://localhost:5100 ``` 浏览器打开:**http://localhost:5100/designer/MainForm** ### Visual Studio 运行 1. 打开 `NetForms.slnx` 2. 解决方案资源管理器右键 `samples/NetForms.Demo` →「设为启动项目」 3. F5 启动,浏览器手动打开上面的地址 ### Demo 演示内容 | 操作 | 验证的能力 | |------|-----------| | 输入姓名点「问候」 | 事件回发闭环:点击 → 服务器执行 C# → Patch 更新 Label | | 「开始流式」 | 后台线程 `Form.Post` 50ms 实时推点,双曲线滚动 | | 「加载历史 1000 点」 | 批量数据 `SetData` 全量替换 | | 「切换平滑」 | 曲线贝塞尔平滑实时切换 | | 曲线上滚轮 / 拖拽 / Shift+框选 / 双击 | 缩放 / 平移 / 框选放大 / 复位(纯前端交互) | | 点击图例 | 切换该曲线显隐 | 辅助端点:`/nf-metrics`(会话数、空闲阈值 `idleTimeoutSec` 等开发期指标)。 ### 运行时稳定性(M2 补完) | 机制 | 行为 | |------|------| | 空闲回收 | 后台每 60s 扫描,回收 `LastActive` 超过 20min(可配)的会话;前端 15s 心跳持续续命,故仅清「浏览器已断线但服务器未收到关闭帧」的僵尸会话 | | 重连补发 | WS 断线指数退避重连(1s→8s);重连 `bind` 携带客户端最后应用的 `lastSeq`,服务器回放 `_sentLog`(容量 256)中 `seq > lastSeq` 的增量,曲线流式从中断点无缝续传 | | 缺口 / 失败兜底 | 若 `lastSeq` 过旧导致无法连续补发,或客户端 Patch 应用抛异常 → 服务器下发 `resync` / 客户端守卫式整窗体重载,重新拉取首屏 | --- ## 二、设计器说明(M3 ✅ 已交付) 设计器本身是本框架的 Web 应用:`dotnet run` 后浏览器打开 **http://localhost:5200/designer/MainForm**, 即可三栏布局(项目树+工具箱 / 画布 / 属性窗口)拖拽开发。 - 左侧:工具箱(按分类搜索/拖拽/双击添加)+ 大纲树(选中/层级) - 中间:画布(网格点背景、拖放定位、8 手柄缩放、6px 吸附参考线、状态栏坐标) - 右侧:属性窗口(按分类分组的属性编辑器 + 事件页签,事件绑定到代码后置同名方法);底部「?查看控件帮助」按钮可弹悬浮窗查看属性/事件说明 - 顶栏:「保存」(写 `.form_json` + 刷新主 .cs 内的设计器区块)、「运行 F5」(新标签打开 `/run/{form}`) 窗体落盘为 **两件套**(`Xxx.form_json` + `Xxx.cs`)。设计器把「控件字段声明 + `InitializeComponent()`」以哨兵注释包裹写入主 .cs,保存时仅替换该区块、不覆盖用户代码;运行时由 `DesignerSupport.LoadInto` 反序列化并回填字段、接线事件: ```csharp // MainForm.cs(单文件:设计器区块 + 用户代码) public partial class MainForm : Form { // ===== NetForms Designer: begin (auto-generated, do not edit) ===== private Label label1 = null!; private void InitializeComponent() { NetForms.Designer.DesignerSupport.LoadInto(this, "Forms/MainForm.form_json"); } // ===== NetForms Designer: end ===== } ``` ```csharp // MainForm.cs(用户编写:只放事件处理与运行时逻辑) public partial class MainForm : Form { public MainForm() { InitializeComponent(); /* 系列构建、Load 等 */ } private void buttonGreet_Click(object? sender, EventArgs e) => label1.Text = $"你好,{textBox1.Text}!"; } ``` > 设计通道(`/nf-designer-ws`)与运行时通道(`/nf-ws`)**完全隔离**:设计器只维护 `.form_json` 设计模型, > 不执行事件、不生成运行时 Patch;保存后 `dotnet run` 的窗体立即反映设计结果。 --- ## 三、项目结构与目录结构 ``` D:\AI\work\WebForm\ ├─ NetForms.slnx # 解决方案(.NET 10 新格式) ├─ build.bat # 构建 + 发布(产出 Output/) ├─ README.md ├─ docs/ # 规划文档(00 愿景 ~ 16 VS2026 扩展设计) ├─ src/ │ ├─ NetForms.Core/ # 内核:控件模型/元数据/序列化/变更追踪(无 Web 依赖) │ ├─ NetForms.Controls/ # 内置控件:Form/Label/TextBox/Button/Panel/PlotChart/ │ │ # GroupBox/PaintBox/PictureBox/ProgressBar/RadioButton/Switch │ ├─ NetForms.Runtime/ # 运行时:FormSession 事件循环、FormRuntime │ ├─ NetForms.Designer/ # 设计器内核:DesignSession/DesignRuntime/DesignerCodeGenerator/ │ │ # DesignerSupport/DesignerProjectContext │ ├─ NetForms.Hosting/ # ASP.NET 集成:/run、/nf-ws、/nf-designer-ws、/nf-assets、/nf-designer-help │ ├─ NetForms.Studio/ # 设计模式宿主(独立 exe,支持 --rootdir/--ns/端口) │ └─ NetForms.VSExt/ # VS2026 扩展骨架(.vsix,VSSDK 进程内,仅 Windows;未纳入主 slnx) ├─ tools/ │ └─ GenHelp/ # 控件帮助文档生成器(与 DesignerHelpTemplate 同源) ├─ samples/ │ └─ NetForms.Demo/ # 演示应用(含 Forms/ 两件套) ├─ tests/ │ ├─ NetForms.Tests/ # xUnit 单测(当前 10 项) │ └─ e2e-ws-test.js # WS 端到端验证脚本 └─ Output/ # 发布包(git 忽略):NetForms.Studio.exe + lib/*.dll + docs/帮助.html ``` **依赖方向**(严格单向,不允许反向引用): ``` NetForms.Demo ──> Hosting ──> Runtime ──> Controls ──> Core(无任何外部依赖) NetForms.Studio ─> Hosting(复用同一套端点) NetForms.Designer ─> Core / Controls (无 Web 依赖,可独立测试) NetForms.VSExt ─>(外部 VS SDK,仅 Windows;运行期调用 NetForms.Studio 子进程) Tests ──────> Core/Controls/Runtime/Designer ``` | 项目 | 关键类型 | 职责一句话 | |------|----------|-----------| | Core | `Control` / `ChangeTracker` / `FormSerializer` / `ControlRegistry` | 控件树模型与 Patch 数据源 | | Controls | `Form` / `Button` / `PlotChart` / `PlotSeries` … | 各控件属性、事件、`Render()` | | Runtime | `FormSession` / `FormRuntime` | 会话串行事件循环、Patch 生成下发 | | Designer | `DesignSession` / `DesignRuntime` / `DesignerCodeGenerator` / `DesignerSupport` / `DesignerProjectContext` | 设计模型、命令应用、两件套生成、运行时回填、工程上下文 | | Hosting | `NetFormsHostingExtensions` / `WsHandler` / `DesignerWsHandler` / `PageTemplate` / `DesignerHelpTemplate` | HTTP/WS 端点、嵌入静态资源、控件帮助页 | | Studio | `Program` | 设计模式宿主(干净设计器环境,支持 `--rootdir`/`--ns`/端口) | | VSExt | `VsPackage` / `NetFormsEditorFactory` / `NetFormsDesignerPane` / `BackendLauncher` / `PreviewToolWindow` | VS2026 内嵌设计器与预览(仅 Windows) | | GenHelp | `Program` | 由控件注册表实时生成 `设计器控件帮助.html` | | Demo | `MainForm` | `.form_json` 驱动示例 + 曲线三种模式演示 | **内置控件清单**:`Form` / `Label` / `TextBox` / `Button` / `Panel` / `PlotChart` / `GroupBox` / `PaintBox` / `PictureBox` / `ProgressBar` / `RadioButton` / `Switch`。新增控件 = 加 `[NfControl]` + 合规基类即自动进工具箱与帮助文档。 --- ## 四、VS2026 插件集成(NetForms.VSExt) 把网页版设计器「嵌」进 Visual Studio 2026(Windows),让双击 `.form_json` 在 VS 内打开设计器、运行时自动弹出 WebView2 预览窗;浏览器手动模式(Windows / Linux)保持不变。 ### 为什么可行 当前设计器本身就是**自包含的 Web 应用**(ASP.NET Core 后端 + 纯 HTML/JS 前端,走 `/designer/{form}` + WebSocket)。因此 VS 插件 = 用 WebView2 把它「嵌」进 VS 窗口 + 管文件关联 + 管后端进程,**后端同源复用,浏览器手动模式一行都不用改**。 ### 三大组件 | 组件 | 平台 | 职责 | |------|------|------| | `NetForms.VSExt`(.vsix) | 仅 Windows | `.form_json` 文件关联 + 自定义编辑器(WebView2 设计器窗格)+ 后端启动器(子进程生命周期)+ 预览工具窗口(WebView2 承载 `/run`)+ 「在浏览器打开」命令 | | `NetForms.Studio`(现有后端) | 跨平台 | 已支持端口参数;新增 `--rootdir ` 让扩展把 `.form_json` 所在目录当工程根 | | 浏览器手动模式(现有) | Windows / Linux | 启动 Studio + 浏览器即可,零改动 | ### 三种运行模式 1. **VS2026 内嵌(Windows)**:双击 `.form_json` → WebView2 设计器;F5/运行 → WebView2 预览窗自动弹出。 2. **Windows 浏览器手动**:启动 Studio,浏览器打开 `/designer`、`/run`。 3. **Linux 浏览器手动**:同上(.NET 跨平台)。 ### 关键流程 - **双击打开**:扩展取文件名(formName)与目录(RootDir)→ 确保后端带 `--rootdir` 运行 → 打开编辑器窗格 → WebView2 → `/designer/`;WS 连线后拖拽/属性/保存与浏览器模式完全一致。 - **运行预览**:启动后端(若未运行)→ 预览工具窗口 WebView2 → `/run/`;可同时开外部浏览器。 ### 当前状态与下一步 - `src/NetForms.VSExt/` 骨架已落地(VSSDK 进程内,`VsPackage`/`NetFormsEditorFactory`/`NetFormsDesignerPane`/`BackendLauncher`/`FormsRootResolver`/`PreviewToolWindow`/`NetFormsCommands` + `vsixmanifest`/`vsct`/`README`)。 - **未纳入主 `NetForms.slnx`**,避免在缺 VS SDK 的环境破坏主构建 0/0。 - 沙箱无 VS2026,**扩展源码尚未在 VS 中编译**;需在 VS2026 实验实例联调(可能按 VS2026 微调 SDK 版本与 `InstallationTarget` 范围,详见 `src/NetForms.VSExt/README.md`)。 - 设计文档:`docs/16-VS2026扩展设计.md`。 --- ## 五、三层用户使用指南 NetForms 把「做框架 / 搭界面 / 用系统」三类角色清晰分层,各司其职、互不侵入。 ### 1. 最底层开发者(框架 / 控件作者) **角色**:维护或扩展 NetForms 本身——新增控件、加属性/事件、改运行时或设计器内核。 **怎么用**: - 在 `NetForms.Controls` 新建类,标 `[NfControl]`、继承 `Control`、实现 `Render()`。 - 属性标 `[NfProperty]`(可设 `Category`/`DefaultValue`/`Editor`),事件标 `[NfEvent]` → **自动进工具箱、自动进帮助文档**,零额外注册。 - 复杂控件增量更新走 `CustomOp`(名称 + 数据),前端 `nf-runtime.js` 的对应 handler 重放;后台线程更新控件**必须走 `Form.Post`**。 - 外观:通用 `BackColor`/`ForeColor`(`NfColor`,空=继承/透明)、`Font`(`NfFont` 字符串)已在基类;`BorderVisible`/`BorderWidth` 等边线开关在 Button/PaintBox/PictureBox 已示范。 - 必读:`docs/04-控件模型与元数据.md`、`docs/12-内置控件清单.md`、`docs/02-术语与约定.md`。 - 验证:`dotnet build NetForms.slnx -c Release`(要求 **0 警告 0 错误`)、`dotnet test tests/NetForms.Tests`。 ### 2. 中层设计者(界面搭建者) **角色**:用设计器搭出业务窗体的应用开发者。少写/不写框架代码,拖控件、改属性、双击写事件。 **怎么用**(三种入口任选): - **VS2026 内嵌(Windows)**:在 VS 中双击 `.form_json` 即开设计器;运行时自动弹 WebView2 预览窗。 - **浏览器手动(Win / Linux)**:`dotnet run` 后开 `/designer/{form}`;或启动 `NetForms.Studio.exe`(设计模式宿主)后浏览器打开。 - **操作闭环**:从左侧工具箱拖控件入画布 → 右侧属性窗口改属性/绑事件(事件绑到代码后置同名 `Xxx_Click` 方法)→ 顶栏「保存」(写 `.form_json` + 刷新主 `.cs` 设计器区块)→ F5 预览 `/run/{form}`。 - **帮助**:属性窗口底部「?查看控件帮助」按钮弹悬浮窗,或访问 `/nf-designer-help`,列出每个控件的属性/事件说明。 - 编译提示:预览前若工程未编译/过期,设计器会 `confirm` 提示,避免「点了按钮没反应」。 ### 3. 上层使用者(业务终端用户) **角色**:最终使用 B/S 系统的业务人员,只关心功能、不关心 NetForms。 **怎么用**: - 浏览器打开 `/run/{form}` 的网址即可,像用普通网站一样操作(按钮、输入、图表、绘图)。 - **无需安装、无需理解 NetForms**:没有桌面客户端、没有插件;开发者发布后把链接发给使用者即可。 - 对中层设计者:应用发布后把运行地址(如 `http://:/run/MainForm`)交付给使用者即完成交付。 > 分层边界:最底层开发者产出「控件能力」,中层设计者把它拼成「业务窗体」,上层使用者只消费「最终页面」——三者通过 `.form_json` + 主 `.cs` 两件套与 WebSocket 协议解耦。 --- ## 六、运作机制(请求生命周期) ``` 浏览器 服务器(ASP.NET Core 单进程) ────── ────────────────────────── 1. GET /run/MainForm ────────────> 创建 FormSession(控件树驻留内存) 触发 Load 事件 → 渲染控件树 HTML <──────────── 返回首屏页面(含 data-nf-session) 2. nf-runtime.js 启动 ───────────> /nf-ws 建立 WebSocket bind {session} ───────────> 会话绑定通道 → bindAck 3. 点击按钮 ───────────> {type:"event", target, inputs 值快照} 会话事件循环串行执行 C# 处理器 <──────────── {type:"patch", ops:[set/insert/remove/...]} 前端应用 DOM 更新(原子一批) 4. 后台定时器 Form.Post(() => chart.AddPoint(...)) <──────────── {type:"patch", ops:[custom/chartData]} nf.plot.js 增量绘制曲线 ``` 要点: 1. **控件树只在服务器**,浏览器 DOM 是影子(`data-nf-name` 关联)。 2. **事件回发**:每次事件携带输入控件值快照(inputs),处理器里 `textBox1.Text` 就是用户刚输入的值。 3. **改属性即推送**:处理器中 `label1.Text = "..."` 自动进入变更追踪,事件结束统一成一条 Patch 下发。 4. **后台线程更新控件必须走 `Form.Post`**(排入会话事件循环,线程安全)。 --- ## 七、测试与验证 ```bash dotnet test tests/NetForms.Tests # 10 个单测(序列化幂等/变更追踪/曲线缓冲/PaintBox 字段 round-trip…) node tests/e2e-ws-test.js # 运行时 WS 端到端(需 Demo 已运行) # 设计器端到端(需 Demo 已运行,驱动 /nf-designer-ws 的 bind→增删改→保存 闭环): node <(管理器工作区)/e2e-designer.js # 见下方说明 ``` > 设计器 e2e 脚本位于 Node 工作区(`C:/Users/xa87/.workbuddy/binaries/node/workspace/e2e-designer.js`), > 运行前先 `npm install ws`。它会临时修改 `MainForm.form_json` 并随后由调用方还原,不会污染仓库。 --- ## 八、路线图与文档 - `docs/00-愿景与范围.md` — 定位、非目标、成功标准 - `docs/02-术语与约定.md` — 统一术语基线 - `docs/04-控件模型与元数据.md` — 控件开发规范(自定义控件必读) - `docs/12-内置控件清单.md` — PlotChart 完整 API 文档 - `docs/03-设计器界面规范.md` / `docs/06-属性窗口设计.md` / `docs/07-拖拽与布局引擎.md` — M3 设计器依据 - `docs/15-路线图与里程碑.md` — M1~M5 进度与出口判据(M1/M2/M3 已勾选) - `docs/16-VS2026扩展设计.md` — VS2026 集成架构/流程/接口/分期 - 下一步:**M4 控件扩充与自举**(容器/选择/菜单/DataGrid;设计器自身用 NetForms 重写)