多窗体:新建 / 删除窗体,项目 1 登录示例;标识符支持中文

- 新建窗体:生成与 VS 新建窗体相同的 X.cs 和 X.Designer.cs,沿用工程命名空间;
  删除窗体(启动窗体不能删)
- 标签页按窗体分组,启动窗体排在最前并默认打开
- 运行时点哪个窗口哪个到最前;消息框始终在所有窗口之上
- 示例:项目 1 任务 1 登录与增加用户,两个窗体的 Designer.cs 由设计器引擎生成;
  修正书上未声明的 change 变量、关闭后重复 Show 已释放窗体的问题
- 设计器的正则支持中文控件名、类名、命名空间
- 修正:切换窗体时属性窗口的名称没有刷新
- 测试:多窗体端到端测试;新窗体模板测试

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-22 11:23:37 +08:00
1 parent 02d2f0eabc
commit 8feda289de
19 files changed
+739 -36

No files matched your search

+10 -10
View File
@@ -123,17 +123,17 @@ function skipVerbatim(text: string, i: number) {
// ---------- 解析 ----------
const CREATE = /^this\.(\w+) = new ([\w.]+)\(([^()]*)\);$/
const LAYOUT = /^this\.(?:(\w+)\.)?(SuspendLayout|ResumeLayout|PerformLayout)\((?:false)?\);$/
const INIT = /^\(\(System\.ComponentModel\.ISupportInitialize\)\(this\.(\w+)\)\)\.(BeginInit|EndInit)\(\);$/
const EVENT = /^this\.(?:(\w+)\.)?(\w+) \+= new ([\w.]+)\(this\.(\w+)\);$/
const ADD = /^this\.(?:(\w+)\.)?Controls\.Add\(this\.(\w+)\);$/
const ITEMS = /^this\.(\w+)\.Items\.AddRange\(new object\[\] \{([\s\S]*)\}\);$/
const PROP = /^this\.([\w.]+) = ([\s\S]+);$/
const CREATE = /^this\.([\p{L}\p{N}_]+) = new ([\p{L}\p{N}_.]+)\(([^()]*)\);$/u
const LAYOUT = /^this\.(?:([\p{L}\p{N}_]+)\.)?(SuspendLayout|ResumeLayout|PerformLayout)\((?:false)?\);$/u
const INIT = /^\(\(System\.ComponentModel\.ISupportInitialize\)\(this\.([\p{L}\p{N}_]+)\)\)\.(BeginInit|EndInit)\(\);$/u
const EVENT = /^this\.(?:([\p{L}\p{N}_]+)\.)?([\p{L}\p{N}_]+) \+= new ([\p{L}\p{N}_.]+)\(this\.([\p{L}\p{N}_]+)\);$/u
const ADD = /^this\.(?:([\p{L}\p{N}_]+)\.)?Controls\.Add\(this\.([\p{L}\p{N}_]+)\);$/u
const ITEMS = /^this\.([\p{L}\p{N}_]+)\.Items\.AddRange\(new object\[\] \{([\s\S]*)\}\);$/u
const PROP = /^this\.([\p{L}\p{N}_.]+) = ([\s\S]+);$/u
/** 一个 Designer.cs 文件里窗体类的名字:partial class 后面的标识符 */
function findFormName(text: string) {
const match = /partial\s+class\s+(\w+)/.exec(text)
const match = /partial\s+class\s+([\p{L}\p{N}_]+)/u.exec(text)
if (!match) throw new DesignerParseError('找不到 partial class 声明')
return match[1]
}
@@ -157,7 +157,7 @@ export function parseDesigner(text: string): DesignerDocument {
// 第一遍:找出所有创建的控件。只有声明了字段的才算控件——
// 窗体属性也可能写成 this.ClientSize = new System.Drawing.Size(...),形式和创建控件一样
const fields = new Set([...text.matchAll(/^\s*private\s+[\w.]+\s+(\w+)\s*;/gm)].map((m) => m[1]))
const fields = new Set([...text.matchAll(/^\s*private\s+[\p{L}\p{N}_.]+\s+([\p{L}\p{N}_]+)\s*;/gmu)].map((m) => m[1]))
const controls = new Set<string>()
for (const piece of pieces) {
const m = piece.kind === 'stmt' ? CREATE.exec(piece.text) : null
@@ -173,7 +173,7 @@ export function parseDesigner(text: string): DesignerDocument {
headerLines.push(piece.text)
// VS 的块头是三行注释:"// "、"// 名字"、"// "
if (headerLines.length === 3) {
const name = /^\/\/ (\w+)$/.exec(headerLines[1])?.[1]
const name = /^\/\/ ([\p{L}\p{N}_]+)$/u.exec(headerLines[1])?.[1]
if (!name || headerLines[0].trim() !== '//' || headerLines[2].trim() !== '//') {
throw new DesignerParseError(`无法识别的注释:${headerLines.join(' ')}`)
}