基线:全量源码首提(D1 版本控制落地,含第一轮优化 B1-B4/A1/A4/缩放修复)
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
---
|
||||
name: sd-enhanced-components
|
||||
description: SharpDevelop 5.x WinForms 设计器**全部工具箱**(5 分类 81 条目)的属性手册与 Designer.cs 代码生成规则:增强组件(Smart.CustomComponents 13 个:停靠面板/停靠窗口、圆角按钮、图片按钮、侧边菜单、功能区 RibbonStrip、增强数据表格、多选下拉框、可调高文本框/下拉框、OK/NG 状态灯、变量绑定控件、日志组件)+ 标准控件(Button/TextBox/ListView/TreeView/TabControl/菜单工具栏/对话框等 45 个)+ 数据/组件/打印(BindingSource、Timer、SerialPort、PrintDocument 等 23 个)。每个控件含全部属性(类型/默认值/序列化规则)、事件与运行时 API。当用户要在 WinForms 项目里使用、询问属性、手写或生成任何 *.Designer.cs 代码、生成停靠窗口类、排查"?"载入错误或"valid theme"运行错误时,必须使用本技能。提到"增强组件""Smart.CustomComponents""停靠窗口""功能区""RibbonStrip""工具箱""某个控件有哪些属性""生成设计器代码"即触发。新建解决方案时用 copy-to-solution.cmd 把本技能复制到项目里。
|
||||
---
|
||||
|
||||
# SharpDevelop 增强组件(Smart.CustomComponents)手册
|
||||
|
||||
库源码:`F:\Visual Studio\1\33\SharpDevelop-master\src\Libraries\CustomComponents\`(本技能内容即从该源码与编译产物反射逐项提取,属性名/类型/默认值以源码为准)。
|
||||
工具箱类别名:**增强组件**(SharpDevelopControlLibrary.sdcl 注册,13 个控件)。
|
||||
|
||||
## 使用本技能的典型任务
|
||||
|
||||
1. 在用户项目里手工使用/推荐这些控件的属性("这个控件有哪些属性")
|
||||
2. 生成或校对 `*.Designer.cs`(SharpDevelop CodeDom 序列化风格)
|
||||
3. 从停靠窗口集合配置生成窗口类三件套(等价于"生成停靠窗口代码"命令)
|
||||
4. 排错:设计器载入 `?`、运行时 "DockPanel.Theme must be set to a valid theme"
|
||||
|
||||
## 工具箱全量清单(5 分类 81 条目,权威来源 data\options\SharpDevelopControlLibrary.sdcl)
|
||||
|
||||
| 分类 | 条目数 | 属性手册 |
|
||||
|---|---|---|
|
||||
| 增强组件(Smart.CustomComponents) | 13 | `references/components.md` |
|
||||
| Windows Forms(标准控件+菜单+对话框) | 45 | `references/winforms-controls.md` |
|
||||
| Data(绑定/表格/数据集) | 7 | `references/winforms-components.md` |
|
||||
| Components(后台/IO/系统组件) | 11 | `references/winforms-components.md` |
|
||||
| Printing(打印全家桶) | 5 | `references/winforms-components.md` |
|
||||
|
||||
(Timer/ErrorProvider/HelpProvider/ImageList 在 Windows Forms 与 Components 两个分类重复出现,规则一致;Data 分类里的 DataNavigator 在 .NET 4.8 不存在,是死条目,用 BindingNavigator。)
|
||||
|
||||
**读哪个文件**:增强组件 → `references/components.md`;标准控件/菜单/对话框 → `references/winforms-controls.md`;组件/数据/打印 → `references/winforms-components.md`;**写任何 Designer.cs 代码前再读 `references/codegen.md`**(骨架模板、Content 集合、菜单嵌套、ListView/TreeView、RibbonStrip 三层集合、非可视组件、停靠窗口三文件流程)。
|
||||
|
||||
## 复制到新解决方案(三种方式,已自动化)
|
||||
|
||||
本技能的分发链(源→编译产物→新方案):
|
||||
|
||||
```
|
||||
用户级正本 C:\Users\099978\.agents\skills\sd-enhanced-components\ ←源(改这里)
|
||||
① 编译 Smart.CustomComponents 时同步到 SharpDevelop 仓库根副本 + bin\sd-enhanced-components\
|
||||
(bin = 设计器编译输出/测试运行目录)
|
||||
② SharpDevelop 新建解决方案时自动复制到 <新方案>\.agents\skills\sd-enhanced-components\
|
||||
```
|
||||
|
||||
1. **全自动**:本定制版 SharpDevelop 在"文件→新建→解决方案"落盘后自动复制(`ProjectTemplate.CreateAndOpenSolution` 里调 `SkillFolderSync`,日志在 `bin\SkillSync.Log.txt`)。源找不到时自动回退:先 bin,再应用根目录。
|
||||
2. **手动脚本**:`copy-to-solution.cmd <解决方案目录>`(同样复制到 `<目录>\.agents\skills\sd-enhanced-components\`)。
|
||||
3. **全局生效**:用户级技能目录本就全局生效——复制是留档与给其他 AI 工具。
|
||||
|
||||
改手册后同步三处:用户级正本 → 重编 Smart.CustomComponents(自动进仓库根副本 + bin)→ 新方案由 SharpDevelop 自动带。
|
||||
|
||||
## 零、项目组织铁律(用户强制,最高优先级)
|
||||
|
||||
1. **UI 只写主窗口**:新建解决方案自带的主窗口 MainForm(MainForm.cs / MainForm.Designer.cs)是唯一 UI 承载体,所有控件/布局/Designer.cs 生成一律写进 MainForm;Program.cs 保持 `Application.Run(new MainForm())`。
|
||||
2. **绝不另开窗口写 UI**:不要为了放界面新建 TestForm/Form2 之类第二个窗体;多窗口只在用户明确要求时才做。
|
||||
3. **AI 生成 Designer.cs 前必须先写引用(否则死锁)**:手写/生成 Designer.cs 用了增强控件而项目尚无程序集引用时,设计器加载第一步(类型解析)就失败(一片 CS0246),而"自动补引用"恰恰跑在设计器加载流程内部——类型解析不过它就没机会执行,形成死循环:**引用缺失 → 设计器加载失败 → 自动补引用不触发 → 引用依然缺失**。所以**写 Designer.cs 的同一次改动里必须先把 9 个引用写进 csproj**(完整清单见第一章)。HintPath **默认指向 SharpDevelop 安装目录** `C:\Program Files (x86)\SharpDevelop\5.2\bin`——装完即存在、机器间一致;**装在非默认位置时先用第一章"定位实际安装目录"找到真实 bin 再写**;开发机要立刻用仓库最新控件时可临时换仓库 bin `F:\Visual Studio\1\33\SharpDevelop-master\bin`(仅本机存在,**交付/通用模板别用**)。前提:安装目录必须是最新构建(jk55 案例:08-27 旧安装包缺 RibbonStrip/Logger,13 控件只认 11——须重打 MSI 重装或用仓库 bin 最新 DLL 覆盖安装目录)。"切设计标签自动补引用"只在**工具箱拖放**场景有效(那时设计器本来就能加载)。`<Private>True</Private>` 编译时会把 DLL 复制进项目 bin\Debug,之后运行/分发不再依赖 HintPath 原位置。案例:jk55 项目(2026-09-01)。
|
||||
4. 历史测试项目里 AI 写的 TestForm(如 ww123)是测试产物,**不是范本**;交付写法以本节为准。
|
||||
5. **属性白名单 + 报错后完全重启(s23 案例 2026-09-01)**:Designer.cs 每个属性必须能在 components.md 该控件条目下查到——跨控件抄属性会抛 `CodeDomSerializerException`("没有名为 X 的属性")并**中止整个设计器加载**(已知易错:VariableEditTextBox 无 PlaceholderText、VariableDisplayLabel 无 BorderStyle)。改完报错的 Designer.cs 后**必须完全退出 SharpDevelop.exe 重开**(关标签页没用)——设计器 CodeDom 反序列化的 AppDomain 在进程内缓存旧异常,不重启会一直回放。
|
||||
6. **Designer.cs 内绝不调用自定义辅助方法(H1 案例 2026-09-01,适用于所有项目)**:`InitializeComponent()` 里只写标准 CodeDom 语句(new/属性赋值/集合 Add/AddRange/事件挂接/Controls.Add)。自定义方法调用(如 `ConfigurePage(...)`)会被反序列化错误解析成基类 Form 方法,抛 `MissingMethodException` 并中止加载。处置:只改该项目自己的 Designer.cs(删调用+删方法定义,展开成属性赋值),**不动 SharpDevelop 本身、不动设计器配置**;重新编译项目后完全退出 SharpDevelop 再重开(清进程内异常缓存)。
|
||||
7. **控件库修 bug 后,用户项目必须重编一次才吃到**(成套报价软件案例 2026-09-01):项目引用是 `<Private>True</Private>`——编译时把控件 DLL **拷贝**进项目 `bin\Debug`,之后运行/设计器用的都是这份拷贝。所以控件库更新后:改控件 → 重编 Smart.CustomComponents → **用户项目也要重编**(拷贝才会刷新);排查"控件还有老 bug"先看项目 `bin\Debug\Smart.CustomComponents.dll` 时间戳,不是看仓库 bin。
|
||||
|
||||
## 一、项目前提(缺一不可)
|
||||
|
||||
用这些控件的用户项目必须是:
|
||||
|
||||
```xml
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<PlatformTarget>x86</PlatformTarget> <!-- SharpDevelop 调试器仅 32 位;SQLite 为 x86 混合模式 -->
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest> <!-- dpiAware,否则运行时缩放 2/3 -->
|
||||
```
|
||||
|
||||
引用清单(老式 csproj 引用不传递,**9 个全要**,HintPath 默认指向 **SharpDevelop 安装目录** `C:\Program Files (x86)\SharpDevelop\5.2\bin`(稳定、装完即有);开发机临时用仓库 bin 见零章第 3 条):
|
||||
|
||||
| 程序集 | 备注 |
|
||||
|---|---|
|
||||
| Smart.CustomComponents, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ba044a4aa43e0dc4 | 主库,强命名 |
|
||||
| WeifenLuo.WinFormsUI.Docking | DockSurface 基类 DockPanel |
|
||||
| WeifenLuo.WinFormsUI.Docking.ThemeVS2015 | VS2015 主题(含关闭按钮补丁) |
|
||||
| System.Data.SQLite | x86 混合模式 1.0.119(增强表格用,无需 Interop.dll) |
|
||||
| log4net | **Logger 日志组件后端**(漏了 Logger 报类型解析失败) |
|
||||
| System.Resources.Extensions / System.Memory / System.Runtime.CompilerServices.Unsafe / System.Numerics.Vectors | **二级依赖链**(主题资源加载必需,漏了运行时报 valid theme) |
|
||||
|
||||
HintPath 模板:`C:\Program Files (x86)\SharpDevelop\5.2\bin\<dll 名>`,并加 `<Private>True</Private>`(可复制 csproj 片段见 codegen.md §0.1)。
|
||||
工具箱拖放会自动补齐引用(此时设计器能加载,自动补才跑得起来);AI 手写 Designer.cs 必须按零章第 3 条先写引用。
|
||||
|
||||
### 定位实际安装目录(装在非默认位置时,AI 按顺序执行)
|
||||
|
||||
注册表卸载键的 `InstallLocation` 是**空的**(WiX 安装包不写该值),别指望它。按可靠性依次试:
|
||||
|
||||
```powershell
|
||||
# ① IDE 正在运行:进程路径(exe 所在目录即 bin,最直接)
|
||||
Get-Process SharpDevelop -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
|
||||
|
||||
# ② 开始菜单快捷方式解析(已实测可用)
|
||||
$s = New-Object -ComObject WScript.Shell
|
||||
Get-ChildItem "$env:ProgramData\Microsoft\Windows\Start Menu\Programs", "$env:AppData\Microsoft\Windows\Start Menu\Programs" -Recurse -Filter '*SharpDevelop*.lnk' -ErrorAction SilentlyContinue | ForEach-Object { $s.CreateShortcut($_.FullName).TargetPath }
|
||||
|
||||
# ③ 已有项目的 HintPath(能跑通的项目里记的就是真实安装位置)
|
||||
Get-ChildItem "$env:USERPROFILE\Documents\SharpDevelop Projects" -Recurse -Filter *.csproj -ErrorAction SilentlyContinue | Select-String 'Smart\.CustomComponents\.dll.*HintPath'
|
||||
|
||||
# ④ 常见默认位置探测
|
||||
Test-Path 'C:\Program Files (x86)\SharpDevelop\5.2\bin\SharpDevelop.exe'
|
||||
Test-Path 'C:\Program Files\SharpDevelop\5.2\bin\SharpDevelop.exe'
|
||||
```
|
||||
|
||||
找到的 `SharpDevelop.exe` 所在目录就是 HintPath 前缀(安装布局固定为 `...\SharpDevelop\<版本>\bin\`)。仍找不到时问用户装在哪,不要瞎猜路径写进 csproj。
|
||||
|
||||
## 二、控件速查(详见 references/components.md;标准控件见 winforms-controls.md)
|
||||
|
||||
| 控件 | 类型全名(命名空间 Smart.CustomComponents) | 核心属性 | 生成代码注意 |
|
||||
|---|---|---|---|
|
||||
| 停靠面板 DockSurface | DockSurface : DockPanel | DockTheme、DockWindows 集合、LayoutFileName | Content 集合 + 三文件生成(见 codegen.md) |
|
||||
| 圆角按钮 RoundedButton | RoundedButton : Control | Style(8 种配色)、Radius、三态色、Border | 枚举序列化用英文名 |
|
||||
| 图片按钮 ImageButton | ImageButton : Control | **属性名就是中文**:图片路径/图片缩放/图文布局… | 生成代码直接写中文标识符 |
|
||||
| 侧边菜单 SideMenuPanel | SideMenuPanel : UserControl | MenuItems、Pages、菜单宽度/行高/配色 10 项 | 两个 Content 集合,页面走 sideMenuPageN 字段 |
|
||||
| 功能区 RibbonStrip | RibbonStrip : UserControl | Tabs→Groups→Buttons 三层集合、AccentColor 等 4 色、ActiveTabIndex | 图标走 `RibbonButton.FromFile("路径")`;点击统一 ButtonClick(e.Tab/e.Group/e.Button) |
|
||||
| 增强数据表格 EnhancedDataGridView | EnhancedDataGridView : DataGridView | EnableExcelStyleSelection、Sqlite 三件套、EnableCellPaste | 列走标准 DataGridView Columns 序列化 |
|
||||
| 多选下拉框 MultiSelectComboBox | MultiSelectComboBox : Control | Items、MultiSelect、Searchable、AllowCustomInput、Token 配色 | Items 是 Content 字符串集合 |
|
||||
| 可调高下拉框 ResizableComboBox | ResizableComboBox : Control | Items、SelectedIndex、Flat、箭头/边框配色 | SelectedText 不序列化 |
|
||||
| 可调高文本框 ResizableTextBox | ResizableTextBox : Control | PlaceholderText、Flat、三态边框色、密码字符 | 高度自由(这是它的存在意义) |
|
||||
| OK/NG 状态灯 OKNGStatusControl | OKNGStatusControl : Control | IsOK、BindVariable | **绝不写 Text**(自绘覆盖) |
|
||||
| 变量显示标签 VariableDisplayLabel | VariableDisplayLabel : Control | BindVariable、DisplayValue、TextAlign | 只读,事件 ValueChanged |
|
||||
| 变量编辑框 VariableEditTextBox | VariableEditTextBox : TextBox | BindVariable(Value=Text) | 即 TextBox,仅多绑定属性 |
|
||||
| 日志记录器 Logger(非可视) | Logger : Component | 日志文件名/日志级别/按天滚动/最多保留份数 | log4net 后端;代码调 写信息/写警告/写错误/写异常;详见 components.md 第 12 节 |
|
||||
|
||||
标准控件最常用的"总是写/绝不写"清单(完整规则在 winforms-controls.md):Label 的 `AutoSize = true`、CheckBox 与 TabPage 的 `UseVisualStyleBackColor = true` 总是写;DateTimePicker 的 Value、Timer 的 Enabled、各对话框的运行时结果绝不写。
|
||||
|
||||
## 三、代码生成五条铁律
|
||||
|
||||
1. **只序列化非默认值**。属性表里给了每个属性的默认值——与默认相同就不写进 Designer.cs(SharpDevelop 序列化器行为,手写也要遵守,否则设计器重载后会"多出"赋值行)。
|
||||
2. **枚举一律用英文标识符**(属性网格显示的中文来自 TypeConverter,`ChineseEnumConverter.ConvertTo` 只管显示;`EnumCodeDomSerializer` 序列化走原始名)。例:`this.roundedButton1.Style = Smart.CustomComponents.RoundedButtonStyle.Success;` 唯一例外是 ImageButton——它的属性/枚举**本身就是中文标识符**(`图文布局.图左文右`)。
|
||||
3. **Content 集合**(MenuItems/Pages/Items/DockWindows)逐条目生成:`new` 条目 → 赋非默认属性 → `父.集合.Add(条目临时变量)`。标了 `Hidden/Browsable(false)` 的属性(PersistString、ContentXml、ColumnsXml、SourceFile、Selected* 等)**绝不写**。
|
||||
4. **事件**在属性赋值之后、`Controls.Add` 之前挂:`this.roundedButton1.Click += new System.EventHandler(this.roundedButton1_Click);`
|
||||
5. **颜色属性带 ShouldSerialize 默认值**(见属性表"序列化条件"列),与出厂色相同则省略。
|
||||
|
||||
## 四、排错速查
|
||||
|
||||
| 症状 | 根因 | 处置 |
|
||||
|---|---|---|
|
||||
| **RibbonStrip 分组名只显示半个字**(下半截被裁,成套报价软件案例 2026-09-01) | 旧版控件布局是固定像素(分组名带 18px/选项卡条 26px/大按钮文字带 20px,按 96dpi 宋体 12px 调的);项目给功能区设了高字体(如微软雅黑 9F,字高~17px)装不下,TextRenderer 裁掉下半截 | **控件已根治**(09-01 21:54 起自适应字体高度)。老症状=项目在用旧 DLL:把项目 Smart.CustomComponents.dll 升到 21:56+(HintPath 指仓库 bin 或新 MSI 安装目录),**项目重编一次**拉新拷贝 |
|
||||
| **RibbonStrip 大按钮文字显示不全**(截成省略号,同案例 2026-09-01) | 旧版大按钮宽度写死 58px,4+ 汉字按钮被 EndEllipsis 截断(小按钮一直是动态宽,大按钮漏了) | **控件已根治**(09-01 21:56 起按文字实测宽度 `max(58, 文字宽+12)`)。处置同上:升级 DLL + 项目重编 |
|
||||
| 设计器抛 `MissingMethodException`:未找到方法 `System.Windows.Forms.Form.ConfigurePage`,窗体全不显示(通用规则,H1 为案例) | **当前用户项目自己的** `MainForm.Designer.cs` 在 `InitializeComponent()` 中调用了设计器无法处理的自定义辅助方法;这不是修改 SharpDevelop 本身,也不是修改设计器配置。SharpDevelop CodeDom 反序列化把它错误当成基类 Form 方法解析 | 对任何项目都只改该项目自己的 Designer.cs:删除 `ConfigurePage(...)` 调用和方法定义,展开为标准 `page.BackColor`/`Dock`/`MenuKey`/`Name` 属性赋值;Designer.cs 只保留标准 CodeDom 语句,辅助方法只能放该项目 MainForm.cs 的运行时代码。重新编译项目后**完全退出 SharpDevelop 再重新打开项目**,清除旧异常缓存 |
|
||||
| 设计器抛 `CodeDomSerializerException`:"XX 没有名为 YY 的属性",窗体全不显示(s23 案例) | Designer.cs 写了该控件没有的属性(跨控件抄属性);反序列化遇未知属性即中止整个加载 | 对照 components.md 该控件的白名单删掉错行;删完**完全退出重启 IDE**(设计器 AppDomain 缓存旧异常,不重启继续报) |
|
||||
| 构建报 MSB3644"找不到 v4.8 参考程序集"+ 一串 MSB3247 版本冲突;设计器偶发无输出(s23 案例) | 系统只装了 4.8.1 Developer Pack,`Reference Assemblies\...\.NETFramework\v4.8` 目录为空,MSBuild 只能从 GAC 捞到 v2.0/v4.0 混解析 | 装 .NET 4.8 Developer Pack 离线包(NDP48-DevPack-ENU.exe 约 140MB,静默装);装完 v4.8 目录 133 个参考 DLL,MSB3644/MSB3247 一起消失 |
|
||||
| **AI 生成项目后设计区空白/一片 CS0246**(jk55 案例) | Designer.cs 用了增强控件但 csproj 无程序集引用;设计器加载第一步类型解析就失败,"自动补引用"跑在加载流程内部永远不触发——死锁:引用缺失→加载失败→自动补不触发→引用仍缺失 | **同一次改动里手动把 9 个引用写进 csproj**(清单见第一章),HintPath 指向安装目录;写完再开设计视图即正常。自动补引用只在工具箱拖放场景可用 |
|
||||
| 自动补引用兜底后仍剩个别 CS0246(RibbonStrip/Logger 认不出) | 安装目录 `C:\Program Files (x86)\SharpDevelop\5.2\bin` 的 DLL 是旧构建(08-27 包缺这两个类型,13 控件只认 11) | **重打 MSI 重装**(或把仓库 bin 最新 DLL 覆盖安装目录),让稳定路径带全 13 控件;开发机可临时把 HintPath 指仓库 bin 过渡 |
|
||||
| 设计器载入 "Could not find type '?'" | 项目引用解析不到(裸引用无 HintPath) | 08-25 13:53 及以后的 MSI 已在引用解析层中心兜底(自动回退到安装 bin);旧包则补 HintPath |
|
||||
| 运行时 "DockPanel.Theme must be set to a valid theme" | 缺二级依赖链(Resources.Extensions 等 4 个) | 13:53+ 的库带 AssemblyResolve 运行时兜底;或按第一节补全 8 个引用。诊断看 `bin\Debug\DockWindows.Log.txt` |
|
||||
| 运行时菜单重复/集合翻倍 | 设计期默认内容放进了构造函数 | 默认集合内容只能在 Designer.Initialize 且 `!host.Loading` 时注入(SideMenuPanel 已修,新控件照此办理) |
|
||||
| 自定义集合编辑器改了不保存 | 原地改集合没发 IComponentChangeService 通知 | 编辑器写回前后发 OnComponentChanging/Changed |
|
||||
| CellStyle Builder/列编辑器等框架对话框全英文 | .NET Framework 中文语言包未装(GAC 无 System.Design.resources zh-Hans) | 装 .NET 4.8 简体中文语言包(fwlink 2053984,`/q /norestart`),一次修复所有控件的框架编辑器**外壳**(标题/按钮) |
|
||||
| 对话框外壳已中文但**内部属性行**仍英文 | 内部 PropertyGrid 的属性行走翻译层词典;`DataGridViewCellStyle` 不是 Component,单独注册 | DesignerTranslation.Install() 已为 DataGridViewCellStyle 补挂类型级提供器;缺行名就往词典加一条(如 SortMode→排序模式) |
|
||||
| 改 CoreProperties.UILanguage 为 zh-CN 后整个 IDE 变英文 | SD 自家资源只有中性 zh 命名,具体文化不被识别回落 en | 保持 UILanguage=zh;用 ResourceService 里 IsNeutralCulture→CreateSpecificCulture 升格线程文化的代码补丁(勿动配置) |
|
||||
| 属性面板改列标题高度模式后不生效/被顶回 | Designer.cs 残留显式模式行 + 引用指向旧版库 DLL | 删掉 Designer.cs 中该行;确认项目 HintPath 指向当前编译产物;EDG.Ctor.Log.txt 有"谁改的模式+调用栈" |
|
||||
|
||||
诊断优先级(用户要求):**读日志文件**(DockWindows.Log.txt / EDG.Ctor.Log.txt / DesignerTranslation.log),截图识别是最后手段。
|
||||
|
||||
## 五、Smart.CustomComponents 控件库修复历史速查(判断"项目是不是在用旧 DLL 复现旧 bug")
|
||||
|
||||
| 日期 | 修复 | 载体与判定 |
|
||||
|---|---|---|
|
||||
| 09-01 21:54 | RibbonStrip 带区高度自适应字体(分组名带 max(18,字高+4)、选项卡条、大按钮文字带;OnFontChanged 重画)——修"分组名只显示半个字" | 仓库 bin 与 21:54+ 编译的 DLL 均含;安装 MSI 13:14 版**不含** |
|
||||
| 09-01 21:56 | RibbonStrip 大按钮宽度按文字实测 max(58, 文字宽+12)——修"按钮文字截成省略号" | 同上;判定项目是否吃到:看项目 `bin\Debug\Smart.CustomComponents.dll` 时间戳 ≥ 21:56 |
|
||||
| 09-01 00:01 | SkillFolderSync(新建解决方案自动复制技能手册) | ICSharpCode.SharpDevelop.dll;13:14 MSI 已含 |
|
||||
| 08-31 | 翻译词典收录 RibbonStrip→"增强选项卡"等全部工具箱中文名 | FormsDesigner.dll;13:14 MSI 已含 |
|
||||
| 08-25 13:53 | 裸引用解析兜底、主题链 AssemblyResolve 运行时兜底 | 详见各排错行 |
|
||||
|
||||
控件库再修 bug 时:往这张表加一行,并提醒用户项目重编(零章第 7 条)。
|
||||
@@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
rem Copy the sd-enhanced-components skill into a solution folder.
|
||||
rem Usage: copy-to-solution.cmd <solution-dir>
|
||||
setlocal
|
||||
set "SKILL_DIR=%~dp0"
|
||||
if "%~1"=="" (
|
||||
echo Usage: %~nx0 ^<solution-dir^>
|
||||
echo Example: %~nx0 "F:\pycode\wpf\src\WinFormsDesigner.Fx48\Samples\COOO\scr\MyApp"
|
||||
exit /b 1
|
||||
)
|
||||
if not exist "%~1" (
|
||||
echo ERROR: directory not found: %~1
|
||||
exit /b 1
|
||||
)
|
||||
set "DEST=%~1\.agents\skills\sd-enhanced-components"
|
||||
xcopy "%SKILL_DIR%SKILL.md" "%DEST%\" /Y /I >nul
|
||||
xcopy "%SKILL_DIR%references" "%DEST%\references\" /Y /E /I >nul
|
||||
echo Copied skill to: %DEST%
|
||||
echo Files: SKILL.md + references ^(components / winforms-controls / winforms-components / codegen^)
|
||||
endlocal
|
||||
@@ -0,0 +1,394 @@
|
||||
# Designer.cs 代码生成规则(SharpDevelop CodeDom 序列化风格)
|
||||
|
||||
对应实现:SharpDevelop NRefactoryDesignerLoader 序列化 + `Commands/GenerateDockWindows.cs` 生成器。缩进用 **Tab**,语句带 `this.`,非 System 类型用全名。
|
||||
|
||||
## 0. 目标窗体约定(用户强制)
|
||||
|
||||
- 生成/手写 Designer.cs 的目标永远是解决方案**自带主窗口 MainForm**(MainForm.Designer.cs),事件运行时代码放 MainForm.cs;**绝不新建第二个窗体承载 UI**(TestForm/Form2 之类只在该项目本身就是测试工具时才允许)。
|
||||
- **写 Designer.cs 前先补引用,否则设计器死锁**(jk55 案例):设计器加载第一步是类型解析,项目没引用增强控件程序集就直接一片 CS0246;而"切设计标签自动补引用"的代码跑在设计器加载流程**内部**,类型解析不过它永远不执行——引用缺失→加载失败→自动补不触发→引用仍缺失。**AI 生成 Designer.cs 的同一次改动必须先写 §0.1 的 9 个引用。** 工具箱拖放场景不受影响(那时设计器本来就能加载)。
|
||||
|
||||
### 0.1 增强控件引用九件套(写 Designer.cs 时同步写进 csproj)
|
||||
|
||||
HintPath **默认指向 SharpDevelop 安装目录** `C:\Program Files (x86)\SharpDevelop\5.2\bin`:装完即存在、机器间一致(工具箱自动补引用与引用解析兜底用的同一规范位置)。三条变通:
|
||||
|
||||
- **装在非默认位置**:先用下面的命令找到真实安装 bin,把 9 个 HintPath 整体替换成实际路径(注册表卸载键 InstallLocation 为空,别用它):
|
||||
```powershell
|
||||
# ① IDE 在跑:进程路径(exe 所在目录即 bin)
|
||||
Get-Process SharpDevelop -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
|
||||
# ② 开始菜单快捷方式(已实测)
|
||||
$s = New-Object -ComObject WScript.Shell
|
||||
Get-ChildItem "$env:ProgramData\Microsoft\Windows\Start Menu\Programs","$env:AppData\Microsoft\Windows\Start Menu\Programs" -Recurse -Filter '*SharpDevelop*.lnk' -ErrorAction SilentlyContinue | ForEach-Object { $s.CreateShortcut($_.FullName).TargetPath }
|
||||
# ③ 已有项目 HintPath
|
||||
Get-ChildItem "$env:USERPROFILE\Documents\SharpDevelop Projects" -Recurse -Filter *.csproj -ErrorAction SilentlyContinue | Select-String 'Smart\.CustomComponents\.dll.*HintPath'
|
||||
```
|
||||
都找不到就问用户,不要瞎猜路径。完整说明见 SKILL.md 第一章"定位实际安装目录"。
|
||||
- **开发机临时追新**:仓库 bin `F:\Visual Studio\1\33\SharpDevelop-master\bin` 有最新控件时可把 HintPath 临时换成它,但该路径仅本机存在,**交付/给用户机的模板一律用安装目录**。
|
||||
- **前提:安装目录必须是最新构建**。jk55 案例发现 08-27 旧安装包缺 RibbonStrip/Logger(13 控件只认 11)——先重打 MSI 重装,或把仓库 bin 最新 DLL 覆盖到安装目录。
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<Reference Include="Smart.CustomComponents, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ba044a4aa43e0dc4">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\Smart.CustomComponents.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="WeifenLuo.WinFormsUI.Docking">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\WeifenLuo.WinFormsUI.Docking.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2015">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\WeifenLuo.WinFormsUI.Docking.ThemeVS2015.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.SQLite">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Data.SQLite.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="log4net">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\log4net.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Resources.Extensions">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Resources.Extensions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Memory.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics.Vectors">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Numerics.Vectors.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
`<Private>True</Private>` 会在编译时把 DLL 复制进项目 `bin\Debug`,之后项目运行/分发不再依赖 HintPath 原位置(换机器、重装 SharpDevelop 都不受影响)。
|
||||
|
||||
## 1. 标准骨架(单控件)
|
||||
|
||||
```csharp
|
||||
namespace DemoApp
|
||||
{
|
||||
partial class MainForm
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
private Smart.CustomComponents.RoundedButton roundedButton1;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null)) components.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.roundedButton1 = new Smart.CustomComponents.RoundedButton();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// roundedButton1
|
||||
//
|
||||
this.roundedButton1.Location = new System.Drawing.Point(40, 60);
|
||||
this.roundedButton1.Name = "roundedButton1";
|
||||
this.roundedButton1.Radius = 20;
|
||||
this.roundedButton1.Size = new System.Drawing.Size(140, 44);
|
||||
this.roundedButton1.Style = Smart.CustomComponents.RoundedButtonStyle.Success;
|
||||
this.roundedButton1.TabIndex = 0;
|
||||
this.roundedButton1.Text = "保存";
|
||||
this.roundedButton1.Click += new System.EventHandler(this.roundedButton1_Click);
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(784, 561);
|
||||
this.Controls.Add(this.roundedButton1);
|
||||
this.Name = "MainForm";
|
||||
this.Text = "DemoApp";
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
规则:每个控件一段注释头;Location/Name/Size/TabIndex 总是写;其余属性只写**非默认值**(对照 components.md 表);事件在属性后、Controls.Add 前;容器结尾 `ResumeLayout(false)`,有子布局的控件加 `PerformLayout()` 对。
|
||||
|
||||
## 2. Content 集合生成
|
||||
|
||||
### 2.1 字符串项集合(MultiSelectComboBox.Items / ResizableComboBox.Items)
|
||||
|
||||
```csharp
|
||||
this.multiSelectComboBox1.Items.Add("选项A");
|
||||
this.multiSelectComboBox1.Items.Add("选项B");
|
||||
```
|
||||
|
||||
### 2.2 递归对象集合(SideMenuPanel.MenuItems,条目有 Children)
|
||||
|
||||
```csharp
|
||||
Smart.CustomComponents.SideMenuItem sideMenuItem1 = new Smart.CustomComponents.SideMenuItem();
|
||||
sideMenuItem1.Text = "首页";
|
||||
Smart.CustomComponents.SideMenuItem sideMenuItem2 = new Smart.CustomComponents.SideMenuItem();
|
||||
sideMenuItem2.Text = "数据管理";
|
||||
Smart.CustomComponents.SideMenuItem sideMenuItem3 = new Smart.CustomComponents.SideMenuItem();
|
||||
sideMenuItem3.Text = "导出记录";
|
||||
sideMenuItem2.Children.Add(sideMenuItem3);
|
||||
this.sideMenuPanel1.MenuItems.Add(sideMenuItem1);
|
||||
this.sideMenuPanel1.MenuItems.Add(sideMenuItem2);
|
||||
```
|
||||
|
||||
### 2.3 页面集合(SideMenuPanel.Pages)
|
||||
|
||||
页面是 Panel 派生 → 走**字段**路径(设计器把页面注册进 IDesignerHost.Container,命名为 sideMenuPageN):
|
||||
|
||||
```csharp
|
||||
private Smart.CustomComponents.SideMenuPage sideMenuPage1;
|
||||
|
||||
this.sideMenuPage1 = new Smart.CustomComponents.SideMenuPage();
|
||||
this.sideMenuPage1.BackColor = System.Drawing.Color.White;
|
||||
this.sideMenuPage1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.sideMenuPage1.Location = ...;
|
||||
this.sideMenuPage1.MenuKey = "首页";
|
||||
this.sideMenuPage1.Name = "sidePage_首页"; // 用户可改
|
||||
this.sideMenuPanel1.Pages.Add(this.sideMenuPage1);
|
||||
```
|
||||
|
||||
注意:页面一般由菜单自动同步生成(MenuKey=菜单文字);手写时 MenuKey 必须与某菜单项 Text 匹配才会被点击切换。**页面内子控件用 EnableDesignMode 暴露,不要生成 `父.页面` 形式的点号字段名**(历史崩溃点:`sideMenuPanel1.Page_0_首页` 是非法 C# 字段名)。
|
||||
|
||||
### 2.4 停靠窗口集合(DockSurface.DockWindows,DockWindowItem 条目)
|
||||
|
||||
```csharp
|
||||
Smart.CustomComponents.DockWindows.DockWindowItem dockWindowItem1 = new Smart.CustomComponents.DockWindows.DockWindowItem();
|
||||
dockWindowItem1.ClassName = "ToolWindow";
|
||||
dockWindowItem1.Title = "工具箱";
|
||||
dockWindowItem1.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockLeft;
|
||||
dockWindowItem1.DefaultWidth = 240;
|
||||
dockWindowItem1.ShowCloseButton = false;
|
||||
this.dockSurface1.DockWindows.Add(dockWindowItem1);
|
||||
```
|
||||
|
||||
只写非默认属性(DefaultHeight=180、FixedPosition=false、ShowCloseButton=true 是默认,省略)。
|
||||
|
||||
### 2.5 功能区三层集合(RibbonStrip.Tabs → RibbonTab.Groups → RibbonGroup.Buttons → RibbonButton)
|
||||
|
||||
```csharp
|
||||
Smart.CustomComponents.RibbonTab ribbonTab1 = new Smart.CustomComponents.RibbonTab();
|
||||
ribbonTab1.Text = "开始";
|
||||
Smart.CustomComponents.RibbonGroup ribbonGroup1 = new Smart.CustomComponents.RibbonGroup();
|
||||
ribbonGroup1.Text = "文件";
|
||||
Smart.CustomComponents.RibbonButton ribbonButton1 = new Smart.CustomComponents.RibbonButton();
|
||||
ribbonButton1.Text = "打开";
|
||||
ribbonButton1.Image = Smart.CustomComponents.RibbonButton.FromFile("C:\\icons\\open.png"); // 有图标时
|
||||
ribbonButton1.Enabled = false; // 非默认才写
|
||||
ribbonGroup1.Buttons.Add(ribbonButton1);
|
||||
ribbonTab1.Groups.Add(ribbonGroup1);
|
||||
this.ribbonStrip1.Tabs.Add(ribbonTab1);
|
||||
```
|
||||
|
||||
规则:
|
||||
- 逐层 `new` → 赋非默认属性 → `Add` 进上一级集合;只写非默认(Text 无默认标记通常总写,SizeStyle=Small、Enabled=true 是默认省略)。
|
||||
- **图标**:`按钮.Image = RibbonButton.FromFile("路径")`(RibbonImageConverter 序列化形态;ImageFile 本身隐藏不写)。无图标则不写 Image 行。
|
||||
- 事件统一走一个 `this.ribbonStrip1.ButtonClick += new System.EventHandler<Smart.CustomComponents.RibbonButtonEventArgs>(this.ribbonStrip1_ButtonClick);`,处理器里按 `e.Button.Text` 分发(参数带 e.Tab/e.Group/e.Button)。
|
||||
- `this.ribbonStrip1.ActiveTabIndex = 0;` 是默认,省略。
|
||||
|
||||
## 3. 停靠窗口三文件生成(等价"生成停靠窗口代码"命令)
|
||||
|
||||
集合配置好后生成 3 类文件(幂等:窗口类已存在不覆盖,仅同步 Designer.cs 的 this.Text 行;partial 与构造函数接线按当前配置刷新):
|
||||
|
||||
### 3.1 窗口类 `DockWindows\<类名>\<类名>.cs`
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using WeifenLuo.WinFormsUI.Docking;
|
||||
|
||||
namespace DemoApp
|
||||
{
|
||||
/// <summary>停靠窗口:工具箱。内容在本类的设计器视图中编辑。</summary>
|
||||
public partial class ToolWindow : DockContent
|
||||
{
|
||||
public ToolWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Text = "工具箱";
|
||||
TabText = "工具箱";
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 窗口类设计器骨架 `DockWindows\<类名>\<类名>.Designer.cs`
|
||||
|
||||
```csharp
|
||||
namespace DemoApp
|
||||
{
|
||||
partial class ToolWindow
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null)) components.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Designer generated code
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
this.Name = "ToolWindow";
|
||||
this.Text = "工具箱";
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
(双击 .cs 可继续用 SharpDevelop 设计器往里拖控件,序列化规则同第 1、2 节。)
|
||||
|
||||
### 3.3 创建/停靠 partial `DockWindows\<Form>.<surface>.DockWindows.cs`
|
||||
|
||||
```csharp
|
||||
// 本文件由『生成停靠窗口代码』自动生成,集合变更后会自动刷新,请勿手工编辑。
|
||||
using System;
|
||||
using WeifenLuo.WinFormsUI.Docking;
|
||||
|
||||
namespace DemoApp
|
||||
{
|
||||
public partial class MainForm
|
||||
{
|
||||
void CreateDockWindows_dockSurface1()
|
||||
{
|
||||
var dockSurface1 = this.dockSurface1 as Smart.CustomComponents.DockSurface;
|
||||
if (dockSurface1 == null) return;
|
||||
dockSurface1.EnsureDefaultTheme(); // 必须:Show 前保证有效主题
|
||||
var w1 = new ToolWindow();
|
||||
w1.CloseButtonVisible = false; // 仅 ShowCloseButton=false 时生成
|
||||
w1.DockAreas = WeifenLuo.WinFormsUI.Docking.DockAreas.DockLeft; // 仅 FixedPosition=true 时
|
||||
w1.TabText = "工具箱"; // Title 非空时生成
|
||||
w1.Text = "工具箱";
|
||||
w1.Show(dockSurface1, DockState.DockLeft);
|
||||
var w2 = new FloatWindow2();
|
||||
w2.Size = new System.Drawing.Size(320, 240); // 仅 DockState.Float 时
|
||||
w2.Show(dockSurface1, DockState.Float);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
属性设置**必须在 Show 之前**。FixedPosition 的 DockAreas 映射:
|
||||
|
||||
| DockState | DockAreas |
|
||||
|---|---|
|
||||
| DockLeft / DockLeftAutoHide | DockLeft |
|
||||
| DockRight / DockRightAutoHide | DockRight |
|
||||
| DockTop / DockTopAutoHide | DockTop |
|
||||
| DockBottom / DockBottomAutoHide | DockBottom |
|
||||
| Document | Document |
|
||||
| Float | Float |
|
||||
| Hidden / Unknown | 不锁定(不生成 DockAreas 行) |
|
||||
|
||||
### 3.4 构造函数接线(MainForm.cs)
|
||||
|
||||
`InitializeComponent();` 之后插入(已存在则跳过):
|
||||
|
||||
```csharp
|
||||
InitializeComponent();
|
||||
CreateDockWindows_dockSurface1();
|
||||
```
|
||||
|
||||
自动触发链:集合编辑器"确定" → IComponentChangeService.OnComponentChanged → 400ms 防抖静默生成。多面板时每个 DockSurface 各一个 `CreateDockWindows_<面板名>` 方法。
|
||||
|
||||
## 4. 特殊控件生成注意
|
||||
|
||||
- **Designer.cs 只能写标准 Designer 语句(适用于所有项目;H1 是已验证案例)**:`InitializeComponent()` 内只允许 `new`、属性赋值、集合 `Add/AddRange`、事件挂接和 `Controls.Add` 等标准 CodeDom 语句。**不要调用自定义辅助方法**(例如 `ConfigurePage(this.overviewPage, "概览", "sidePage_Overview")`):SharpDevelop 反序列化时可能把它错误解析成 `System.Windows.Forms.Form.ConfigurePage(...)`,继而抛 `MissingMethodException`(未找到方法 `System.Windows.Forms.Form.ConfigurePage`),整个设计器加载中止。页面配置必须展开成标准属性赋值:`page.BackColor`、`page.Dock`、`page.MenuKey`、`page.Name` 等;辅助方法放到 MainForm.cs 的运行时代码中,或不要使用。
|
||||
**边界说明(适用于所有项目)**:此修复只修改**用户项目自己的** `MainForm.Designer.cs`:删除 `ConfigurePage(...)` 调用和 Designer 文件末尾的方法定义,重新编译项目(如生成 `<项目>\bin\Debug\<项目>.exe`);没有修改 SharpDevelop 本身,也没有修改任何设计器配置。若当前 SharpDevelop 进程已经缓存旧的加载异常,修复后自动完全退出 SharpDevelop、重新打开项目再进设计器(这是清缓存,不是因为修改了 SharpDevelop)。
|
||||
- **属性白名单铁律(s23 案例 2026-09-01)**:Designer.cs 里写的每个属性必须能在 components.md 该控件条目下查到(自有属性表或继承属性行)。**跨控件抄属性是头号翻车点**——写一个该控件没有的属性,CodeDom 反序列化直接抛 `CodeDomSerializerException`("XX 没有名为 YY 的属性"),**整个设计器加载中止**、窗体全黑。已知最易抄错的两对:
|
||||
- `VariableEditTextBox` **没有 PlaceholderText**(那是 ResizableTextBox 的;它继承 TextBox,自有属性只有 BindVariable/Value,要占位提示就用 ResizableTextBox);
|
||||
- `VariableDisplayLabel` **没有 BorderStyle**(它继承 Control 不是 Label,自有属性只有 BindVariable/DisplayValue/TextAlign)。
|
||||
- **ImageButton**:属性名/枚举是中文标识符,直接写:`this.imageButton1.图文布局 = Smart.CustomComponents.图文布局枚举.图左文右;`
|
||||
- **OKNGStatusControl**:不写 Text(自绘 OK/NG);IsOK=true 默认省略。
|
||||
- **EnhancedDataGridView**:列与普通 DataGridView 相同(`this.enhancedDataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {...})` + 每列独立字段段);Sqlite 三属性按字符串直写。
|
||||
- **事件处理器签名**:`private void roundedButton1_Click(object sender, EventArgs e)`,写在主 .cs 文件(不放 Designer.cs)。
|
||||
- **图片属性**(SideMenuItem.Icon、Image 类型):走 .resx 资源(`System.ComponentModel.ComponentResourceManager resources = new ...typeof(MainForm));` + `resources.GetObject("$this.sideMenuItem1.Icon")`),SharpDevelop 风格同 WinForms。
|
||||
|
||||
## 5. 生成的运行时诊断(生成器自带,勿删)
|
||||
|
||||
partial 里附带了写 `bin\Debug\DockWindows.Log.txt` 的日志:主题类型/页签条/补丁重载数 + 每窗口 Show 前后的 CloseButtonVisible/DockAreas/DockState/TabText。排错时**先读这个文件**(用户要求:日志优先,截图最后)。
|
||||
|
||||
## 6. 非可视组件模板
|
||||
|
||||
属性表见 `winforms-components.md`。要点:无 Location/Size/TabIndex;构造带 IContainer 的(Timer/ImageList/ErrorProvider/ToolTip/BindingSource/NotifyIcon/HelpProvider/BackgroundWorker/SerialPort)写 `new ...(this.components)` 且不再 Add;不带的(PrintDocument/Process/EventLog/FileSystemWatcher/PerformanceCounter)用 `this.components.Add(this.xxx);`;DataSet/BindingSource 用 ISupportInitialize 的 BeginInit/EndInit 包裹。扩展提供器(ToolTip/ErrorProvider/HelpProvider)对其他控件逐行 `SetXxx(目标控件, 值)`。
|
||||
|
||||
## 7. 菜单/工具栏嵌套模板(每个菜单项/工具栏项都是字段)
|
||||
|
||||
```csharp
|
||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||
private System.Windows.Forms.ToolStripMenuItem 文件ToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem 退出ToolStripMenuItem;
|
||||
|
||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||
this.文件ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.退出ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
//
|
||||
// 退出ToolStripMenuItem
|
||||
//
|
||||
this.退出ToolStripMenuItem.Name = "退出ToolStripMenuItem";
|
||||
this.退出ToolStripMenuItem.Size = new System.Drawing.Size(180, 22); // 布局值,设计器会写
|
||||
this.退出ToolStripMenuItem.Text = "退出";
|
||||
this.退出ToolStripMenuItem.Click += new System.EventHandler(this.退出ToolStripMenuItem_Click);
|
||||
//
|
||||
// 文件ToolStripMenuItem
|
||||
//
|
||||
this.文件ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.退出ToolStripMenuItem});
|
||||
this.文件ToolStripMenuItem.Text = "文件";
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.文件ToolStripMenuItem});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Size = ...; this.menuStrip1.TabIndex = ...;
|
||||
this.menuStrip1.Text = "menuStrip1";
|
||||
// 窗体段:
|
||||
this.MainMenuStrip = this.menuStrip1;
|
||||
this.Controls.Add(this.menuStrip1);
|
||||
```
|
||||
|
||||
ContextMenuStrip 同构(无 Dock、无 MainMenuStrip 行);StatusStrip 用 ToolStripStatusLabel(Spring=true 占满 + BorderSide 等);ToolStrip 混用 ToolStripButton/ToolStripSeparator/ToolStripTextBox…,均独立字段。MenuStrip/ToolStrip 的 Items 段**不写每项 Name 之外的 Text 之外的默认值**;`DropDownItems.AddRange` 表示层级。
|
||||
|
||||
## 8. ListView / TreeView 集合模板
|
||||
|
||||
```csharp
|
||||
// ListView:列为字段,行可 ctor 一行带上
|
||||
this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
|
||||
this.columnHeader1.Text = "姓名"; this.columnHeader1.Width = 120;
|
||||
this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
|
||||
this.columnHeader2.Text = "分数";
|
||||
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.columnHeader1, this.columnHeader2});
|
||||
this.listView1.FullRowSelect = true; // View=Details 时常用组合
|
||||
this.listView1.View = System.Windows.Forms.View.Details;
|
||||
this.listView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
|
||||
new System.Windows.Forms.ListViewItem(new string[] {"张三", "95"}),
|
||||
new System.Windows.Forms.ListViewItem(new string[] {"李四", "88"})});
|
||||
// 分组(可选)
|
||||
this.listViewGroup1 = new System.Windows.Forms.ListViewGroup("一组");
|
||||
this.listView1.Groups.AddRange(new System.Windows.Forms.ListViewGroup[] {this.listViewGroup1});
|
||||
|
||||
// TreeView:节点递归,ctor 带文本
|
||||
System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("根节点");
|
||||
System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("子节点1");
|
||||
System.Windows.Forms.TreeNode treeNode3 = new System.Windows.Forms.TreeNode(new string[] {
|
||||
"子节点1", "子节点2"}); // 兄弟数组写法(Name1,Name2 平铺)
|
||||
treeNode1.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {treeNode3});
|
||||
this.treeView1.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {treeNode1});
|
||||
```
|
||||
|
||||
TreeView 的 designer 实际写法是临时变量 treeNodeN 逐级挂 Nodes.AddRange;ImageKey/SelectedImageKey 在绑定 ImageList 后写。
|
||||
@@ -0,0 +1,593 @@
|
||||
# 增强组件属性手册(Smart.CustomComponents 全部 13 控件 + 条目类型 + 枚举,穷尽版)
|
||||
|
||||
> 生成来源:反射 `Smart.CustomComponents.dll`(bin\Debug 编译产物)+ 源码逐文件核对(src\Libraries\CustomComponents\Controls)。属性名/类型/默认值以 DLL 与源码为准。
|
||||
> 工具箱类别:**增强组件**(13 个控件)。Designer.cs 代码生成规则另见 `codegen.md`。
|
||||
> 序列化列约定:**Content 集合**=逐条目生成;不序列化=设计器/手写都不写;非默认才写=与默认值相同则省略;ShouldSerialize=与出厂值比较不同才写。
|
||||
|
||||
## 目录
|
||||
- 停靠面板 DockSurface(WeifenLuo.WinFormsUI.Docking.DockPanel)
|
||||
- 圆角按钮 RoundedButton(System.Windows.Forms.Control)
|
||||
- 图片按钮 ImageButton(System.Windows.Forms.Control)
|
||||
- 增强数据表格 EnhancedDataGridView(System.Windows.Forms.DataGridView)
|
||||
- 多选下拉框 MultiSelectComboBox(System.Windows.Forms.Control)
|
||||
- 可调高下拉框 ResizableComboBox(System.Windows.Forms.Control)
|
||||
- 可调高文本框 ResizableTextBox(System.Windows.Forms.Control)
|
||||
- OK/NG 状态灯 OKNGStatusControl(System.Windows.Forms.Control)
|
||||
- 侧边菜单 SideMenuPanel(System.Windows.Forms.UserControl)
|
||||
- 功能区 RibbonStrip(System.Windows.Forms.UserControl)
|
||||
- 变量显示标签 VariableDisplayLabel(System.Windows.Forms.Control)
|
||||
- 变量编辑框 VariableEditTextBox(System.Windows.Forms.TextBox)
|
||||
- 日志记录器 Logger(非可视组件)(System.ComponentModel.Component)
|
||||
|
||||
---
|
||||
|
||||
## 停靠面板 DockSurface
|
||||
|
||||
**继承**:`Smart.CustomComponents.DockSurface` ← `WeifenLuo.WinFormsUI.Docking.DockPanel`
|
||||
|
||||
停靠窗口宿主容器。设计器拖入窗体后通过 DockWindows 集合声明若干停靠窗口,用"生成停靠窗口代码"(等价手写三文件)生成窗口类。主题=VS2015Light 起步。
|
||||
|
||||
### 属性(全部 4 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| DockTheme | SurfaceTheme | VS2015Light | 非默认才写 | 停靠主题;Designer.cs 生成配套 Theme 对象(VS2015Light→ThemeVS2015Light),改主题须同步改引用的 Theme 程序集 |
|
||||
| TabDirection | TabTipDirection | Left | 非默认才写 | 选项卡提示方向(左/右),少量布局场景才改 |
|
||||
| DockWindows | DockWindowItemCollection | | **Content 集合** | 停靠窗口声明集合,条目类型 DockWindowItem;生成走"三文件流程"(codegen.md §3) |
|
||||
| LayoutFileName | String | dock_layout.xml | 非默认才写 | 运行时布局持久化 XML 文件名,相对 exe 目录 |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 DockPanel(33 个):DockBackColor, ActiveAutoHideContent, AllowEndUserDocking, AllowEndUserNestedDocking, Contents(RO), RightToLeftLayout, ShowDocumentIcon, DocumentTabStripLocation, Extender(RO), DockPaneFactory(RO), FloatWindowFactory(RO), DockWindowFactory(RO), Panes(RO), DockArea(RO), DockBottomPortion, DockLeftPortion, DockRightPortion, DockTopPortion, DockWindows(RO), DocumentsCount(RO), Documents(RO), FloatWindows(RO), DefaultFloatWindowSize, DocumentStyle, SupportDeeplyNestedContent, ShowAutoHideContentOnHover, DocumentWindowBounds(RO), ActiveContent(RO), ActivePane(RO), ActiveDocument(RO), ActiveDocumentPane(RO), Skin(RO), Theme
|
||||
- 继承 Panel(5 个):AutoSize, AutoSizeMode, BorderStyle, TabStop, Text
|
||||
- 继承 ScrollableControl(8 个):AutoScroll, AutoScrollMargin, AutoScrollPosition, AutoScrollMinSize, DisplayRectangle(RO), HorizontalScroll(RO), VerticalScroll(RO), DockPadding(RO)
|
||||
- 继承 Control(70 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoScrollOffset, LayoutEngine(RO), BackColor, BackgroundImage, BackgroundImageLayout, BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, ForeColor, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
自有事件:无(仅基类事件)。
|
||||
- 运行时错误 "DockPanel.Theme must be set to a valid theme" = 二级依赖链(System.Resources.Extensions 等 4 个)没引用全。
|
||||
- 诊断日志:bin\Debug\DockWindows.Log.txt。
|
||||
- API:EnsureDefaultTheme()(代码兜底挂主题)。
|
||||
- 不可当作普通 Control 用(没有 Size/Location 语义),占满窗体或容器使用。
|
||||
|
||||
---
|
||||
|
||||
## 圆角按钮 RoundedButton
|
||||
|
||||
**继承**:`Smart.CustomComponents.RoundedButton` ← `System.Windows.Forms.Control`
|
||||
|
||||
Element-UI 配色的圆角按钮,8 种风格预设 + 三态(常态/悬停/按下)颜色可覆写。点击用基类 Click 事件。
|
||||
|
||||
### 属性(全部 9 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Style | RoundedButtonStyle | Primary | 非默认才写 | 风格预设 Primary/Success/Warning/Danger/Info/Dark/Light/Outline,Designer.cs 写英文名(如 RoundedButtonStyle.Success) |
|
||||
| Radius | Int32 | 10 | 非默认才写 | 圆角半径 px,0=直角 |
|
||||
| NormalColor | Color | | — | 常态底色;出厂 24,144,255(随 Style 预设,ShouldSerialize 按出厂色比较) |
|
||||
| HoverColor | Color | | — | 悬停底色;出厂 64,169,255 |
|
||||
| PressColor | Color | | — | 按下底色;出厂 9,109,217 |
|
||||
| BorderWidth | Int32 | 0 | 非默认才写 | 边框宽 px,0=无边框(Outline 风格通常设 1~2) |
|
||||
| BorderColor | Color | | — | 边框色;出厂 217,217,217 |
|
||||
| TextAlign | ContentAlignment | MiddleCenter | 非默认才写 | 文字对齐(ContentAlignment),默认 MiddleCenter |
|
||||
| BackColor | Color | | 不序列化 | 被隐藏(自绘不用),绝不写 |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 Control(73 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoSize, AutoScrollOffset, LayoutEngine(RO), BackgroundImage, BackgroundImageLayout, BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), DisplayRectangle(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, ForeColor, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Text, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
自有事件:无(仅基类事件)。
|
||||
- 三色只在改变出厂预设时序列化(ShouldSerialize 与出厂色比较)。
|
||||
- 改色想完整自定义时三态色都要写,避免悬停跳回蓝色。
|
||||
|
||||
---
|
||||
|
||||
## 图片按钮 ImageButton
|
||||
|
||||
**继承**:`Smart.CustomComponents.ImageButton` ← `System.Windows.Forms.Control`
|
||||
|
||||
图片+文字的按钮。**属性名与枚举值本身就是中文标识符**,Designer.cs 直接写中文(全项目唯一例外)。
|
||||
|
||||
### 属性(全部 12 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| BackgroundImage | Image | | 不序列化 | |
|
||||
| BackgroundImageLayout | ImageLayout | | 不序列化 | |
|
||||
| BackColor | Color | | 不序列化 | |
|
||||
| 图片路径 | String | | — | 图片文件绝对/相对路径;非空才序列化(ShouldSerialize图片路径) |
|
||||
| 图片缩放 | 图片缩放模式 | Fit | 非默认才写 | Original/Fit/Stretch/Tile,默认 Fit |
|
||||
| 图片锚点 | 图片锚点位置 | Center | 非默认才写 | Center/N/S/E/W,默认 Center |
|
||||
| 图片透明度 | Double | 1 | 非默认才写 | 0.0~1.0,默认 1 |
|
||||
| 图文布局 | 图文布局枚举 | 文字居中 | 非默认才写 | 文字居中/图下文上/图上文下/图右文左/图左文右/纯图片;枚举成员是中文 |
|
||||
| 文字间距 | Int32 | 1 | 非默认才写 | 图与文字间距 px,默认 1 |
|
||||
| Text | String | | — | 按钮文字(类目 外观) |
|
||||
| Font | Font | | — | 文字字体 |
|
||||
| ForeColor | Color | | — | 文字颜色 |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 Control(68 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoSize, AutoScrollOffset, LayoutEngine(RO), BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), DisplayRectangle(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
自有事件:无(仅基类事件)。
|
||||
- Designer.cs 示例:this.imageButton1.图文布局 = Smart.CustomComponents.图文布局枚举.图左文右;
|
||||
- 点击用基类 Click。
|
||||
|
||||
---
|
||||
|
||||
## 增强数据表格 EnhancedDataGridView
|
||||
|
||||
**继承**:`Smart.CustomComponents.EnhancedDataGridView` ← `System.Windows.Forms.DataGridView`
|
||||
|
||||
在标准 DataGridView 上增加:Excel 风格选择高亮、剪贴板多单元格粘贴、SQLite 三键绑定(x86 混合模式 System.Data.SQLite 1.0.119,无需 Interop.dll)。列仍用标准 Columns 序列化。
|
||||
|
||||
### 属性(全部 9 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| EnableExcelStyleSelection | Boolean | True | 非默认才写 | 列头/行头点击整列整行高亮 + 框选区域高亮,默认开 |
|
||||
| EnableCellPaste | Boolean | False | 非默认才写 | 允许从 Excel 复制后粘贴多单元格,默认关 |
|
||||
| SqliteDatabasePath | String | | — | SQLite 数据库文件路径(.db);相对路径相对 exe |
|
||||
| SqliteQuery | String | | — | 绑定用的 SELECT 查询语句 |
|
||||
| AutoBindSqliteOnLoad | Boolean | False | 非默认才写 | 窗体载入时自动执行 BindSqliteData() |
|
||||
| CreateTableButton | String | | — | 设计时动作:属性面板点"…"按当前列生成建表 SQL/执行(值恒空,不序列化,绝不手写) |
|
||||
| BindDataButton | String | | — | 设计时动作:点"…"立即按三件套绑定一次(值恒空,不序列化) |
|
||||
| ClearBindButton | String | | — | 设计时动作:点"…"解除数据绑定(值恒空,不序列化) |
|
||||
| ColumnHeadersHeightSizeMode | DataGridViewColumnHeadersHeightSizeMode | EnableResizing | 非默认才写 | 遮蔽基类同名属性;默认 EnableResizing(表头可拖拽调高),与基类一致不用写 |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 DataGridView(79 个):AdjustedTopLeftHeaderBorderStyle(RO), AdvancedCellBorderStyle(RO), AdvancedColumnHeadersBorderStyle(RO), AdvancedRowHeadersBorderStyle(RO), AllowUserToAddRows, AllowUserToDeleteRows, AllowUserToOrderColumns, AllowUserToResizeColumns, AllowUserToResizeRows, AlternatingRowsDefaultCellStyle, AutoGenerateColumns, AutoSize, AutoSizeColumnsMode, AutoSizeRowsMode, BackColor, BackgroundColor, BackgroundImage, BackgroundImageLayout, BorderStyle, CellBorderStyle, ClipboardCopyMode, ColumnCount, ColumnHeadersBorderStyle, ColumnHeadersDefaultCellStyle, ColumnHeadersHeight, ColumnHeadersVisible, Columns(RO), CurrentCell, CurrentCellAddress(RO), CurrentRow(RO), DataMember, DataSource, DefaultCellStyle, DisplayRectangle(RO), EditMode, EditingControl(RO), EditingPanel(RO), EnableHeadersVisualStyles, FirstDisplayedCell, FirstDisplayedScrollingColumnHiddenWidth(RO), FirstDisplayedScrollingColumnIndex, FirstDisplayedScrollingRowIndex, ForeColor, Font, GridColor, HorizontalScrollingOffset, IsCurrentCellDirty(RO), IsCurrentCellInEditMode(RO), IsCurrentRowDirty(RO), MultiSelect, NewRowIndex(RO), Padding, ReadOnly, RowCount, RowHeadersBorderStyle, RowHeadersDefaultCellStyle, RowHeadersVisible, RowHeadersWidth, RowHeadersWidthSizeMode, Rows(RO), RowsDefaultCellStyle, RowTemplate, ScrollBars, SelectedCells(RO), SelectedColumns(RO), SelectedRows(RO), SelectionMode, ShowCellErrors, ShowCellToolTips, ShowEditingIcon, ShowRowErrors, SortedColumn(RO), SortOrder(RO), StandardTab, Text, TopLeftHeaderCell, UserSetCursor(RO), VerticalScrollingOffset(RO), VirtualMode
|
||||
- 继承 Control(65 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoScrollOffset, LayoutEngine(RO), BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
自有事件:无(仅基类事件)。
|
||||
- API:BindSqliteData()(按三件套绑定);CreateSqliteTableFromColumns(dbPath, tableName, dropIfExists)(按列建表,返回 SQL);PreviewCreateTableSql(tableName, dropIfExists)。
|
||||
- Designer.cs 列走 DataGridViewColumn 序列化(DataGridViewTextBoxColumn 等),与本库无关。
|
||||
- 排错:EDG.Ctor.Log.txt 记录"谁改了列高模式+调用栈";属性面板改了不生效 → 查 Designer.cs 残留显式模式行 + HintPath 是否指向旧版 DLL。
|
||||
|
||||
---
|
||||
|
||||
## 多选下拉框 MultiSelectComboBox
|
||||
|
||||
**继承**:`Smart.CustomComponents.MultiSelectComboBox` ← `System.Windows.Forms.Control`
|
||||
|
||||
带勾选、可搜索、可自定义输入的多选下拉框,选中项渲染成 Token(标签)。
|
||||
|
||||
### 属性(全部 19 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Items | ComboBoxItemCollection | | **Content 集合** | 下拉项字符串集合(Content 集合,逐条生成) |
|
||||
| Text | String | | — | 显示文本;运行时取已选用 SelectedItems/SelectedValues |
|
||||
| MultiSelect | Boolean | True | 非默认才写 | 允许多选(Token 叠加);false 时单选 |
|
||||
| AllowCustomInput | Boolean | False | 非默认才写 | 允许输入集合外自定义项 |
|
||||
| Searchable | Boolean | True | 非默认才写 | 下拉内搜索框开关 |
|
||||
| SelectedIndices | List<Int32> | | 不序列化 | |
|
||||
| SelectedValues | List<String> | | 不序列化 | |
|
||||
| SelectedItems | List<Object> | | 不序列化 | |
|
||||
| SelectedText | String | | 不序列化 | 隐藏,不序列化 |
|
||||
| MaxDropDownItems | Int32 | 8 | 非默认才写 | 下拉最大可见行数,默认 8 |
|
||||
| DropDownWidth | Int32 | 0 | 非默认才写 | |
|
||||
| DropDownHeight | Int32 | 0 | 非默认才写 | |
|
||||
| DroppedDown | Boolean | | 不序列化 | |
|
||||
| TokenBackColor | Color | | — | Token 底色,出厂 64,158,255 |
|
||||
| TokenForeColor | Color | | — | Token 文字色,出厂 White |
|
||||
| BorderColor | Color | | — | 边框色,出厂 173,178,184 |
|
||||
| HoverBorderColor | Color | | — | 悬停边框色,出厂 64,158,255 |
|
||||
| ArrowColor | Color | | — | 箭头色,出厂 96,98,102 |
|
||||
| ArrowHoverColor | Color | | — | 箭头悬停色,出厂 64,158,255 |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 Control(73 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoSize, AutoScrollOffset, LayoutEngine(RO), BackColor, BackgroundImage, BackgroundImageLayout, BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), DisplayRectangle(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, ForeColor, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件 | 签名 |
|
||||
|---|---|
|
||||
| SelectedItemsChanged | EventHandler |
|
||||
| DropDown | EventHandler |
|
||||
| DropDownClosed | EventHandler |
|
||||
| TextChangedEx | EventHandler |
|
||||
- API:SelectIndex(i)/UnselectIndex(i)/ToggleIndex(i)/ClearSelection()/SelectAll();ToggleDropDown()/OpenDropDown()/CloseDropDown()。
|
||||
- 事件:SelectedItemsChanged、DropDown、DropDownClosed、TextChangedEx。
|
||||
- 取值:selectedValues 属性(List<string>)或 SelectedIndices。
|
||||
|
||||
---
|
||||
|
||||
## 可调高下拉框 ResizableComboBox
|
||||
|
||||
**继承**:`Smart.CustomComponents.ResizableComboBox` ← `System.Windows.Forms.Control`
|
||||
|
||||
高度可自由调整的下拉框(标准 ComboBox 高度锁死,这是它存在的意义)。下拉面板自绘,支持排序/整合高度。
|
||||
|
||||
### 属性(全部 17 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Items | ComboBoxItemCollection | | **Content 集合** | 下拉项字符串集合(Content 集合) |
|
||||
| Text | String | | — | 当前显示文本 |
|
||||
| SelectedIndex | Int32 | -1 | 非默认才写 | 选中索引,默认 -1 |
|
||||
| SelectedItem | Object | | 不序列化 | 选中项(隐藏,运行时用) |
|
||||
| SelectedText | String | | 不序列化 | 隐藏,不序列化 |
|
||||
| DropDownStyle | ComboBoxStyle | DropDownList | 非默认才写 | DropDownList(只可选)/DropDown(可输入),默认 DropDownList |
|
||||
| MaxDropDownItems | Int32 | 8 | 非默认才写 | 默认 8 |
|
||||
| DropDownWidth | Int32 | 0 | 非默认才写 | |
|
||||
| DropDownHeight | Int32 | 0 | 非默认才写 | |
|
||||
| IntegralHeight | Boolean | True | 非默认才写 | 行高取整,默认 true |
|
||||
| Sorted | Boolean | False | 非默认才写 | 自动排序,默认 false |
|
||||
| Flat | Boolean | False | 非默认才写 | 扁平外观开关 |
|
||||
| BorderColor | Color | | — | 出厂 173,178,184 |
|
||||
| HoverBorderColor | Color | | — | 出厂 64,158,255 |
|
||||
| ArrowColor | Color | | — | 出厂 96,98,102 |
|
||||
| ArrowHoverColor | Color | | — | 出厂 64,158,255 |
|
||||
| DroppedDown | Boolean | | 不序列化 | 只读状态 |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 Control(73 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoSize, AutoScrollOffset, LayoutEngine(RO), BackColor, BackgroundImage, BackgroundImageLayout, BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), DisplayRectangle(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, ForeColor, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件 | 签名 |
|
||||
|---|---|
|
||||
| SelectedIndexChanged | EventHandler |
|
||||
| SelectedValueChanged | EventHandler |
|
||||
| DropDown | EventHandler |
|
||||
| DropDownClosed | EventHandler |
|
||||
| TextChangedEx | EventHandler |
|
||||
- API:ToggleDropDown()/OpenDropDown()/CloseDropDown()。
|
||||
- 事件:SelectedIndexChanged、SelectedValueChanged、DropDown、DropDownClosed、TextChangedEx。
|
||||
- Designer.cs 里 SelectedText 绝不写。
|
||||
|
||||
---
|
||||
|
||||
## 可调高文本框 ResizableTextBox
|
||||
|
||||
**继承**:`Smart.CustomComponents.ResizableTextBox` ← `System.Windows.Forms.Control`
|
||||
|
||||
高度可自由调整的单行文本框,带占位提示与三态边框色(常态/悬停/聚焦)。
|
||||
|
||||
### 属性(全部 15 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Text | String | | — | 文本内容 |
|
||||
| MaxLength | Int32 | 32767 | 非默认才写 | 最大长度,默认 32767 |
|
||||
| ReadOnly | Boolean | False | 非默认才写 | 只读 |
|
||||
| CharacterCasing | CharacterCasing | Normal | 非默认才写 | 大小写转换,默认 Normal |
|
||||
| PasswordChar | Char | \0(不掩码,写法 \0) | 非默认才写 | 密码掩码字符,默认 \0(不掩码) |
|
||||
| TextAlign | HorizontalAlignment | Left | 非默认才写 | Left/Center/Right,默认 Left |
|
||||
| PlaceholderText | String | | — | 占位提示文字 |
|
||||
| Flat | Boolean | False | 非默认才写 | 扁平外观(去圆角) |
|
||||
| BorderColor | Color | | — | 常态边框色,出厂 173,178,184 |
|
||||
| HoverBorderColor | Color | | — | 悬停边框色,出厂 64,158,255 |
|
||||
| FocusBorderColor | Color | | — | 聚焦边框色,出厂 64,158,255 |
|
||||
| PlaceholderColor | Color | | — | 占位文字色,出厂 192,196,206 |
|
||||
| SelectionStart | Int32 | | 不序列化 | |
|
||||
| SelectionLength | Int32 | | 不序列化 | |
|
||||
| SelectedText | String | | 不序列化 | |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 Control(73 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoSize, AutoScrollOffset, LayoutEngine(RO), BackColor, BackgroundImage, BackgroundImageLayout, BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), DisplayRectangle(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, ForeColor, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件 | 签名 |
|
||||
|---|---|
|
||||
| TextChanged | EventHandler |
|
||||
- 事件:TextChanged(基类)。
|
||||
- API:SelectAll()、Select(start, length)。
|
||||
|
||||
---
|
||||
|
||||
## OK/NG 状态灯 OKNGStatusControl
|
||||
|
||||
**继承**:`Smart.CustomComponents.OKNGStatusControl` ← `System.Windows.Forms.Control`
|
||||
|
||||
圆形 OK(绿)/NG(红) 状态灯,可绑定变量名由变量系统驱动。
|
||||
|
||||
### 属性(全部 2 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| BindVariable | String | | — | 绑定的变量名(变量绑定编辑器选择) |
|
||||
| IsOK | Boolean | | — | true=OK(绿),false=NG(红);源码初始值 true |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 Control(74 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoSize, AutoScrollOffset, LayoutEngine(RO), BackColor, BackgroundImage, BackgroundImageLayout, BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), DisplayRectangle(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, ForeColor, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Text, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
自有事件:无(仅基类事件)。
|
||||
- **绝不写 Text**(自绘覆盖文字)。
|
||||
- 运行时 this.okngStatusControl1.IsOK = false; 切红灯。
|
||||
|
||||
---
|
||||
|
||||
## 侧边菜单 SideMenuPanel
|
||||
|
||||
**继承**:`Smart.CustomComponents.SideMenuPanel` ← `System.Windows.Forms.UserControl`
|
||||
|
||||
折叠式侧边菜单 + 内容页面:MenuItems 声明菜单树(可递归子项),Pages 声明页面(MenuKey 关联菜单项),点击菜单自动切页。
|
||||
|
||||
### 属性(全部 19 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| MenuItems | BindingList<SideMenuItem> | | **Content 集合** | 菜单项集合(Content,条目 SideMenuItem 可递归 Children) |
|
||||
| Pages | BindingList<SideMenuPage> | | **Content 集合** | 页面集合(Content,条目 SideMenuPage : Panel,设计器里页面字段名 sideMenuPageN) |
|
||||
| ActivePage | SideMenuPage | | 不序列化 | 当前页(只读,隐藏) |
|
||||
| MenuWidth | Int32 | 160 | 非默认才写 | 菜单宽 px,默认 160 |
|
||||
| MenuCollapsed | Boolean | False | 非默认才写 | 折叠菜单,默认 false |
|
||||
| ItemHeight | Int32 | 34 | 非默认才写 | 菜单行高 px,默认 34 |
|
||||
| Indent | Int32 | 18 | 非默认才写 | 子级缩进 px,默认 18 |
|
||||
| IconSize | Int32 | 18 | 非默认才写 | 图标尺寸 px,默认 18 |
|
||||
| ShowIcons | Boolean | True | 非默认才写 | 显示图标,默认 true |
|
||||
| MenuBackColor | Color | | — | 出厂 White |
|
||||
| MenuForeColor | Color | | — | 出厂 80,80,80 |
|
||||
| SelectedColor | Color | | — | 选中底色,出厂 230,247,255 |
|
||||
| SelectedForeColor | Color | | — | 选中文字色,出厂 24,144,255 |
|
||||
| HoverColor | Color | | — | 悬停底色,出厂 245,245,245 |
|
||||
| ArrowColor | Color | | — | 展开箭头色,出厂 150,150,150 |
|
||||
| SplitterColor | Color | | — | 菜单/内容分隔线色,出厂 235,235,235 |
|
||||
| ContentPanel | SideMenuContentPanel | | 不序列化 | 内容容器(只读,隐藏;运行时向它加控件) |
|
||||
| SelectedNode | SideMenuItem | | 不序列化 | 当前选中菜单项(隐藏) |
|
||||
| ForeColor | Color | | 不序列化 | 隐藏(用 MenuForeColor 系列代替) |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 UserControl(5 个):AutoSize, AutoSizeMode, AutoValidate, BorderStyle, Text
|
||||
- 继承 ContainerControl(6 个):AutoScaleDimensions, AutoScaleMode, BindingContext, ActiveControl, CurrentAutoScaleDimensions(RO), ParentForm(RO)
|
||||
- 继承 ScrollableControl(8 个):AutoScroll, AutoScrollMargin, AutoScrollPosition, AutoScrollMinSize, DisplayRectangle(RO), HorizontalScroll(RO), VerticalScroll(RO), DockPadding(RO)
|
||||
- 继承 Control(69 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoScrollOffset, LayoutEngine(RO), BackColor, BackgroundImage, BackgroundImageLayout, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件 | 签名 |
|
||||
|---|---|
|
||||
| SelectedNodeChanged | EventHandler< |
|
||||
- API:SelectNode(SideMenuItem)、ShowPage(string key)、CanDropAt(Point)、GetActiveDropTarget()。
|
||||
- 事件:SelectedNodeChanged(参数 SelectedNodeChangedEventArgs.Node)。
|
||||
- 设计期默认集合内容只能在 Designer.Initialize 且 !host.Loading 时注入——绝不写进构造函数(会运行时翻倍)。
|
||||
|
||||
---
|
||||
|
||||
## 功能区 RibbonStrip
|
||||
|
||||
**继承**:`Smart.CustomComponents.RibbonStrip` ← `System.Windows.Forms.UserControl`
|
||||
|
||||
Excel 风格功能区:Tabs(选项卡)→ Groups(分组)→ Buttons(按钮)三层集合。设计器支持拖拽重排按钮、双击选项卡改名;点击按钮在运行时统一走 ButtonClick 事件。
|
||||
|
||||
### 属性(全部 10 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Tabs | BindingList<RibbonTab> | | **Content 集合** | 选项卡集合(Content,条目 RibbonTab.Groups→RibbonGroup.Buttons→RibbonButton) |
|
||||
| ActiveTabIndex | Int32 | 0 | 非默认才写 | 当前选项卡索引,默认 0 |
|
||||
| ActiveTab | RibbonTab | | 不序列化 | 当前选项卡(只读,隐藏) |
|
||||
| AccentColor | Color | A=255, R=33, G=115, B=70 | 非默认才写 | 主题强调色(选中选项卡下划线等),出厂 33,115,70(Excel 绿) |
|
||||
| TabBarColor | Color | A=255, R=243, G=242, B=241 | 非默认才写 | 选项卡条底色,出厂 243,242,241 |
|
||||
| ContentColor | Color | White | 非默认才写 | 内容区底色,出厂 White |
|
||||
| SeparatorColor | Color | A=255, R=180, G=180, B=180 | 非默认才写 | 组分隔线色,出厂 180,180,180 |
|
||||
| ForeColor | Color | | 不序列化 | 隐藏(组名/文字色内部固定) |
|
||||
| BackgroundImage | Image | | 不序列化 | |
|
||||
| BackgroundImageLayout | ImageLayout | | 不序列化 | |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 UserControl(5 个):AutoSize, AutoSizeMode, AutoValidate, BorderStyle, Text
|
||||
- 继承 ContainerControl(6 个):AutoScaleDimensions, AutoScaleMode, BindingContext, ActiveControl, CurrentAutoScaleDimensions(RO), ParentForm(RO)
|
||||
- 继承 ScrollableControl(8 个):AutoScroll, AutoScrollMargin, AutoScrollPosition, AutoScrollMinSize, DisplayRectangle(RO), HorizontalScroll(RO), VerticalScroll(RO), DockPadding(RO)
|
||||
- 继承 Control(67 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoScrollOffset, LayoutEngine(RO), BackColor, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件 | 签名 |
|
||||
|---|---|
|
||||
| ButtonClick | EventHandler< |
|
||||
- 事件:ButtonClick(object sender, RibbonButtonEventArgs e);e.Tab/e.Group/e.Button 分别为所在选项卡/分组/按钮(用 e.Button.Text 分发)。
|
||||
- 图标:RibbonButton.Image 序列化为 `buttonN.Image = RibbonButton.FromFile("路径")`(有来源路径时;ImageFile 属性本身隐藏)。
|
||||
- Designer.cs 生成规则(三层 Content 集合)见 codegen.md §2.5。
|
||||
|
||||
---
|
||||
|
||||
## 变量显示标签 VariableDisplayLabel
|
||||
|
||||
**继承**:`Smart.CustomComponents.VariableDisplayLabel` ← `System.Windows.Forms.Control`
|
||||
|
||||
只读展示绑定变量当前值的标签,值变化触发 ValueChanged。
|
||||
|
||||
### 属性(全部 3 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| BindVariable | String | | — | 绑定的变量名(变量绑定编辑器选择) |
|
||||
| DisplayValue | String | | — | 当前显示值,默认 "——"(运行时由变量系统刷新) |
|
||||
| TextAlign | ContentAlignment | MiddleLeft | 非默认才写 | ContentAlignment,默认 MiddleLeft |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 Control(74 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoSize, AutoScrollOffset, LayoutEngine(RO), BackColor, BackgroundImage, BackgroundImageLayout, BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), DisplayRectangle(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, ForeColor, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Text, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), Padding, ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件 | 签名 |
|
||||
|---|---|
|
||||
| ValueChanged | EventHandler |
|
||||
- 只读控件:不接受输入(PreProcessMessage 恒 false)。
|
||||
- **⚠ 没有 BorderStyle**(继承 Control 不是 Label,s23 案例抄错即设计器中止加载);要边框容器就用 Panel/GroupBox 包一层。
|
||||
- 事件:ValueChanged。
|
||||
|
||||
---
|
||||
|
||||
## 变量编辑框 VariableEditTextBox
|
||||
|
||||
**继承**:`Smart.CustomComponents.VariableEditTextBox` ← `System.Windows.Forms.TextBox`
|
||||
|
||||
标准 TextBox + 变量绑定:BindVariable 指向变量,Value 与 Text 同步。
|
||||
|
||||
### 属性(全部 2 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| BindVariable | String | | — | 绑定的变量名(变量绑定编辑器选择) |
|
||||
| Value | String | | 不序列化 | 与 Text 同步的镜像值(隐藏,不序列化) |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 TextBox(11 个):AcceptsReturn, AutoCompleteMode, AutoCompleteSource, AutoCompleteCustomSource, CharacterCasing, Multiline, PasswordChar, ScrollBars, Text, TextAlign, UseSystemPasswordChar
|
||||
- 继承 TextBoxBase(21 个):AcceptsTab, ShortcutsEnabled, AutoSize, BackColor, BackgroundImage, BackgroundImageLayout, BorderStyle, CanUndo(RO), ForeColor, HideSelection, Lines, MaxLength, Modified, Padding, PreferredHeight(RO), ReadOnly, SelectedText, SelectionLength, SelectionStart, TextLength(RO), WordWrap
|
||||
- 继承 Control(67 个):AccessibilityObject(RO), AccessibleDefaultActionDescription, AccessibleDescription, AccessibleName, AccessibleRole, AllowDrop, Anchor, AutoScrollOffset, LayoutEngine(RO), BindingContext, Bottom(RO), Bounds, CanFocus(RO), CanSelect(RO), Capture, CausesValidation, ClientRectangle(RO), ClientSize, CompanyName(RO), ContainsFocus(RO), ContextMenu, ContextMenuStrip, Controls(RO), Created(RO), Cursor, DataBindings(RO), DeviceDpi(RO), DisplayRectangle(RO), IsDisposed(RO), Disposing(RO), Dock, Enabled, Focused(RO), Font, Handle(RO), HasChildren(RO), Height, IsHandleCreated(RO), InvokeRequired(RO), IsAccessible, IsMirrored(RO), Left, Location, Margin, MaximumSize, MinimumSize, Name, Parent, ProductName(RO), ProductVersion(RO), RecreatingHandle(RO), Region, Right(RO), RightToLeft, Site, Size, TabIndex, TabStop, Tag, Top, TopLevelControl(RO), UseWaitCursor, Visible, Width, WindowTarget, PreferredSize(RO), ImeMode
|
||||
- 继承 Component(1 个):Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
自有事件:无(仅基类事件)。
|
||||
- 其余全部属性/事件与标准 TextBox 一致(Text/Multiline/PasswordChar/TextChanged…见 winforms-controls.md#TextBox)。
|
||||
- Designer.cs 只需写 BindVariable 与常用 TextBox 属性。
|
||||
- **⚠ 没有 PlaceholderText**(那是 ResizableTextBox 的自有属性,s23 案例抄错即设计器中止加载);要占位提示就用 ResizableTextBox。
|
||||
|
||||
---
|
||||
|
||||
## 日志记录器 Logger(非可视组件)
|
||||
|
||||
**继承**:`Smart.CustomComponents.Logger` ← `System.ComponentModel.Component`
|
||||
|
||||
拖到窗体托盘的日志组件,代码里直接调中文方法写日志。
|
||||
|
||||
### 属性(全部 4 个自有属性)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| 日志文件名 | String | logs\app.log | 非默认才写 | 日志文件路径,默认 logs\app.log |
|
||||
| 日志级别 | 日志级别枚举 | 信息 | 非默认才写 | 调试/信息/警告/错误/致命,默认 信息 |
|
||||
| 按天滚动 | Boolean | True | 非默认才写 | 按日期滚动文件,默认 true |
|
||||
| 最多保留份数 | Int32 | 30 | 非默认才写 | 滚动保留份数,默认 30 |
|
||||
|
||||
### 继承属性(名称全列)
|
||||
|
||||
- 继承 Component(2 个):Site, Container(RO)
|
||||
|
||||
### 事件
|
||||
|
||||
自有事件:无(仅基类事件)。
|
||||
- API:写调试(msg)/写信息(msg)/写警告(msg)/写错误(msg)/写异常(msg, ex);设计模式下调用是空操作。
|
||||
- 事件:无(仅 Disposed)。
|
||||
|
||||
---
|
||||
|
||||
## Content 集合条目类型(全部属性)
|
||||
|
||||
### DockWindowItem(DockSurface.DockWindows 的条目)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| ClassName | String | DockWindow | 非默认才写 | |
|
||||
| Title | String | 停靠窗口 | 非默认才写 | |
|
||||
| PersistString | String | | 不序列化 | |
|
||||
| DockState | DockState | DockLeft | 非默认才写 | |
|
||||
| DefaultWidth | Int32 | 220 | 非默认才写 | |
|
||||
| DefaultHeight | Int32 | 180 | 非默认才写 | |
|
||||
| FixedPosition | Boolean | False | 非默认才写 | |
|
||||
| ShowCloseButton | Boolean | True | 非默认才写 | |
|
||||
| ContentXml | String | | 不序列化 | |
|
||||
| ColumnsXml | String | | 不序列化 | |
|
||||
| SourceFile | String | | 不序列化 | |
|
||||
| HasContent | Boolean | | 不序列化 | |
|
||||
- Hidden 属性(PersistString/ContentXml/ColumnsXml/SourceFile)由三文件生成器消费,绝不手写。
|
||||
- HasContent 只读,指示是否已挂窗口内容。
|
||||
|
||||
### SideMenuItem(SideMenuPanel.MenuItems 的条目)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Text | String | | — | |
|
||||
| Icon | Image | | — | |
|
||||
| IsExpanded | Boolean | False | 非默认才写 | |
|
||||
| Children | BindingList<SideMenuItem> | | **Content 集合** | |
|
||||
- Children 可递归嵌套子菜单(Content 集合)。
|
||||
|
||||
### SideMenuPage(SideMenuPanel.Pages 的条目): Panel
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| MenuKey | String | | — | |
|
||||
- MenuKey 与某个菜单项对应(点击该菜单项显示本页);其余属性同 Panel。
|
||||
|
||||
### RibbonTab(RibbonStrip.Tabs 的条目)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Text | String | | — | |
|
||||
| Groups | BindingList<RibbonGroup> | | **Content 集合** | |
|
||||
- Groups 为分组集合(Content)。
|
||||
|
||||
### RibbonGroup(RibbonTab.Groups 的条目)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Text | String | | — | |
|
||||
| Buttons | BindingList<RibbonButton> | | **Content 集合** | |
|
||||
- Buttons 为按钮集合(Content)。
|
||||
|
||||
### RibbonButton(RibbonGroup.Buttons 的条目)
|
||||
|
||||
| 属性 | 类型 | 默认值 | 序列化 | 说明 |
|
||||
|---|---|---|---|---|
|
||||
| Name | String | | — | |
|
||||
| ImageFile | String | | 不序列化 | |
|
||||
| Image | Image | | — | |
|
||||
| Text | String | | — | |
|
||||
| SizeStyle | RibbonButtonSize | Small | 非默认才写 | |
|
||||
| Enabled | Boolean | True | 非默认才写 | |
|
||||
- Image 有路径时序列化为 `= RibbonButton.FromFile("路径")`;ImageFile 本身隐藏。
|
||||
- Name 用于设计器标识;运行时分发用 Text。
|
||||
|
||||
---
|
||||
|
||||
## 枚举全值表(Designer.cs 一律写英文标识符,ImageButton 枚举例外——本身就是中文)
|
||||
|
||||
### RoundedButtonStyle:Primary=0、Success=1、Warning=2、Danger=3、Info=4、Dark=5、Light=6、Outline=7
|
||||
|
||||
### Smart.CustomComponents.DockSurface+SurfaceTheme:VS2015Light=0、VS2015Blue=1、VS2015Dark=2、VS2013Light=3、VS2013Blue=4、VS2013Dark=5、VS2012Light=6、VS2005=7、VS2003=8、Default=9
|
||||
|
||||
### Smart.CustomComponents.DockSurface+TabTipDirection:Left=0、Right=1
|
||||
|
||||
### RibbonButtonSize:Large=0、Small=1
|
||||
|
||||
### 日志级别枚举:调试=0、信息=1、警告=2、错误=3、致命=4
|
||||
|
||||
### 图片锚点位置:Center=0、N=1、S=2、E=3、W=4
|
||||
|
||||
### 图片缩放模式:Original=0、Fit=1、Stretch=2、Tile=3
|
||||
|
||||
### 图文布局枚举:文字居中=0、图下文上=1、图上文下=2、图右文左=3、图左文右=4、纯图片=5
|
||||
|
||||
## DockState 枚举(DockWindowItem.DockState 用,WeifenLuo)
|
||||
|
||||
值:Unknown、Float、DockTop、DockBottom、DockLeft、DockRight、Document、Hidden(常用:DockLeft/DockRight/DockBottom/Document/Float)。
|
||||
|
||||
## 共享基类属性速览
|
||||
|
||||
大多数增强控件继承 System.Windows.Forms.Control;Control 基类全部 73 个属性(含默认值)的完整表格见 `winforms-controls.md` 第 0 节"Control 基类属性总表",此处不重复。
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
# 整合方案 v3(按用户要求调整):插件改源码、左侧工具栏 + 网页 CAD 文档区
|
||||
|
||||
## 布局(Smart.CustomComponents.DockSurface 停靠面板)
|
||||
| 窗口 | 位置 | 内容 |
|
||||
|---|---|---|
|
||||
| **QuoteWindow(插件工具栏)** | **DockLeft**(宽 ~420,不可关闭) | Form3 报价界面(cadcj 改造版,作为左侧工具面板) |
|
||||
| **NewProjectWindow** | DockLeft(第二个标签页或 DockBottom) | 新建项目窗体 |
|
||||
| **CadViewerWindow** | **Document(中央文档区,独立窗口)** | WebView2 → http://localhost:5173 网页 CAD |
|
||||
|
||||
## A. 网页端(CAD-Viewer 本地仓库,packages/cad-viewer-example 新增 src/cadbridge.ts)
|
||||
与 v2 相同:
|
||||
- 连续扒图命令(仿 AcApRectCmd 两点 jig;正向=逐条/反向=合并;画彩色 AcDbPolyline 框;回传 text/frameHandle/textHandle)
|
||||
- 消息监听:zoomToHandle / deleteHandles / clearHighlight;删除实体回发 entityErased
|
||||
- 注册 patu/paxh/payj 命令
|
||||
|
||||
## B. 插件改造(直接修改 cadcj 源码,生成 cadbt 专用版本)
|
||||
- 复制 Form3.cs/Form3.ui.cs/新建项目.cs/新建项目.ui.cs 到 cadbt 项目(cadcj 原插件目录不动,保持 AutoCAD 版独立)
|
||||
- Form3 中约 15 处 `Autodesk.AutoCAD.*` 调用直接改写为 `CadBridge` 调用(扒图命令、ZoomToFrameHandle、DeleteFramesByHandles、ObjectErased 订阅→桥事件、SaveCurrentDwg 删除)
|
||||
- PaMode/PaTextResult 保留;PaTextSelector 的桩改为 CadBridge.StartPick / EntityErased 事件
|
||||
- 好处:无 stub 假类型,编译不依赖 acdbmgd,代码可读可维护
|
||||
|
||||
## C. 宿主程序(cadbt)
|
||||
- csproj:增强组件九件套 + AdvancedDataGridView + System.Data.SQLite(安装目录/..\cadcj HintPath)+ WebView2(nuget 解压到 libs\webview2,x86 loader 复制输出)
|
||||
- MainForm.Designer.cs:dockSurface1 + DockWindows 集合(左:QuoteWindow/NewProjectWindow;Document:CadViewerWindow)
|
||||
- CadViewerWindow:代码创建 WebView2,导航 localhost:5173,WebMessageReceived 分发到 CadBridge 事件;失败提示启动 pnpm dev
|
||||
- MainForm.DockWindows.cs:EnsureDefaultTheme + Show
|
||||
|
||||
## D. 验证
|
||||
编译 cadbt → 运行 → 三面板布局正确 → 联调扒图/定位/删框链路;网页端 pnpm dev 已验证可跑
|
||||
|
||||
## 说明
|
||||
- cadcj 原目录不动,AutoCAD 插件版继续可用;cadbt 用改造副本
|
||||
- 后续可选:pnpm build 出 dist + SetVirtualHostNameToFolderMapping 实现离线嵌入
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
# SharpDevelop 5.2
|
||||
VisualStudioVersion = 12.0.20827.3
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cadbt", "cadbt\cadbt.csproj", "{0A91D57B-B279-4672-9440-78088D022EB1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0A91D57B-B279-4672-9440-78088D022EB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0A91D57B-B279-4672-9440-78088D022EB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0A91D57B-B279-4672-9440-78088D022EB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0A91D57B-B279-4672-9440-78088D022EB1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Submodule
+1
Submodule cadbt/cadbt/CAD-Viewer added at c5962cc3f9
@@ -0,0 +1,213 @@
|
||||
// ============================================================
|
||||
// CadBridge:WinForms 宿主与网页 CAD(CAD-Viewer)之间的互操作桥
|
||||
// · 下行(C#→网页):PostMessage 发 startPick/zoomToHandle/deleteHandles/clearHighlight
|
||||
// · 上行(网页→C#):WebMessageReceived 收 paPicked/entityErased/paExited
|
||||
// · 扒图结果与实体删除事件以 C# 事件形式提供给 Form3(替代 AutoCAD 版
|
||||
// 的 PaTextSelector.BatchTextsPicked 与 Database.ObjectErased)
|
||||
// ============================================================
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Script.Serialization;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Microsoft.Web.WebView2.WinForms;
|
||||
|
||||
namespace cadbt
|
||||
{
|
||||
public static class CadBridge
|
||||
{
|
||||
private static WebView2 _wv;
|
||||
private static readonly JavaScriptSerializer _json = new JavaScriptSerializer();
|
||||
|
||||
/// <summary>网页 CAD 是否已挂载(WebView2 已附加)。</summary>
|
||||
public static bool IsViewerReady
|
||||
{
|
||||
get { return _wv != null && _wv.CoreWebView2 != null; }
|
||||
}
|
||||
|
||||
/// <summary>网页 CAD 中实体被删除(句柄十六进制字符串)。</summary>
|
||||
public static event Action<string> EntityErased;
|
||||
|
||||
/// <summary>网页 CAD 扒图完成一批(图号/箱号/元件)。</summary>
|
||||
public static event Action<MyApp.PaMode, List<MyApp.PaTextResult>> PaPicked;
|
||||
|
||||
/// <summary>用户按 ESC 退出网页扒图连续模式。</summary>
|
||||
public static event Action PaExited;
|
||||
|
||||
/// <summary>用户按 F1(焦点在网页或宿主时都触发)——元件两两合并。</summary>
|
||||
public static event Action F1Requested;
|
||||
|
||||
/// <summary>用户按 F2——元件三个一组合并。</summary>
|
||||
public static event Action F2Requested;
|
||||
|
||||
/// <summary>用户按 F3——元件四个一组合并。</summary>
|
||||
public static event Action F3Requested;
|
||||
|
||||
/// <summary>触发 F1 请求(消息过滤器等宿主侧来源调用)。</summary>
|
||||
public static void RaiseF1()
|
||||
{
|
||||
if (F1Requested != null) F1Requested();
|
||||
}
|
||||
|
||||
/// <summary>触发 F2 请求(消息过滤器等宿主侧来源调用)。</summary>
|
||||
public static void RaiseF2()
|
||||
{
|
||||
if (F2Requested != null) F2Requested();
|
||||
}
|
||||
|
||||
/// <summary>触发 F3 请求(消息过滤器等宿主侧来源调用)。</summary>
|
||||
public static void RaiseF3()
|
||||
{
|
||||
if (F3Requested != null) F3Requested();
|
||||
}
|
||||
|
||||
/// <summary>把 WebView2 挂到桥上(在控件初始化完成后调用一次)。</summary>
|
||||
public static void Attach(WebView2 wv)
|
||||
{
|
||||
if (_wv == wv) return;
|
||||
if (_wv != null) _wv.WebMessageReceived -= OnMessage;
|
||||
_wv = wv;
|
||||
if (_wv != null) _wv.WebMessageReceived += OnMessage;
|
||||
}
|
||||
|
||||
private static void OnMessage(object sender, CoreWebView2WebMessageReceivedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.TryGetWebMessageAsString() != null) return; // 只处理 JSON 消息
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
var msg = _json.Deserialize<Dictionary<string, object>>(e.WebMessageAsJson);
|
||||
if (msg == null || !msg.ContainsKey("type")) return;
|
||||
string type = msg["type"] as string;
|
||||
|
||||
if (type == "paPicked")
|
||||
{
|
||||
int mode = Convert.ToInt32(msg["mode"]);
|
||||
var results = new List<MyApp.PaTextResult>();
|
||||
var arr = msg["results"] as System.Collections.ArrayList;
|
||||
if (arr != null)
|
||||
{
|
||||
foreach (Dictionary<string, object> item in arr)
|
||||
{
|
||||
results.Add(new MyApp.PaTextResult
|
||||
{
|
||||
Text = Convert.ToString(item["text"]) ?? "",
|
||||
FrameHandle = Convert.ToString(item["frameHandle"]) ?? "",
|
||||
TextHandle = Convert.ToString(item["textHandle"]) ?? ""
|
||||
});
|
||||
}
|
||||
}
|
||||
// WebMessageReceived 在 UI 线程触发,可直接调用 Form3
|
||||
if (PaPicked != null)
|
||||
PaPicked((MyApp.PaMode)mode, results);
|
||||
}
|
||||
else if (type == "entityErased")
|
||||
{
|
||||
string handle = Convert.ToString(msg["handle"]);
|
||||
if (!string.IsNullOrWhiteSpace(handle) && EntityErased != null)
|
||||
EntityErased(handle);
|
||||
}
|
||||
else if (type == "paError")
|
||||
{
|
||||
string errMsg = Convert.ToString(msg["message"]);
|
||||
System.Windows.Forms.MessageBox.Show(
|
||||
"网页 CAD 返回错误:\n" + errMsg,
|
||||
"扒数据", System.Windows.Forms.MessageBoxButtons.OK,
|
||||
System.Windows.Forms.MessageBoxIcon.Warning);
|
||||
}
|
||||
else if (type == "f1")
|
||||
{
|
||||
if (F1Requested != null) F1Requested();
|
||||
}
|
||||
else if (type == "f2")
|
||||
{
|
||||
if (F2Requested != null) F2Requested();
|
||||
}
|
||||
else if (type == "f3")
|
||||
{
|
||||
if (F3Requested != null) F3Requested();
|
||||
}
|
||||
else if (type == "paExited")
|
||||
{
|
||||
if (PaExited != null) PaExited();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 消息格式异常直接忽略,不影响宿主
|
||||
}
|
||||
}
|
||||
|
||||
private static void Post(object msg)
|
||||
{
|
||||
if (!IsViewerReady) return;
|
||||
try
|
||||
{
|
||||
_wv.CoreWebView2.PostWebMessageAsJson(_json.Serialize(msg));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送扒图命令。接受原 CAD 命令名(CADPATU/CADPAXH/CADPAYJ/CADPACL)
|
||||
/// 或网页命令名(patu/paxh/payj)。
|
||||
/// </summary>
|
||||
public static void SendPaCommand(string cmd)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(cmd)) return;
|
||||
string c = cmd.Trim().ToUpperInvariant();
|
||||
int mode = 0;
|
||||
if (c == "CADPATU" || c == "PATU") mode = 0;
|
||||
else if (c == "CADPAXH" || c == "PAXH") mode = 1;
|
||||
else if (c == "CADPAYJ" || c == "PAYJ") mode = 2;
|
||||
else if (c == "CADPACL" || c == "PACL")
|
||||
{
|
||||
ClearHighlight();
|
||||
return;
|
||||
}
|
||||
else return;
|
||||
Post(new { type = "startPick", mode = mode });
|
||||
}
|
||||
|
||||
/// <summary>缩放居中到指定 Handle 的实体并高亮(点击表格行定位框)。</summary>
|
||||
public static void ZoomToFrameHandle(string handle)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(handle)) return;
|
||||
Post(new { type = "zoomToHandle", handle = handle });
|
||||
}
|
||||
|
||||
/// <summary>批量删除指定 Handle 的实体(删表格行联动删框)。</summary>
|
||||
public static void DeleteFramesByHandles(IEnumerable<string> handles)
|
||||
{
|
||||
if (handles == null) return;
|
||||
var list = new List<string>();
|
||||
foreach (string h in handles)
|
||||
if (!string.IsNullOrWhiteSpace(h)) list.Add(h);
|
||||
if (list.Count == 0) return;
|
||||
Post(new { type = "deleteHandles", handles = list });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并蒙版:保留 keep 句柄的蒙版并扩大到所有 removes 蒙版的并集,
|
||||
/// 被合并行的蒙版从网页 CAD 移除(元件合并时蒙版同步合并)。
|
||||
/// </summary>
|
||||
public static void MergeMasks(string keep, System.Collections.Generic.IEnumerable<string> removes)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(keep)) return;
|
||||
var list = new List<string>();
|
||||
if (removes != null)
|
||||
foreach (string h in removes)
|
||||
if (!string.IsNullOrWhiteSpace(h) && h != keep) list.Add(h);
|
||||
if (list.Count == 0) return;
|
||||
Post(new { type = "mergeMasks", keep = keep, removes = list });
|
||||
}
|
||||
|
||||
/// <summary>清除网页 CAD 中的所有高亮。</summary>
|
||||
public static void ClearHighlight()
|
||||
{
|
||||
Post(new { type = "clearHighlight" });
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
namespace cadbt.DockWindows
|
||||
{
|
||||
partial class CadViewerWindow
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null)) components.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Designer generated code
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// CadViewerWindow
|
||||
//
|
||||
this.Name = "CadViewerWindow";
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// ============================================================
|
||||
// 停靠窗口:CAD 查看器(中央文档区)
|
||||
// WebView2 承载网页版 CAD-Viewer(localhost:5173),并挂到 CadBridge。
|
||||
// ★ 网页服务自启动:exe 旁 node\node.exe + CAD-Viewer\dist\server.cjs
|
||||
// (静态产物零依赖,客户机无需 Node 环境/pnpm dev;主程序同款端口探测模式)
|
||||
// ============================================================
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Web.WebView2.WinForms;
|
||||
|
||||
namespace cadbt.DockWindows
|
||||
{
|
||||
/// <summary>停靠窗口:网页 CAD 查看器(DockState.Document)。</summary>
|
||||
public partial class CadViewerWindow : WeifenLuo.WinFormsUI.Docking.DockContent
|
||||
{
|
||||
private WebView2 _webView;
|
||||
private Label _hint;
|
||||
|
||||
/// <summary>静态服务进程句柄(退出时杀进程树)。</summary>
|
||||
private static Process _serverProcess;
|
||||
|
||||
public CadViewerWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Text = "CAD 查看器";
|
||||
TabText = "CAD 查看器";
|
||||
FormClosed += (s, e) => 停止网页服务();
|
||||
LoadViewer();
|
||||
}
|
||||
|
||||
/// <summary>CAD 网页服务端口(与 server.cjs 一致)。</summary>
|
||||
private const int 服务端口 = 5173;
|
||||
|
||||
private async void LoadViewer()
|
||||
{
|
||||
try
|
||||
{
|
||||
_webView = new WebView2();
|
||||
_webView.Dock = DockStyle.Fill;
|
||||
Controls.Add(_webView);
|
||||
await _webView.EnsureCoreWebView2Async(null);
|
||||
CadBridge.Attach(_webView);
|
||||
|
||||
// ★ 禁用浏览器快捷键(F5/Ctrl+R/Ctrl+F 等):刷新会丢掉所有扒图蒙版
|
||||
_webView.CoreWebView2.Settings.AreBrowserAcceleratorKeysEnabled = false;
|
||||
|
||||
// ★ 先确保静态服务在线(复刻主程序 StartPriceWebView 的探测+拉起模式)
|
||||
启动网页服务();
|
||||
|
||||
// 服务不可达时给用户明确提示
|
||||
_webView.CoreWebView2.NavigationCompleted += (s, e) =>
|
||||
{
|
||||
if (!e.IsSuccess && _hint == null)
|
||||
ShowLoadHint("无法连接网页 CAD 服务(端口 " + 服务端口 + ")。\r\n请确认 cadbt\\node\\node.exe 与 cadbt\\CAD-Viewer\\dist 目录完整,然后点击\"重新加载\"。");
|
||||
};
|
||||
_webView.CoreWebView2.Navigate("http://localhost:" + 服务端口 + "/");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowLoadHint("WebView2 初始化失败: " + ex.Message + "\r\n请确认系统已安装 Microsoft Edge WebView2 运行时。");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>启动 CAD 网页静态服务(端口已通=复用;不通=node server.cjs 后台拉起)。</summary>
|
||||
private void 启动网页服务()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (端口已通()) return; // 上次实例或手动 dev server 已在服务
|
||||
|
||||
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string nodeExe = Path.Combine(baseDir, "node", "node.exe");
|
||||
// dist 与 server.cjs 同目录;找不到时尝试上一级 CAD-Viewer 结构(开发机)
|
||||
// 安装布局: {app}\cadbt\CAD-Viewer\dist\ (make_package 已展平 packages 层)
|
||||
string serverScript = Path.Combine(baseDir, "CAD-Viewer", "dist", "server.cjs");
|
||||
if (!File.Exists(serverScript))
|
||||
{
|
||||
// 开发机回退: bin\Debug 上两级 = cadbt\cadbt\,保留 monorepo 原始层级
|
||||
serverScript = Path.GetFullPath(Path.Combine(baseDir, @"..\..\CAD-Viewer\packages\cad-viewer-example\dist\server.cjs"));
|
||||
}
|
||||
|
||||
if (!File.Exists(nodeExe) || !File.Exists(serverScript))
|
||||
{
|
||||
WriteDiag("[CAD服务] 缺少 node.exe 或 server.cjs: node=" + nodeExe + " script=" + serverScript);
|
||||
return;
|
||||
}
|
||||
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = nodeExe,
|
||||
Arguments = "\"" + Path.GetFullPath(serverScript) + "\"",
|
||||
WorkingDirectory = Path.GetDirectoryName(Path.GetFullPath(serverScript)),
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
};
|
||||
_serverProcess = Process.Start(psi);
|
||||
WriteDiag("[CAD服务] 已启动 node PID=" + (_serverProcess == null ? "?" : _serverProcess.Id.ToString()));
|
||||
|
||||
// 等端口就绪(最多 8 秒)
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
System.Threading.Thread.Sleep(200);
|
||||
if (端口已通()) { WriteDiag("[CAD服务] 端口就绪, 耗时 " + (i + 1) * 200 + "ms"); return; }
|
||||
}
|
||||
WriteDiag("[CAD服务] 端口 8 秒未就绪(继续导航,由 NavigationCompleted 提示)");
|
||||
}
|
||||
catch (Exception ex) { WriteDiag("[CAD服务] 启动异常: " + ex.Message); }
|
||||
}
|
||||
|
||||
/// <summary>快速探测本机端口。</summary>
|
||||
private static bool 端口已通()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var client = new TcpClient())
|
||||
{
|
||||
var ar = client.BeginConnect("127.0.0.1", 服务端口, null, null);
|
||||
bool ok = ar.AsyncWaitHandle.WaitOne(400);
|
||||
if (ok && client.Connected) { client.EndConnect(ar); return true; }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>退出清理:杀 node 静态服务进程树(taskkill /T 覆盖可能的子进程)。</summary>
|
||||
private void 停止网页服务()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_serverProcess != null && !_serverProcess.HasExited)
|
||||
{
|
||||
int pid = _serverProcess.Id;
|
||||
Process.Start(new ProcessStartInfo("taskkill", "/PID " + pid + " /T /F")
|
||||
{
|
||||
CreateNoWindow = true, UseShellExecute = false, WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
WriteDiag("[CAD服务] 已停止 PID=" + pid);
|
||||
}
|
||||
_serverProcess = null;
|
||||
}
|
||||
catch (Exception ex) { WriteDiag("[CAD服务] 停止异常: " + ex.Message); }
|
||||
}
|
||||
|
||||
/// <summary>简单诊断日志(logs\cad服务.log)。</summary>
|
||||
private static void WriteDiag(string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
|
||||
Directory.CreateDirectory(dir);
|
||||
File.AppendAllText(Path.Combine(dir, "cad服务.log"),
|
||||
DateTime.Now.ToString("HH:mm:ss.fff") + " " + msg + Environment.NewLine);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void ShowLoadHint(string text)
|
||||
{
|
||||
if (_hint != null) return;
|
||||
_hint = new Label();
|
||||
_hint.Dock = DockStyle.Fill;
|
||||
_hint.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
_hint.Text = text;
|
||||
var reload = new Button();
|
||||
reload.Text = "重新加载";
|
||||
reload.Dock = DockStyle.Bottom;
|
||||
reload.Click += (s, e) =>
|
||||
{
|
||||
Controls.Remove(_hint);
|
||||
_hint.Dispose();
|
||||
_hint = null;
|
||||
启动网页服务();
|
||||
if (_webView != null && _webView.CoreWebView2 != null)
|
||||
_webView.CoreWebView2.Navigate("http://localhost:" + 服务端口 + "/");
|
||||
};
|
||||
Controls.Add(reload);
|
||||
Controls.Add(_hint);
|
||||
_hint.BringToFront();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
namespace cadbt.DockWindows
|
||||
{
|
||||
partial class QuoteWindow
|
||||
{
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null)) components.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Designer generated code
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// QuoteWindow
|
||||
//
|
||||
this.Name = "QuoteWindow";
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// ============================================================
|
||||
// 停靠窗口:报价插件工具栏(左侧)
|
||||
// 把 cadcj 的 Form3 报价界面作为子控件嵌入(TopLevel=false)。
|
||||
// ============================================================
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace cadbt.DockWindows
|
||||
{
|
||||
/// <summary>停靠窗口:报价插件工具栏(DockState.DockLeft)。</summary>
|
||||
public partial class QuoteWindow : WeifenLuo.WinFormsUI.Docking.DockContent
|
||||
{
|
||||
public QuoteWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Text = "报价工具";
|
||||
TabText = "报价工具";
|
||||
|
||||
var form = new MyApp.Form3();
|
||||
form.TopLevel = false;
|
||||
form.FormBorderStyle = FormBorderStyle.None;
|
||||
form.Dock = DockStyle.Fill;
|
||||
Controls.Add(form);
|
||||
form.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+69
@@ -0,0 +1,69 @@
|
||||
namespace cadbt
|
||||
{
|
||||
partial class MainForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Designer variable used to keep track of non-visual components.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Disposes resources used by the form.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) {
|
||||
if (components != null) {
|
||||
components.Dispose();
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is required for Windows Forms designer support.
|
||||
/// Do not change the method contents inside the source code editor. The Forms designer might
|
||||
/// not be able to load this method if it was changed manually.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.dockSurface1 = new Smart.CustomComponents.DockSurface();
|
||||
Smart.CustomComponents.DockWindows.DockWindowItem dockWindowItem1 = new Smart.CustomComponents.DockWindows.DockWindowItem();
|
||||
Smart.CustomComponents.DockWindows.DockWindowItem dockWindowItem2 = new Smart.CustomComponents.DockWindows.DockWindowItem();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// dockSurface1
|
||||
//
|
||||
dockWindowItem1.ClassName = "QuoteWindow";
|
||||
dockWindowItem1.Title = "报价工具";
|
||||
dockWindowItem1.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockLeft;
|
||||
dockWindowItem1.DefaultWidth = 440;
|
||||
dockWindowItem1.ShowCloseButton = false;
|
||||
this.dockSurface1.DockWindows.Add(dockWindowItem1);
|
||||
dockWindowItem2.ClassName = "CadViewerWindow";
|
||||
dockWindowItem2.Title = "CAD 查看器";
|
||||
dockWindowItem2.DockState = WeifenLuo.WinFormsUI.Docking.DockState.Document;
|
||||
dockWindowItem2.ShowCloseButton = false;
|
||||
this.dockSurface1.DockWindows.Add(dockWindowItem2);
|
||||
this.dockSurface1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.dockSurface1.Location = new System.Drawing.Point(0, 0);
|
||||
this.dockSurface1.Name = "dockSurface1";
|
||||
this.dockSurface1.Size = new System.Drawing.Size(1184, 661);
|
||||
this.dockSurface1.TabIndex = 0;
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(1184, 661);
|
||||
this.Controls.Add(this.dockSurface1);
|
||||
this.Name = "MainForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "cadbt - 报价工具 + 网页 CAD";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
}
|
||||
|
||||
private Smart.CustomComponents.DockSurface dockSurface1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 本文件由『生成停靠窗口代码』自动生成格式参考手写,集合变更后请同步刷新。
|
||||
using System;
|
||||
using WeifenLuo.WinFormsUI.Docking;
|
||||
|
||||
namespace cadbt
|
||||
{
|
||||
public partial class MainForm
|
||||
{
|
||||
void CreateDockWindows_dockSurface1()
|
||||
{
|
||||
var dockSurface1 = this.dockSurface1 as Smart.CustomComponents.DockSurface;
|
||||
if (dockSurface1 == null) return;
|
||||
dockSurface1.EnsureDefaultTheme(); // 必须:Show 前保证有效主题
|
||||
|
||||
var quote = new cadbt.DockWindows.QuoteWindow();
|
||||
quote.CloseButtonVisible = false;
|
||||
quote.TabText = "报价工具";
|
||||
quote.Text = "报价工具";
|
||||
quote.Show(dockSurface1, DockState.DockLeft);
|
||||
|
||||
var viewer = new cadbt.DockWindows.CadViewerWindow();
|
||||
viewer.CloseButtonVisible = false;
|
||||
viewer.TabText = "CAD 查看器";
|
||||
viewer.Text = "CAD 查看器";
|
||||
viewer.Show(dockSurface1, DockState.Document);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 由SharpDevelop创建。
|
||||
* 用户: 099978
|
||||
* 日期: 2026-09-04
|
||||
* 时间: 12:24
|
||||
*
|
||||
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace cadbt
|
||||
{
|
||||
/// <summary>
|
||||
/// Description of MainForm.
|
||||
/// </summary>
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
public MainForm()
|
||||
{
|
||||
//
|
||||
// The InitializeComponent() call is required for Windows Forms designer support.
|
||||
//
|
||||
InitializeComponent();
|
||||
CreateDockWindows_dockSurface1();
|
||||
|
||||
// ★ 全局 F1 过滤器:焦点在任何 WinForms 控件上都触发元件合并
|
||||
// (焦点在网页时由网页端转发,两路都汇聚到 CadBridge.F1Requested)
|
||||
Application.AddMessageFilter(new F1MessageFilter());
|
||||
FormClosed += (s, e) => Application.RemoveMessageFilter(f1Filter);
|
||||
}
|
||||
|
||||
private readonly F1MessageFilter f1Filter = new F1MessageFilter();
|
||||
|
||||
/// <summary>把 F1 按键(0x70)转成 CadBridge.F1Requested,不拦截按键本身。</summary>
|
||||
private class F1MessageFilter : IMessageFilter
|
||||
{
|
||||
public bool PreFilterMessage(ref Message m)
|
||||
{
|
||||
const int WM_KEYDOWN = 0x0100;
|
||||
if (m.Msg == WM_KEYDOWN && (int)m.WParam == 0x70)
|
||||
{
|
||||
try { CadBridge.RaiseF1(); } catch { }
|
||||
}
|
||||
else if (m.Msg == WM_KEYDOWN && (int)m.WParam == 0x71)
|
||||
{
|
||||
try { CadBridge.RaiseF2(); } catch { }
|
||||
}
|
||||
else if (m.Msg == WM_KEYDOWN && (int)m.WParam == 0x72)
|
||||
{
|
||||
try { CadBridge.RaiseF3(); } catch { }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
// ============================================================
|
||||
// 扒图数据类型(与原 cadcj 插件 / 网页端 cadbridge.ts 保持一致)
|
||||
// ============================================================
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MyApp
|
||||
{
|
||||
/// <summary>
|
||||
/// 扒数据模式:图号/箱号/元件
|
||||
/// </summary>
|
||||
public enum PaMode
|
||||
{
|
||||
Tuhao, // 扒图号
|
||||
Xianghao, // 扒箱号
|
||||
Yuanjian // 扒元件
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扒图结果:一条文本 + 它对应的彩色框的 Handle(句柄字符串)
|
||||
/// 用于关联表格数据行与网页 CAD 框实体,实现点击行定位、删除行删框。
|
||||
/// ★ TextHandle:源文本实体自身的 Handle,用于判断"是否同一个文本"。
|
||||
/// </summary>
|
||||
public class PaTextResult
|
||||
{
|
||||
public string Text;
|
||||
public string FrameHandle;
|
||||
public string TextHandle = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace MyApp
|
||||
{
|
||||
/// <summary>
|
||||
/// 新建项目 窗口(简化版,不依赖企业信息)。
|
||||
/// UI 布局见 新建项目.ui.cs,本文件只含交互逻辑。
|
||||
/// </summary>
|
||||
public partial class 新建项目 : Form
|
||||
{
|
||||
// 价格格式化选项(与主项目 价格格式化.cs 保持一致)
|
||||
private static readonly string[] 小数位选项 =
|
||||
{
|
||||
"保留整数", "保留1位小数", "保留2位小数", "保留3位小数",
|
||||
"保留4位小数", "保留5位小数", "保留6位小数"
|
||||
};
|
||||
private static readonly string[] 取整方式选项 =
|
||||
{
|
||||
"不取整", "四舍五入", "四舍五入到角", "四舍五入到元",
|
||||
"四舍五入到十元", "向上取整到元", "最近5元", "最近10元"
|
||||
};
|
||||
|
||||
private readonly string _dbPath;
|
||||
private readonly string _tableName;
|
||||
|
||||
public 新建项目() : this(null, null) { }
|
||||
|
||||
/// <summary>带数据库路径的构造函数(用于自动生成项目编号)。</summary>
|
||||
public 新建项目(string dbPath, string tableName)
|
||||
{
|
||||
InitializeComponent();
|
||||
_dbPath = dbPath;
|
||||
_tableName = tableName;
|
||||
InitPriceFormatCombos();
|
||||
this.Load += 新建项目_Load;
|
||||
}
|
||||
|
||||
private void InitPriceFormatCombos()
|
||||
{
|
||||
cmb单台价格.Items.Clear();
|
||||
cmb单台价格.Items.AddRange(小数位选项);
|
||||
cmb项目总价.Items.Clear();
|
||||
cmb项目总价.Items.AddRange(小数位选项);
|
||||
comboBox1.Items.Clear();
|
||||
comboBox1.Items.AddRange(取整方式选项);
|
||||
comboBox2.Items.Clear();
|
||||
comboBox2.Items.AddRange(取整方式选项);
|
||||
}
|
||||
|
||||
private void 新建项目_Load(object sender, EventArgs e)
|
||||
{
|
||||
FillAutoFields();
|
||||
}
|
||||
|
||||
/// <summary>自动填充项目日期(当天)和项目编号(yyyyMMdd+3位序号)。</summary>
|
||||
private void FillAutoFields()
|
||||
{
|
||||
DateTime today = DateTime.Today;
|
||||
txt项目日期.Text = today.ToString("yyyy-MM-dd");
|
||||
|
||||
string datePart = today.ToString("yyyyMMdd");
|
||||
int seq = GetNextSeqForDate(datePart);
|
||||
txt项目编号.Text = datePart + seq.ToString("000");
|
||||
txt项目编号.ReadOnly = true;
|
||||
txt项目编号.TabStop = false;
|
||||
txt项目编号.BackColor = System.Drawing.SystemColors.Control;
|
||||
}
|
||||
|
||||
/// <summary>查数据库当天最大序号+1,失败返回1。</summary>
|
||||
private int GetNextSeqForDate(string datePart)
|
||||
{
|
||||
if (string.IsNullOrEmpty(datePart) ||
|
||||
string.IsNullOrEmpty(_dbPath) ||
|
||||
string.IsNullOrEmpty(_tableName) ||
|
||||
!System.IO.File.Exists(_dbPath))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
try
|
||||
{
|
||||
string connStr = "Data Source=" + _dbPath + ";Version=3;";
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection(connStr))
|
||||
{
|
||||
conn.Open();
|
||||
string sql =
|
||||
"SELECT MAX(CAST(SUBSTR(项目编号, 9) AS INTEGER)) " +
|
||||
"FROM " + _tableName + " " +
|
||||
"WHERE SUBSTR(项目编号, 1, 8) = '" + datePart + "';";
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn))
|
||||
{
|
||||
object result = cmd.ExecuteScalar();
|
||||
if (result == null || result == DBNull.Value) return 1;
|
||||
int maxSeq;
|
||||
if (int.TryParse(result.ToString(), out maxSeq)) return maxSeq + 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { return 1; }
|
||||
}
|
||||
|
||||
/// <summary>收集对话框字段,以列名为键返回(供 Form3 写入数据库)。</summary>
|
||||
public Dictionary<string, string> GetInputValues()
|
||||
{
|
||||
var values = new Dictionary<string, string>();
|
||||
AddIfNotEmpty(values, "项目编号", txt项目编号.Text);
|
||||
AddIfNotEmpty(values, "项目日期", txt项目日期.Text);
|
||||
AddIfNotEmpty(values, "项目名称", txt项目名称.Text);
|
||||
AddIfNotEmpty(values, "客户名称", txt客户名称.Text);
|
||||
AddIfNotEmpty(values, "客户地址", txt客户地址.Text);
|
||||
AddIfNotEmpty(values, "联系人", txt联系人.Text);
|
||||
AddIfNotEmpty(values, "联系电话", txt联系电话.Text);
|
||||
AddIfNotEmpty(values, "备注", txt备注.Text);
|
||||
AddIfNotEmpty(values, "成套厂名称", txt成套厂名称.Text);
|
||||
AddIfNotEmpty(values, "成套厂地址", txt成套厂地址.Text);
|
||||
AddIfNotEmpty(values, "成套厂联系人", txt成套厂联系人.Text);
|
||||
AddIfNotEmpty(values, "成套厂联系电话", txt成套厂联系电话.Text);
|
||||
values["单台小数位"] = (cmb单台价格.SelectedItem ?? cmb单台价格.Text ?? "").ToString();
|
||||
values["项目小数位"] = (cmb项目总价.SelectedItem ?? cmb项目总价.Text ?? "").ToString();
|
||||
values["单台取整方式"] = (comboBox1.SelectedItem ?? comboBox1.Text ?? "").ToString();
|
||||
values["项目取整方式"] = (comboBox2.SelectedItem ?? comboBox2.Text ?? "").ToString();
|
||||
return values;
|
||||
}
|
||||
|
||||
private static void AddIfNotEmpty(Dictionary<string, string> dict, string key, string value)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value)) dict[key] = value.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 由SharpDevelop创建。
|
||||
* 用户: 099978
|
||||
* 日期: 2026-09-04
|
||||
* 时间: 12:24
|
||||
*
|
||||
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
|
||||
*/
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace cadbt
|
||||
{
|
||||
/// <summary>
|
||||
/// Class with program entry point.
|
||||
/// </summary>
|
||||
internal sealed class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Program entry point.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
// 单实例保护:重复启动时激活已有实例并退出,避免残留多个进程
|
||||
bool createdNew;
|
||||
using (var mutex = new System.Threading.Mutex(true, "cadbt_SingleInstance", out createdNew))
|
||||
{
|
||||
if (!createdNew)
|
||||
{
|
||||
MessageBox.Show("cadbt 已在运行中。", "cadbt",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm());
|
||||
GC.KeepAlive(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#region Using directives
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#endregion
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("cadbt")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("cadbt")]
|
||||
[assembly: AssemblyCopyright("Copyright 2026")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// This sets the default COM visibility of types in the assembly to invisible.
|
||||
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all the values or you can use the default the Revision and
|
||||
// Build Numbers by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{0A91D57B-B279-4672-9440-78088D022EB1}</ProjectGuid>
|
||||
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>cadbt</RootNamespace>
|
||||
<AssemblyName>cadbt</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>Full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<DebugType>None</DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
|
||||
<Prefer32Bit>True</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Data.DataSetExtensions">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Extensions">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="Smart.CustomComponents, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ba044a4aa43e0dc4">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\Smart.CustomComponents.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="WeifenLuo.WinFormsUI.Docking">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\WeifenLuo.WinFormsUI.Docking.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="WeifenLuo.WinFormsUI.Docking.ThemeVS2015">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\WeifenLuo.WinFormsUI.Docking.ThemeVS2015.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.SQLite">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Data.SQLite.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="log4net">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\log4net.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Resources.Extensions">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Resources.Extensions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Memory.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics.Vectors">
|
||||
<HintPath>C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Numerics.Vectors.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="AdvancedDataGridView">
|
||||
<HintPath>libs\AdvancedDataGridView.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.Core">
|
||||
<HintPath>libs\webview2\Microsoft.Web.WebView2.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.WinForms">
|
||||
<HintPath>libs\webview2\Microsoft.Web.WebView2.WinForms.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MainForm.cs" />
|
||||
<Compile Include="MainForm.Designer.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.DockWindows.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="CadBridge.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Plugin\Form3.cs" />
|
||||
<Compile Include="Plugin\Form3.ui.cs">
|
||||
<DependentUpon>Form3.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Plugin\PaTypes.cs" />
|
||||
<Compile Include="Plugin\新建项目.cs" />
|
||||
<Compile Include="Plugin\新建项目.ui.cs">
|
||||
<DependentUpon>新建项目.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="DockWindows\CadViewerWindow.cs" />
|
||||
<Compile Include="DockWindows\CadViewerWindow.Designer.cs">
|
||||
<DependentUpon>CadViewerWindow.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="DockWindows\QuoteWindow.cs" />
|
||||
<Compile Include="DockWindows\QuoteWindow.Designer.cs">
|
||||
<DependentUpon>QuoteWindow.cs</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="app.manifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="libs\webview2\WebView2Loader.dll">
|
||||
<Link>WebView2Loader.dll</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
Binary file not shown.
@@ -0,0 +1,350 @@
|
||||
// ============================================================
|
||||
// CAD 2018 插件入口(cadcj.dll)
|
||||
// · 实现 IExtensionApplication,在 CAD 启动时自动加载
|
||||
// · 注册命令 "CADCJ" 打开 Form3 报价窗口
|
||||
// · 使用 PaletteSet 把 Form3 嵌成 CAD 可停靠工具栏窗口
|
||||
// 启动时自动贴靠到 CAD 左侧,用户可拖到右/上/下边或浮动
|
||||
// · 编译:/target:library /platform:x64
|
||||
// · 加载:在 CAD 里 NETLOAD 选择 cadcj.dll,然后输入 CADCJ 命令
|
||||
// ============================================================
|
||||
using System;
|
||||
using Autodesk.AutoCAD.Runtime;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.Windows;
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Autodesk.AutoCAD.EditorInput;
|
||||
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
|
||||
|
||||
[assembly: CommandClass(typeof(MyApp.CadPlugin))]
|
||||
[assembly: ExtensionApplication(typeof(MyApp.CadPlugin))]
|
||||
|
||||
namespace MyApp
|
||||
{
|
||||
/// <summary>
|
||||
/// CAD 2018 插件入口。CAD 启动时自动初始化,注册 CADCJ 命令打开报价窗口。
|
||||
/// 报价窗口用 PaletteSet 承载,可像 CAD 工具选项板一样停靠/浮动。
|
||||
/// </summary>
|
||||
public class CadPlugin : IExtensionApplication
|
||||
{
|
||||
// PaletteSet 单例:CAD 里常驻,可停靠
|
||||
private static PaletteSet _ps = null;
|
||||
// Form3 单例(作为 PaletteSet 的内容)
|
||||
private static Form3 _form3 = null;
|
||||
// ★ 宿主容器:PaletteSet 只加 Panel,Form3 塞进去(规避主题同步崩溃)
|
||||
private static System.Windows.Forms.Panel _hostPanel = null;
|
||||
|
||||
#region IExtensionApplication 成员
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
// ★ 最简单的文件日志,不依赖任何外部代码,确认 Initialize 是否被调用
|
||||
try
|
||||
{
|
||||
string logPath = System.IO.Path.Combine(
|
||||
System.IO.Path.GetDirectoryName(
|
||||
new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath),
|
||||
"cadcj_load.log");
|
||||
System.IO.File.WriteAllText(logPath,
|
||||
"[" + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "] Initialize 被调用" + System.Environment.NewLine);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// ★ 解决 CAD 安全提示:把插件目录添加到 TRUSTEDPATHS,并关闭 SECURELOAD
|
||||
// 这样 CAD 不会每次都弹"始终加载/加载一次"的安全提示
|
||||
try
|
||||
{
|
||||
string pluginDir = System.IO.Path.GetDirectoryName(
|
||||
new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
|
||||
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null)
|
||||
{
|
||||
// ★ 关闭 SECURELOAD(0=不检查可执行文件签名)
|
||||
try
|
||||
{
|
||||
object sl = Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("SECURELOAD");
|
||||
if (sl == null || Convert.ToInt32(sl) != 0)
|
||||
{
|
||||
Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("SECURELOAD", 0);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// ★ 把插件目录添加到 TRUSTEDPATHS
|
||||
object cur = Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("TRUSTEDPATHS");
|
||||
string curPaths = cur == null ? "" : cur.ToString();
|
||||
bool found = false;
|
||||
string[] parts = curPaths.Split(';');
|
||||
foreach (string p in parts)
|
||||
{
|
||||
if (string.Equals(p.Trim(), pluginDir.Trim(), System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
string newPaths = string.IsNullOrEmpty(curPaths) ? pluginDir : curPaths + ";" + pluginDir;
|
||||
Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("TRUSTEDPATHS", newPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc0 = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc0 != null) doc0.Editor.WriteMessage("\n[CADCJ] 设置可信路径失败: " + ex.Message);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null)
|
||||
{
|
||||
doc.Editor.WriteMessage("\n[CADCJ] 成套报价插件已加载,报价窗口将随文档自动打开。");
|
||||
}
|
||||
// ★ 监听文档创建/激活事件:每次新建/打开文件时自动显示报价窗口
|
||||
AcAp.DocumentManager.DocumentCreated += OnDocumentCreated;
|
||||
|
||||
// ★ 关键修复:acaddoc.lsp 的 NETLOAD 发生在文档已打开之后,
|
||||
// DocumentCreated 事件早已错过;且 Idle 事件在挂接时若 CAD 已空闲
|
||||
// 可能永不触发 → 改为直接显示(Initialize 在命令上下文里可安全创建 UI)
|
||||
if (AcAp.DocumentManager.MdiActiveDocument != null && _ps == null && _form3 == null)
|
||||
{
|
||||
PluginLog("[Initialize] 直接调用 OpenForm3");
|
||||
OpenForm3();
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex2)
|
||||
{
|
||||
PluginLog("[Initialize] ⚠ 直接打开失败: " + ex2.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>文档创建时自动打开/显示报价窗口(停靠左侧)。</summary>
|
||||
private void OnDocumentCreated(object sender, DocumentCollectionEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 延迟到 Idle 时执行,避免在文档创建过程中操作 UI
|
||||
AcAp.Idle -= OnCadIdleShowPalette;
|
||||
AcAp.Idle += OnCadIdleShowPalette;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>Idle 时显示报价窗口(只执行一次后取消订阅)。</summary>
|
||||
private void OnCadIdleShowPalette(object sender, EventArgs e)
|
||||
{
|
||||
AcAp.Idle -= OnCadIdleShowPalette;
|
||||
try
|
||||
{
|
||||
OpenForm3();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_ps != null)
|
||||
{
|
||||
_ps.Visible = false;
|
||||
_ps = null;
|
||||
}
|
||||
if (_form3 != null && !_form3.IsDisposed)
|
||||
{
|
||||
_form3.Dispose();
|
||||
_form3 = null;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
/// <summary>CADCJ 命令:打开/激活报价窗口(PaletteSet,可停靠)。</summary>
|
||||
[CommandMethod("CADCJ", CommandFlags.Modal)]
|
||||
public void OpenForm3()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 已有 PaletteSet 则激活显示并重新停靠到左侧
|
||||
if (_ps != null)
|
||||
{
|
||||
_ps.Visible = true;
|
||||
try
|
||||
{
|
||||
_ps.DockEnabled = DockSides.Left;
|
||||
_ps.Dock = DockSides.Left;
|
||||
}
|
||||
catch { }
|
||||
return;
|
||||
}
|
||||
|
||||
PluginLog("[CADCJ] OpenForm3 开始");
|
||||
|
||||
// 新建 Form3,转成子控件模式嵌入容器
|
||||
_form3 = new Form3();
|
||||
_form3.TopLevel = false;
|
||||
_form3.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
_form3.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
_form3.Visible = true;
|
||||
PluginLog("[CADCJ] Form3 就绪");
|
||||
|
||||
// ★ 根治 ResyncToTheme 崩溃:PaletteSet 只加 UserControl 容器,
|
||||
// 不直接加 Form(CAD 主题同步强改 Form.BackColor 会抛
|
||||
// "控件不支持透明的背景色")。Form3 塞进 Panel 再加。
|
||||
_hostPanel = new System.Windows.Forms.Panel();
|
||||
_hostPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
_hostPanel.BackColor = System.Drawing.SystemColors.Control;
|
||||
_hostPanel.Controls.Add(_form3);
|
||||
|
||||
// 新建 PaletteSet
|
||||
_ps = new PaletteSet("扒图", new System.Guid("A3F2B5C1-7D8E-4A6B-9C0D-1E2F3A4B5C6D"));
|
||||
PluginLog("[CADCJ] PaletteSet 创建成功");
|
||||
|
||||
_ps.Style = PaletteSetStyles.Snappable
|
||||
| PaletteSetStyles.UsePaletteNameAsTitleForSingle;
|
||||
_ps.Add("扒图", _hostPanel);
|
||||
PluginLog("[CADCJ] Add 成功");
|
||||
|
||||
// ★ 启动时自动贴靠到 CAD 左侧,并设置足够尺寸
|
||||
_ps.Size = new System.Drawing.Size(540, 800);
|
||||
_ps.MinimumSize = new System.Drawing.Size(300, 400);
|
||||
|
||||
// ★ 先显示,再设置停靠(CAD PaletteSet 的特殊行为)
|
||||
_ps.Visible = true;
|
||||
PluginLog("[CADCJ] Visible=true 完成");
|
||||
|
||||
// ★ 启用左侧停靠 + 强制停靠到左侧
|
||||
_ps.DockEnabled = DockSides.Left;
|
||||
_ps.Dock = DockSides.Left;
|
||||
PluginLog("[CADCJ] OpenForm3 全部完成 ✓");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
PluginLog("[CADCJ] ⚠ 打开窗口失败: " + ex);
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null)
|
||||
{
|
||||
doc.Editor.WriteMessage("\n[CADCJ] 打开窗口失败: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>插件级文件日志(写 cadcj 目录 diag.log,独立于 Form3)。</summary>
|
||||
private static void PluginLog(string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
string dir = System.IO.Path.GetDirectoryName(
|
||||
new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
|
||||
System.IO.File.AppendAllText(System.IO.Path.Combine(dir, "diag.log"),
|
||||
DateTime.Now.ToString("HH:mm:ss.fff") + " " + msg + System.Environment.NewLine);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 扒图号/扒箱号/扒元件 命令
|
||||
// · 由 Form3 的按钮通过 SendStringToExecute 触发
|
||||
// · 只允许框选 DBText/MText(过滤掉线条)
|
||||
// · 选中文本用半透明颜色高亮
|
||||
// · 选中的文本传回 Form3 插入到对应表格
|
||||
// ============================================================
|
||||
|
||||
/// <summary>扒图号命令:CADPATU</summary>
|
||||
[CommandMethod("CADPATU", CommandFlags.Modal)]
|
||||
public void PaTuhaoCommand()
|
||||
{
|
||||
ExecutePaCommand(MyApp.PaMode.Tuhao);
|
||||
}
|
||||
|
||||
/// <summary>扒箱号命令:CADPAXH</summary>
|
||||
[CommandMethod("CADPAXH", CommandFlags.Modal)]
|
||||
public void PaXianghaoCommand()
|
||||
{
|
||||
ExecutePaCommand(MyApp.PaMode.Xianghao);
|
||||
}
|
||||
|
||||
/// <summary>扒元件命令:CADPAYJ</summary>
|
||||
[CommandMethod("CADPAYJ", CommandFlags.Modal)]
|
||||
public void PaYuanjianCommand()
|
||||
{
|
||||
ExecutePaCommand(MyApp.PaMode.Yuanjian);
|
||||
}
|
||||
|
||||
/// <summary>清除所有高亮颜色命令:CADPACL</summary>
|
||||
[CommandMethod("CADPACL", CommandFlags.Modal)]
|
||||
public void PaClearCommand()
|
||||
{
|
||||
try
|
||||
{
|
||||
PaTextSelector.ClearAllHighlight();
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null) doc.Editor.WriteMessage("\n[扒数据] 已清除所有染色文本。");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null) doc.Editor.WriteMessage("\n[扒数据] 清除失败: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>执行扒数据命令的核心逻辑(连续框选模式)。</summary>
|
||||
private void ExecutePaCommand(PaMode mode)
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc == null) return;
|
||||
var ed = doc.Editor;
|
||||
try
|
||||
{
|
||||
// 订阅事件:每次框选完一批文本,立即通过 Invoke 调用 Form3 的插入方法
|
||||
PaTextSelector.BatchTextsPicked -= OnPaBatchTextsPicked;
|
||||
PaTextSelector.BatchTextsPicked += OnPaBatchTextsPicked;
|
||||
|
||||
// 进入连续框选循环(直到用户按 ESC)
|
||||
PaTextSelector.PickTextsContinuous(mode);
|
||||
|
||||
// 用户按 ESC 退出后,触发一次最终的"完成"提示(可选)
|
||||
ed.WriteMessage("\n[扒数据] 已退出连续模式。");
|
||||
|
||||
// 取消订阅
|
||||
PaTextSelector.BatchTextsPicked -= OnPaBatchTextsPicked;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
ed.WriteMessage("\n[扒数据] 异常: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>PaTextSelector 事件回调:把一批文本+框Handle传给 Form3 插入到表格。</summary>
|
||||
private void OnPaBatchTextsPicked(PaMode mode, System.Collections.Generic.List<PaTextResult> results)
|
||||
{
|
||||
if (_form3 == null || _form3.IsDisposed) return;
|
||||
try
|
||||
{
|
||||
_form3.Invoke(new System.Action(() =>
|
||||
{
|
||||
try { _form3.OnPaTextsPicked(mode, results); }
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null) doc.Editor.WriteMessage("\n[扒数据] 插入数据异常: " + ex.Message);
|
||||
}
|
||||
}));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
// ============================================================
|
||||
// Form3 依赖清单
|
||||
// 此文件记录窗体所使用的控件及其运行时依赖。
|
||||
// ============================================================
|
||||
|
||||
// NuGet 包依赖:
|
||||
// ----------------
|
||||
// System.Data.SQLite.Core (版本 1.0.119.0)
|
||||
// 安装命令: dotnet add package System.Data.SQLite.Core --version 1.0.119.0
|
||||
// 或在 Visual Studio 中: 右键项目 → 管理 NuGet 包 → 搜索 System.Data.SQLite.Core
|
||||
|
||||
// DLL 文件依赖:
|
||||
// ----------------
|
||||
// 来自 未知:
|
||||
// - AdvancedDataGridView.dll (托管 DLL)
|
||||
// 来自 System.Data.SQLite.Core:
|
||||
// - System.Data.SQLite.dll (托管 DLL)
|
||||
|
||||
// 编译说明:
|
||||
// ----------------
|
||||
// 1. 确保 NuGet 包已安装到项目
|
||||
// 2. 编译时使用正确的 CPU 架构(x64 或 x86)
|
||||
// 3. 运行时确保所有 DLL 文件与 exe 在同一目录
|
||||
// 4. ★ 推荐用同目录下的 build.bat 一键编译(已自动配置 response file 和 manifest)
|
||||
// manifest 的 DPI 策略与设计器一致(dpiAware=unaware),保证所见即所得
|
||||
|
||||
// 编译命令示例(x64):
|
||||
// csc /target:winexe /platform:x64 /out:Form3.exe
|
||||
// /win32manifest:app.manifest
|
||||
// /r:System.Windows.Forms.dll /r:System.Drawing.dll
|
||||
// /r:"AdvancedDataGridView.dll"
|
||||
// /r:"System.Data.SQLite.dll"
|
||||
// Form3.cs Form3.ui.cs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,830 @@
|
||||
// ============================================================
|
||||
// 扒图号/扒箱号/扒元件 - CAD文本框选交互
|
||||
// · 直接框选一次完成(无需点完成按钮)
|
||||
// · 只框选 DBText/MText(线条自动过滤)
|
||||
// · 不改文字颜色,而是在每个文字周围画彩色矩形框
|
||||
// (图号=红色框, 箱号=绿色框, 元件=蓝色框)
|
||||
// · 清除时删除所有彩色矩形框
|
||||
// ============================================================
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Autodesk.AutoCAD.EditorInput;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.Geometry;
|
||||
using Autodesk.AutoCAD.Colors;
|
||||
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
|
||||
|
||||
namespace MyApp
|
||||
{
|
||||
/// <summary>
|
||||
/// 扒数据模式:图号/箱号/元件
|
||||
/// </summary>
|
||||
public enum PaMode
|
||||
{
|
||||
Tuhao, // 扒图号
|
||||
Xianghao, // 扒箱号
|
||||
Yuanjian // 扒元件
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扒图结果:一条文本 + 它对应的彩色框的 Handle(句柄字符串)
|
||||
/// 用于关联表格数据行与 CAD 框实体,实现点击行定位、删除行删框。
|
||||
/// ★ TextHandle:源文本实体自身的 Handle,用于判断"是否同一个文本"
|
||||
/// (不同位置的相同内容文本 Handle 不同,不算重复)。
|
||||
/// </summary>
|
||||
public class PaTextResult
|
||||
{
|
||||
public string Text;
|
||||
public string FrameHandle;
|
||||
public string TextHandle = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CAD 文本框选交互:框选一次即完成,在文字周围画彩色矩形框(不改文字颜色)。
|
||||
/// </summary>
|
||||
public static class PaTextSelector
|
||||
{
|
||||
// 三种模式对应不同的矩形框颜色(ACI:3=绿,4=青,1=红)
|
||||
private const short TuhaoColorIndex = 3; // 图号=绿
|
||||
private const short XianghaoColorIndex = 4; // 箱号=青
|
||||
private const short YuanjianColorIndex = 1; // 元件=红
|
||||
|
||||
// 矩形框图层名(便于批量清除)
|
||||
public const string PaLayerName = "__CADCJ_PA_FRAME";
|
||||
|
||||
/// <summary>
|
||||
/// 连续扒数据事件:每次框选完成(松开鼠标)就触发一次,把这一批文本+框Handle送给调用方。
|
||||
/// 调用方在 Form3 里把数据插入到表格,然后控制流回到 PickTexts 继续下一次框选。
|
||||
/// </summary>
|
||||
public static event System.Action<PaMode, List<PaTextResult>> BatchTextsPicked;
|
||||
|
||||
/// <summary>
|
||||
/// 连续框选模式:用户点一次按钮进入,可以连续框选多次,每次框选立即送出一批文本。
|
||||
/// 按回车/空格完成当前批次,ESC 退出连续模式。
|
||||
/// 方向决定行为:
|
||||
/// · 左上→右下(正向框选):每个文本一条记录
|
||||
/// · 右下→左上(反向框选,CAD里称为"交叉框选"):多条文本合并为一条
|
||||
/// </summary>
|
||||
/// <param name="mode">扒数据模式(决定矩形框颜色)</param>
|
||||
public static void PickTextsContinuous(PaMode mode)
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc == null) return;
|
||||
var ed = doc.Editor;
|
||||
|
||||
// ★ 强制开启透明度显示(TRANSPARENCYDISPLAY=1)
|
||||
// 否则 Hatch.Transparency 在某些电脑上不生效,蒙版显示为不透明
|
||||
try { AcAp.SetSystemVariable("TRANSPARENCYDISPLAY", 1); } catch { }
|
||||
|
||||
// ★ 当前模式(允许右键切换,所以用变量保存当前模式)
|
||||
PaMode currentMode = mode;
|
||||
|
||||
try
|
||||
{
|
||||
var typeValues = new TypedValue[]
|
||||
{
|
||||
new TypedValue(-4, "<OR"),
|
||||
new TypedValue((int)DxfCode.Start, "TEXT"),
|
||||
new TypedValue((int)DxfCode.Start, "MTEXT"),
|
||||
new TypedValue(-4, "OR>")
|
||||
};
|
||||
var filter = new SelectionFilter(typeValues);
|
||||
|
||||
// ★ 连续循环:每次框选完立即触发事件,直到用户按 ESC
|
||||
while (true)
|
||||
{
|
||||
// 根据当前模式获取颜色和名称
|
||||
short colorIndex;
|
||||
string modeName;
|
||||
switch (currentMode)
|
||||
{
|
||||
case PaMode.Tuhao: colorIndex = TuhaoColorIndex; modeName = "图号"; break;
|
||||
case PaMode.Xianghao: colorIndex = XianghaoColorIndex; modeName = "箱号"; break;
|
||||
default: colorIndex = YuanjianColorIndex; modeName = "元件"; break;
|
||||
}
|
||||
|
||||
// 第一角点
|
||||
// ★ AllowNone=true:右键会返回 None,用来切换模式;ESC 返回 Cancel 退出
|
||||
var po1 = new PromptPointOptions("\n框选【" + modeName + "】起点(右键切换模式,ESC结束): ");
|
||||
po1.AllowNone = true;
|
||||
var pres1 = ed.GetPoint(po1);
|
||||
if (pres1.Status == PromptStatus.Cancel) break; // ESC 退出
|
||||
if (pres1.Status == PromptStatus.None)
|
||||
{
|
||||
// ★ 右键:切换到下一个模式(图号→箱号→元件→退出)
|
||||
PaMode next = GetNextMode(currentMode);
|
||||
if (next == currentMode)
|
||||
{
|
||||
// 元件模式右键 → 退出
|
||||
ed.WriteMessage("\n[扒数据] 已退出连续模式。");
|
||||
break;
|
||||
}
|
||||
currentMode = next;
|
||||
ed.WriteMessage("\n[扒数据] 切换到【" + GetModeName(currentMode) + "】模式。");
|
||||
continue;
|
||||
}
|
||||
if (pres1.Status != PromptStatus.OK) break;
|
||||
Point3d pt1 = pres1.Value;
|
||||
|
||||
// 对角点
|
||||
// ★ AllowNone=true:右键也允许切换模式(回到第一角点重新开始)
|
||||
var co = new PromptCornerOptions("\n对角点: ", pt1);
|
||||
co.AllowNone = true;
|
||||
var cres = ed.GetCorner(co);
|
||||
if (cres.Status == PromptStatus.Cancel) break; // ESC 退出
|
||||
if (cres.Status == PromptStatus.None)
|
||||
{
|
||||
// ★ 右键:切换到下一个模式,回到第一角点
|
||||
PaMode next = GetNextMode(currentMode);
|
||||
if (next == currentMode)
|
||||
{
|
||||
ed.WriteMessage("\n[扒数据] 已退出连续模式。");
|
||||
break;
|
||||
}
|
||||
currentMode = next;
|
||||
ed.WriteMessage("\n[扒数据] 切换到【" + GetModeName(currentMode) + "】模式。");
|
||||
continue;
|
||||
}
|
||||
if (cres.Status != PromptStatus.OK) break;
|
||||
Point3d pt2 = cres.Value;
|
||||
|
||||
// ★ 关键:判断方向(任意一项反向即为"交叉框选"=合并模式)
|
||||
// pt1.X > pt2.X = 从右往左框选(pt1 在 pt2 右边)
|
||||
// pt1.Y < pt2.Y = 从下往上框选(pt1 在 pt2 下方)
|
||||
// 任一条件满足都视为反向框选 → 多文本合并为一条
|
||||
bool isCrossing = (pt1.X > pt2.X) || (pt1.Y < pt2.Y);
|
||||
|
||||
double minX = Math.Min(pt1.X, pt2.X);
|
||||
double maxX = Math.Max(pt1.X, pt2.X);
|
||||
double minY = Math.Min(pt1.Y, pt2.Y);
|
||||
double maxY = Math.Max(pt1.Y, pt2.Y);
|
||||
Point3d minPt = new Point3d(minX, minY, 0);
|
||||
Point3d maxPt = new Point3d(maxX, maxY, 0);
|
||||
|
||||
// ★ 统一用交叉窗口选择:只要框碰到文字(部分重叠)即选中,
|
||||
// 不再要求把文字完全框住(旧版正向框选用 SelectWindow 必须全包,漏选严重)。
|
||||
// 框选方向只决定业务模式(正向=逐条,反向=合并),不再影响选择方式。
|
||||
PromptSelectionResult res = ed.SelectCrossingWindow(minPt, maxPt, filter);
|
||||
|
||||
if (res.Status != PromptStatus.OK) continue; // 这一次没选中,继续下一次
|
||||
ObjectId[] ids = res.Value.GetObjectIds();
|
||||
if (ids.Length == 0) continue;
|
||||
|
||||
// 收集文本
|
||||
var items = new List<TextItem>();
|
||||
using (var tr = doc.Database.TransactionManager.StartTransaction())
|
||||
{
|
||||
foreach (ObjectId id in ids)
|
||||
{
|
||||
var ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
|
||||
if (ent == null) continue;
|
||||
string text = "";
|
||||
Point3d pos = Point3d.Origin;
|
||||
Extents3d? bounds = null;
|
||||
var dt = ent as DBText;
|
||||
var mt = ent as MText;
|
||||
if (dt != null)
|
||||
{
|
||||
text = dt.TextString;
|
||||
try
|
||||
{
|
||||
// ★ 用 CAD 自身的 GeometricExtents 获取文本框边界
|
||||
// 宽度通常准确,高度包含字体预留空间偏大
|
||||
// 所以高度用字高重新估算,宽度用 GeometricExtents
|
||||
bounds = dt.GeometricExtents;
|
||||
if (bounds.HasValue)
|
||||
{
|
||||
double h = dt.Height;
|
||||
if (h > 0)
|
||||
{
|
||||
// ★ 宽度:直接用 GeometricExtents 的宽度(CAD 自身文本框宽度)
|
||||
double left = bounds.Value.MinPoint.X;
|
||||
double right = bounds.Value.MaxPoint.X;
|
||||
// ★ 高度:用字高紧凑估算(底部=baseline下0.2h,顶部=baseline上1.0h)
|
||||
double bottom = dt.Position.Y - h * 0.2;
|
||||
double top = dt.Position.Y + h * 1.0;
|
||||
bounds = new Extents3d(
|
||||
new Point3d(left, bottom, 0),
|
||||
new Point3d(right, top, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
// ★ 用文字中心点作为排序基准
|
||||
if (bounds.HasValue)
|
||||
pos = new Point3d(
|
||||
(bounds.Value.MinPoint.X + bounds.Value.MaxPoint.X) / 2.0,
|
||||
(bounds.Value.MinPoint.Y + bounds.Value.MaxPoint.Y) / 2.0,
|
||||
0);
|
||||
else
|
||||
pos = dt.Position;
|
||||
}
|
||||
else if (mt != null)
|
||||
{
|
||||
text = mt.Text;
|
||||
try { bounds = mt.GeometricExtents; } catch { }
|
||||
if (bounds.HasValue)
|
||||
pos = new Point3d(
|
||||
(bounds.Value.MinPoint.X + bounds.Value.MaxPoint.X) / 2.0,
|
||||
(bounds.Value.MinPoint.Y + bounds.Value.MaxPoint.Y) / 2.0,
|
||||
0);
|
||||
else
|
||||
pos = mt.Location;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
items.Add(new TextItem
|
||||
{
|
||||
Text = text.Trim(),
|
||||
Position = pos,
|
||||
Bounds = bounds,
|
||||
ObjectId = id,
|
||||
TextHandle = ent.Handle.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
tr.Commit();
|
||||
}
|
||||
|
||||
if (items.Count == 0) continue;
|
||||
|
||||
// ★ 按列分组排序:先从左到右分列,列内从上到下
|
||||
// 用户需求示例:1在上 2在下(左列) 3在上 4在下(右列)
|
||||
// 期望顺序:1、2、3、4(先左列从上到下,再右列从上到下)
|
||||
// 算法:
|
||||
// 1. 按X左边界升序
|
||||
// 2. 扫描分列:X范围有重叠的归为同一列(垂直方向对齐的文字)
|
||||
// 3. 每列内按Y降序(上在前)
|
||||
// 4. 列间按X升序(左在前)合并
|
||||
{
|
||||
System.Func<TextItem, double> getLeft = it =>
|
||||
it.Bounds.HasValue ? it.Bounds.Value.MinPoint.X : it.Position.X;
|
||||
System.Func<TextItem, double> getRight = it =>
|
||||
it.Bounds.HasValue ? it.Bounds.Value.MaxPoint.X : it.Position.X;
|
||||
System.Func<TextItem, double> getCenterY = it =>
|
||||
it.Bounds.HasValue ? (it.Bounds.Value.MinPoint.Y + it.Bounds.Value.MaxPoint.Y) / 2.0 : it.Position.Y;
|
||||
|
||||
// 1. 按X左边界升序
|
||||
items.Sort((a, b) => getLeft(a).CompareTo(getLeft(b)));
|
||||
|
||||
// 2. 扫描分列:X范围有重叠的归为同一列
|
||||
var columns = new List<List<TextItem>>();
|
||||
double curMaxRight = double.NegativeInfinity;
|
||||
List<TextItem> curCol = null;
|
||||
foreach (var it in items)
|
||||
{
|
||||
double left = getLeft(it);
|
||||
double right = getRight(it);
|
||||
if (curCol == null || left > curMaxRight)
|
||||
{
|
||||
// 新列
|
||||
curCol = new List<TextItem>();
|
||||
columns.Add(curCol);
|
||||
curMaxRight = right;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 加入当前列,更新右边界
|
||||
if (right > curMaxRight) curMaxRight = right;
|
||||
}
|
||||
curCol.Add(it);
|
||||
}
|
||||
|
||||
// 3. 每列内按Y降序(上在前)
|
||||
foreach (var col in columns)
|
||||
{
|
||||
col.Sort((a, b) => getCenterY(b).CompareTo(getCenterY(a)));
|
||||
}
|
||||
|
||||
// 4. 合并(列间已按X升序)
|
||||
items.Clear();
|
||||
foreach (var col in columns) items.AddRange(col);
|
||||
}
|
||||
|
||||
var result = new List<PaTextResult>();
|
||||
if (isCrossing && items.Count > 1)
|
||||
{
|
||||
// ★ 反向框选 + 多条文本:合并模式
|
||||
// 画一个大框,所有文本共用这个框Handle
|
||||
// 每个文本作为独立的 PaTextResult 传出(共用同一个框Handle),
|
||||
// 让 Form3 能检查每个文本是否已存在并自动合并已存在的元件
|
||||
string mergedHandle = DrawMergedFrame(items, colorIndex);
|
||||
foreach (var it in items)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(it.Text))
|
||||
{
|
||||
result.Add(new PaTextResult
|
||||
{
|
||||
Text = it.Text.Trim(),
|
||||
FrameHandle = mergedHandle,
|
||||
TextHandle = it.TextHandle
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (currentMode == PaMode.Yuanjian)
|
||||
{
|
||||
// 元件模式(正向框选或单条):每个文字一条,每个文字一个框
|
||||
var handles = DrawTextFrames(items, colorIndex);
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
string h = i < handles.Count ? handles[i] : "";
|
||||
result.Add(new PaTextResult
|
||||
{
|
||||
Text = items[i].Text,
|
||||
FrameHandle = h,
|
||||
TextHandle = items[i].TextHandle
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 图号/箱号(正向框选或单条):去重(同名只算一条),框对应
|
||||
var seen = new HashSet<string>();
|
||||
var seenTextHandles = new HashSet<string>();
|
||||
var handles = DrawTextFrames(items, colorIndex);
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
// ★ 用文本实体Handle去重(同一个文本实体只算一条),
|
||||
// 不同位置的相同内容文本算两条
|
||||
if (seenTextHandles.Add(items[i].TextHandle) && seen.Add(items[i].Text))
|
||||
{
|
||||
string h = i < handles.Count ? handles[i] : "";
|
||||
result.Add(new PaTextResult
|
||||
{
|
||||
Text = items[i].Text,
|
||||
FrameHandle = h,
|
||||
TextHandle = items[i].TextHandle
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 立即把这一批数据(文本+框Handle)送给调用方(插入到表格)
|
||||
if (result.Count > 0)
|
||||
{
|
||||
var handler = BatchTextsPicked;
|
||||
if (handler != null)
|
||||
{
|
||||
try { handler(currentMode, result); } catch { }
|
||||
}
|
||||
|
||||
// ★ 扒完一批后自动切换到下一个模式(图号→箱号→元件)
|
||||
// 元件模式扒完后保持元件模式不变(继续扒下一个箱号的元件)
|
||||
if (currentMode == PaMode.Tuhao)
|
||||
{
|
||||
currentMode = PaMode.Xianghao;
|
||||
ed.WriteMessage("\n[扒数据] 图号已扒完,自动切换到【箱号】模式。");
|
||||
}
|
||||
else if (currentMode == PaMode.Xianghao)
|
||||
{
|
||||
currentMode = PaMode.Yuanjian;
|
||||
ed.WriteMessage("\n[扒数据] 箱号已扒完,自动切换到【元件】模式。");
|
||||
}
|
||||
}
|
||||
// 继续下一次框选
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
try { ed.WriteMessage("\n[扒数据] 异常: " + ex.Message); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取右键切换的下一个扒图模式。
|
||||
/// 顺序:图号 → 箱号 → 元件 → 退出(返回当前模式表示退出)。
|
||||
/// </summary>
|
||||
private static PaMode GetNextMode(PaMode current)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
case PaMode.Tuhao: return PaMode.Xianghao;
|
||||
case PaMode.Xianghao: return PaMode.Yuanjian;
|
||||
default: return PaMode.Yuanjian; // 元件右键 → 返回自身,外层判断退出
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>获取模式的中文名称(用于切换提示)。</summary>
|
||||
private static string GetModeName(PaMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case PaMode.Tuhao: return "图号";
|
||||
case PaMode.Xianghao: return "箱号";
|
||||
default: return "元件";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反向框选合并时:计算所有文字的总边界,画一个大框包住所有文字。
|
||||
/// 返回:大框的 Handle 字符串(失败返回空字符串)。
|
||||
/// </summary>
|
||||
private static string DrawMergedFrame(List<TextItem> items, short colorIndex)
|
||||
{
|
||||
string handle = "";
|
||||
try
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc == null) return handle;
|
||||
var db = doc.Database;
|
||||
|
||||
// 先计算所有文字的合并 Extents
|
||||
double? minX = null, minY = null, maxX = null, maxY = null;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Bounds == null) continue;
|
||||
Extents3d ext = item.Bounds.Value;
|
||||
if (minX == null || ext.MinPoint.X < minX.Value) minX = ext.MinPoint.X;
|
||||
if (minY == null || ext.MinPoint.Y < minY.Value) minY = ext.MinPoint.Y;
|
||||
if (maxX == null || ext.MaxPoint.X > maxX.Value) maxX = ext.MaxPoint.X;
|
||||
if (maxY == null || ext.MaxPoint.Y > maxY.Value) maxY = ext.MaxPoint.Y;
|
||||
}
|
||||
if (minX == null) return handle;
|
||||
|
||||
using (var tr = db.TransactionManager.StartTransaction())
|
||||
{
|
||||
// 确保专用图层存在
|
||||
LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
|
||||
ObjectId layerId;
|
||||
if (lt.Has(PaLayerName))
|
||||
{
|
||||
layerId = lt[PaLayerName];
|
||||
}
|
||||
else
|
||||
{
|
||||
lt.UpgradeOpen();
|
||||
var layerDef = new LayerTableRecord { Name = PaLayerName };
|
||||
layerId = lt.Add(layerDef);
|
||||
tr.AddNewlyCreatedDBObject(layerDef, true);
|
||||
}
|
||||
|
||||
var ms = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
|
||||
|
||||
// ★ 先删除这批文字之前已有的旧框(避免小框和大框同时存在)
|
||||
// 遍历当前空间,找出 __CADCJ_PA_FRAME 图层上与这批文字 Bounds 相交的实体,
|
||||
// 通过 EraseGroupForEntity 删除整个组(边框+蒙版一起删)
|
||||
var idsToDelete = new List<ObjectId>();
|
||||
foreach (ObjectId entId in ms)
|
||||
{
|
||||
var ent = tr.GetObject(entId, OpenMode.ForRead, false, true) as Entity;
|
||||
if (ent == null) continue;
|
||||
if (ent.LayerId != layerId) continue;
|
||||
if (ent.IsErased) continue;
|
||||
Extents3d? entExt = null;
|
||||
try { entExt = ent.GeometricExtents; } catch { }
|
||||
if (!entExt.HasValue) continue;
|
||||
// 检查是否与任何文字 item 的 Bounds 相交
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Bounds == null) continue;
|
||||
if (BoundsIntersect(entExt.Value, item.Bounds.Value))
|
||||
{
|
||||
idsToDelete.Add(entId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (ObjectId id in idsToDelete)
|
||||
{
|
||||
// ★ 先删除同组的其他实体(Hatch),再删除主实体
|
||||
EraseGroupForEntity(tr, id);
|
||||
var ent = tr.GetObject(id, OpenMode.ForWrite);
|
||||
if (!ent.IsErased) ent.Erase();
|
||||
}
|
||||
|
||||
// ★ 边距极小:总宽高的 2%,最小 0.5
|
||||
double totalW = maxX.Value - minX.Value;
|
||||
double totalH = maxY.Value - minY.Value;
|
||||
double padX = Math.Max(totalW * 0.02, 0.5);
|
||||
double padY = Math.Max(totalH * 0.02, 0.5);
|
||||
|
||||
// 大框四角点(带边距,X和Y方向各自按比例)
|
||||
Point3d p1 = new Point3d(minX.Value - padX, minY.Value - padY, 0);
|
||||
Point3d p2 = new Point3d(maxX.Value + padX, minY.Value - padY, 0);
|
||||
Point3d p3 = new Point3d(maxX.Value + padX, maxY.Value + padY, 0);
|
||||
Point3d p4 = new Point3d(minX.Value - padX, maxY.Value + padY, 0);
|
||||
|
||||
var pl = new Polyline();
|
||||
pl.AddVertexAt(0, new Point2d(p1.X, p1.Y), 0, 0, 0);
|
||||
pl.AddVertexAt(1, new Point2d(p2.X, p2.Y), 0, 0, 0);
|
||||
pl.AddVertexAt(2, new Point2d(p3.X, p3.Y), 0, 0, 0);
|
||||
pl.AddVertexAt(3, new Point2d(p4.X, p4.Y), 0, 0, 0);
|
||||
pl.Closed = true;
|
||||
pl.Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex);
|
||||
pl.LayerId = layerId;
|
||||
pl.LineWeight = LineWeight.LineWeight070;
|
||||
|
||||
ms.AppendEntity(pl);
|
||||
tr.AddNewlyCreatedDBObject(pl, true);
|
||||
// ★ 记录大框的 Handle
|
||||
handle = pl.Handle.ToString();
|
||||
|
||||
// ★ 添加半透明填充蒙版(用 Hatch 实体,支持透明度)
|
||||
var hatch = new Hatch();
|
||||
hatch.SetHatchPattern(HatchPatternType.PreDefined, "SOLID");
|
||||
hatch.Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex);
|
||||
hatch.Transparency = new Transparency(65); // 65% 透明度
|
||||
hatch.LayerId = layerId;
|
||||
ms.AppendEntity(hatch);
|
||||
tr.AddNewlyCreatedDBObject(hatch, true);
|
||||
|
||||
ObjectIdCollection hatchLoopIds = new ObjectIdCollection();
|
||||
hatchLoopIds.Add(pl.Id);
|
||||
hatch.AppendLoop(HatchLoopTypes.Default, hatchLoopIds);
|
||||
|
||||
// ★ 把 Polyline 和 Hatch 创建为一个组(整体选中/整体删除)
|
||||
MakeFrameGroup(db, tr, pl.Id, hatch.Id);
|
||||
|
||||
tr.Commit();
|
||||
}
|
||||
|
||||
doc.Editor.UpdateScreen();
|
||||
doc.Editor.Regen();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null) doc.Editor.WriteMessage("\n[扒数据] 画合并框失败: " + ex.Message);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查两个 Extents3d 是否相交(用于删除旧框时判断范围重叠)。
|
||||
/// </summary>
|
||||
private static bool BoundsIntersect(Extents3d a, Extents3d b)
|
||||
{
|
||||
// X 方向不相交
|
||||
if (a.MaxPoint.X < b.MinPoint.X || a.MinPoint.X > b.MaxPoint.X) return false;
|
||||
// Y 方向不相交
|
||||
if (a.MaxPoint.Y < b.MinPoint.Y || a.MinPoint.Y > b.MaxPoint.Y) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ★ 把 Polyline(边框) 和 Hatch(蒙版) 创建为一个命名组,
|
||||
/// 这样用户在 CAD 里选中/删除其中一个时,整个组(边框+蒙版)会一起被选中/删除。
|
||||
/// </summary>
|
||||
private static void MakeFrameGroup(Database db, Transaction tr, ObjectId plId, ObjectId hatchId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var groupDict = (DBDictionary)tr.GetObject(db.GroupDictionaryId, OpenMode.ForWrite);
|
||||
// 组名用 Polyline 的 Handle 保证唯一
|
||||
string groupName = "CADCJ_PA_FRAME_" + plId.Handle.ToString();
|
||||
var group = new Group(groupName, true); // true = 可选中
|
||||
group.Append(new ObjectIdCollection(new[] { plId, hatchId }));
|
||||
groupDict.SetAt(groupName, group);
|
||||
tr.AddNewlyCreatedDBObject(group, true);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ★ 删除包含指定实体的所有 Group 内的所有实体(连同 Group 本身一起删除)。
|
||||
/// 用于"删除框时自动删除关联的蒙版"。
|
||||
/// </summary>
|
||||
public static void EraseGroupForEntity(Transaction tr, ObjectId entId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ent = tr.GetObject(entId, OpenMode.ForRead) as Entity;
|
||||
if (ent == null) return;
|
||||
// 获取实体的所有 PersistentReactor(Group 会作为 reactor 记录)
|
||||
ObjectIdCollection reactors = ent.GetPersistentReactorIds();
|
||||
var idsToErase = new List<ObjectId>();
|
||||
var groupsToErase = new List<ObjectId>();
|
||||
foreach (ObjectId reactorId in reactors)
|
||||
{
|
||||
var grp = tr.GetObject(reactorId, OpenMode.ForRead) as Group;
|
||||
if (grp == null) continue;
|
||||
// 收集 Group 内所有实体
|
||||
foreach (ObjectId memberId in grp.GetAllEntityIds())
|
||||
{
|
||||
if (!idsToErase.Contains(memberId)) idsToErase.Add(memberId);
|
||||
}
|
||||
groupsToErase.Add(reactorId);
|
||||
}
|
||||
// 删除 Group 内所有实体
|
||||
foreach (ObjectId memberId in idsToErase)
|
||||
{
|
||||
if (memberId == entId) continue; // 主实体由调用方删除
|
||||
try
|
||||
{
|
||||
var member = tr.GetObject(memberId, OpenMode.ForWrite);
|
||||
if (!member.IsErased) member.Erase();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
// 删除 Group 本身
|
||||
foreach (ObjectId groupId in groupsToErase)
|
||||
{
|
||||
try
|
||||
{
|
||||
var grp = tr.GetObject(groupId, OpenMode.ForWrite);
|
||||
if (!grp.IsErased) grp.Erase();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在每个文字周围画一个彩色矩形框(LWPolyline),不改文字本身的颜色。
|
||||
/// 所有矩形框放在专用图层 __CADCJ_PA_FRAME 上,方便批量清除。
|
||||
/// 返回:每个 item 对应的框 Handle 字符串列表(和 items 顺序一致;无 Bounds 的为空字符串)。
|
||||
/// </summary>
|
||||
private static List<string> DrawTextFrames(List<TextItem> items, short colorIndex)
|
||||
{
|
||||
var handles = new List<string>();
|
||||
try
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc == null) return handles;
|
||||
var db = doc.Database;
|
||||
|
||||
using (var tr = db.TransactionManager.StartTransaction())
|
||||
{
|
||||
// 确保专用图层存在(如果不存在就创建)
|
||||
LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
|
||||
ObjectId layerId;
|
||||
if (lt.Has(PaLayerName))
|
||||
{
|
||||
layerId = lt[PaLayerName];
|
||||
}
|
||||
else
|
||||
{
|
||||
lt.UpgradeOpen();
|
||||
var layerDef = new LayerTableRecord { Name = PaLayerName };
|
||||
layerId = lt.Add(layerDef);
|
||||
tr.AddNewlyCreatedDBObject(layerDef, true);
|
||||
}
|
||||
|
||||
// 获取当前空间(模型空间或图纸空间)
|
||||
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
|
||||
var ms = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Bounds == null) { handles.Add(""); continue; }
|
||||
Extents3d ext = item.Bounds.Value;
|
||||
|
||||
// ★ 用文字的实际位置和高度估算更紧凑的范围
|
||||
// GeometricExtents 可能包含字体预留的 ascent/descent 空间,
|
||||
// 这里用文字中心点和估算的宽高来定义框,使其更贴合文字
|
||||
double textW, textH;
|
||||
if (ext.MaxPoint.Y - ext.MinPoint.Y > ext.MaxPoint.X - ext.MinPoint.X * 3)
|
||||
{
|
||||
// 如果高度远大于宽度(多行或特殊情况),用 GeometricExtents
|
||||
textW = ext.MaxPoint.X - ext.MinPoint.X;
|
||||
textH = ext.MaxPoint.Y - ext.MinPoint.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 单行文字:用中心点和估算尺寸
|
||||
textW = ext.MaxPoint.X - ext.MinPoint.X;
|
||||
textH = ext.MaxPoint.Y - ext.MinPoint.Y;
|
||||
}
|
||||
|
||||
// ★ 边距极小:文字宽高的 3%,最小 0.3
|
||||
double padX = Math.Max(textW * 0.03, 0.3);
|
||||
double padY = Math.Max(textH * 0.03, 0.3);
|
||||
|
||||
// 矩形四角点(带极小边距)
|
||||
Point3d p1 = new Point3d(ext.MinPoint.X - padX, ext.MinPoint.Y - padY, 0);
|
||||
Point3d p2 = new Point3d(ext.MaxPoint.X + padX, ext.MinPoint.Y - padY, 0);
|
||||
Point3d p3 = new Point3d(ext.MaxPoint.X + padX, ext.MaxPoint.Y + padY, 0);
|
||||
Point3d p4 = new Point3d(ext.MinPoint.X - padX, ext.MaxPoint.Y + padY, 0);
|
||||
|
||||
// 创建 LWPolyline 矩形
|
||||
var pl = new Polyline();
|
||||
pl.AddVertexAt(0, new Point2d(p1.X, p1.Y), 0, 0, 0);
|
||||
pl.AddVertexAt(1, new Point2d(p2.X, p2.Y), 0, 0, 0);
|
||||
pl.AddVertexAt(2, new Point2d(p3.X, p3.Y), 0, 0, 0);
|
||||
pl.AddVertexAt(3, new Point2d(p4.X, p4.Y), 0, 0, 0);
|
||||
pl.Closed = true;
|
||||
|
||||
// 设置颜色和图层
|
||||
pl.Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex);
|
||||
pl.LayerId = layerId;
|
||||
// 线宽更粗,看得清
|
||||
pl.LineWeight = LineWeight.LineWeight070;
|
||||
|
||||
ms.AppendEntity(pl);
|
||||
tr.AddNewlyCreatedDBObject(pl, true);
|
||||
// ★ 记录框的 Handle,用于关联表格数据行
|
||||
handles.Add(pl.Handle.ToString());
|
||||
|
||||
// ★ 添加半透明填充蒙版(用 Hatch 实体,支持透明度)
|
||||
var hatch = new Hatch();
|
||||
hatch.SetHatchPattern(HatchPatternType.PreDefined, "SOLID");
|
||||
hatch.Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex);
|
||||
hatch.Transparency = new Transparency(65); // 65% 透明度
|
||||
hatch.LayerId = layerId;
|
||||
ms.AppendEntity(hatch);
|
||||
tr.AddNewlyCreatedDBObject(hatch, true);
|
||||
|
||||
ObjectIdCollection hatchLoopIds = new ObjectIdCollection();
|
||||
hatchLoopIds.Add(pl.Id);
|
||||
hatch.AppendLoop(HatchLoopTypes.Default, hatchLoopIds);
|
||||
|
||||
// ★ 把 Polyline 和 Hatch 创建为一个组(整体选中/整体删除)
|
||||
MakeFrameGroup(db, tr, pl.Id, hatch.Id);
|
||||
}
|
||||
|
||||
tr.Commit();
|
||||
}
|
||||
|
||||
// 刷新显示
|
||||
doc.Editor.UpdateScreen();
|
||||
doc.Editor.Regen();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null) doc.Editor.WriteMessage("\n[扒数据] 画框失败: " + ex.Message);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有扒数据时画的彩色矩形框(删除 __CADCJ_PA_FRAME 图层上的所有对象)。
|
||||
/// 不动文字本身,文字保持原色。
|
||||
/// </summary>
|
||||
public static void ClearAllHighlight()
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc == null) return;
|
||||
var db = doc.Database;
|
||||
|
||||
using (var tr = db.TransactionManager.StartTransaction())
|
||||
{
|
||||
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
|
||||
// 遍历所有块定义(模型空间+所有布局),删除目标图层上的对象
|
||||
foreach (ObjectId btrId in bt)
|
||||
{
|
||||
var btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
|
||||
var ids = new List<ObjectId>();
|
||||
foreach (ObjectId entId in btr)
|
||||
{
|
||||
var ent = tr.GetObject(entId, OpenMode.ForRead, false, true) as Entity;
|
||||
if (ent == null) continue;
|
||||
if (ent.Layer == PaLayerName)
|
||||
{
|
||||
ids.Add(entId);
|
||||
}
|
||||
}
|
||||
if (ids.Count > 0)
|
||||
{
|
||||
btr.UpgradeOpen();
|
||||
foreach (ObjectId id in ids)
|
||||
{
|
||||
// ★ 先删除同组的其他实体,再删除主实体
|
||||
EraseGroupForEntity(tr, id);
|
||||
var ent = tr.GetObject(id, OpenMode.ForWrite);
|
||||
if (!ent.IsErased) ent.Erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
tr.Commit();
|
||||
}
|
||||
|
||||
doc.Editor.UpdateScreen();
|
||||
doc.Editor.Regen();
|
||||
|
||||
try { doc.Editor.WriteMessage("\n[扒数据] 已清除所有标记框。"); } catch { }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = AcAp.DocumentManager.MdiActiveDocument;
|
||||
if (doc != null) doc.Editor.WriteMessage("\n[扒数据] 清除失败: " + ex.Message);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
private class TextItem
|
||||
{
|
||||
public string Text;
|
||||
public Point3d Position;
|
||||
public Extents3d? Bounds;
|
||||
public ObjectId ObjectId;
|
||||
public string TextHandle = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10/11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">unaware</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
REM ============================================
|
||||
REM Auto-generated build script
|
||||
REM All non-ASCII paths are in build.rsp (UTF-8)
|
||||
REM Keep this .bat file ASCII-only to avoid cmd codepage issues
|
||||
REM ============================================
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
"%SystemRoot%\Microsoft.NET\Framework64\v4.0.30319\csc.exe" @build.rsp
|
||||
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo Build OK. Launching...
|
||||
powershell -NoProfile -Command "Start-Process -FilePath ('%~dp0' + (Select-String -Path build.rsp -Pattern '^/out:(.+)$').Matches.Groups[1].Value)"
|
||||
) else (
|
||||
echo Build FAILED - see messages above
|
||||
pause
|
||||
)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Auto-generated csc response file
|
||||
/target:library
|
||||
/platform:x64
|
||||
/utf8output
|
||||
/out:cadcj.dll
|
||||
/win32manifest:app.manifest
|
||||
/r:System.Windows.Forms.dll
|
||||
/r:System.Drawing.dll
|
||||
/r:System.dll
|
||||
/r:System.Data.dll
|
||||
/r:System.Core.dll
|
||||
/r:System.Xml.dll
|
||||
/r:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF\PresentationCore.dll"
|
||||
/r:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF\WindowsBase.dll"
|
||||
/r:System.IO.Compression.dll
|
||||
/r:System.IO.Compression.FileSystem.dll
|
||||
/r:"AdvancedDataGridView.dll"
|
||||
/r:"System.Data.SQLite.dll"
|
||||
/r:"E:\cad2\AutoCAD 2018\acdbmgd.dll"
|
||||
/r:"E:\cad2\AutoCAD 2018\acmgd.dll"
|
||||
/r:"E:\cad2\AutoCAD 2018\accoremgd.dll"
|
||||
CadPlugin.cs
|
||||
Form3.cs
|
||||
Form3.ui.cs
|
||||
PaTextSelector.cs
|
||||
新建项目.cs
|
||||
新建项目.ui.cs
|
||||
@@ -0,0 +1,25 @@
|
||||
/target:library
|
||||
/platform:x64
|
||||
/utf8output
|
||||
/out:cadcj_v2.dll
|
||||
/r:System.Windows.Forms.dll
|
||||
/r:System.Drawing.dll
|
||||
/r:System.dll
|
||||
/r:System.Data.dll
|
||||
/r:System.Core.dll
|
||||
/r:System.Xml.dll
|
||||
/r:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF\PresentationCore.dll"
|
||||
/r:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF\WindowsBase.dll"
|
||||
/r:System.IO.Compression.dll
|
||||
/r:System.IO.Compression.FileSystem.dll
|
||||
/r:"AdvancedDataGridView.dll"
|
||||
/r:"System.Data.SQLite.dll"
|
||||
/r:"D:\ProgramData\cad\AutoCAD 2018\acdbmgd.dll"
|
||||
/r:"D:\ProgramData\cad\AutoCAD 2018\acmgd.dll"
|
||||
/r:"D:\ProgramData\cad\AutoCAD 2018\accoremgd.dll"
|
||||
CadPlugin.cs
|
||||
Form3.cs
|
||||
Form3.ui.cs
|
||||
PaTextSelector.cs
|
||||
新建项目.cs
|
||||
新建项目.ui.cs
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ApiKey": "sk-65b3963488ab420aa9df3e4dafa02768",
|
||||
"BaseUrl": "https://api.deepseek.com",
|
||||
"Model": "deepseek-v4-flash",
|
||||
"Enabled": false,
|
||||
"WebSearch": true
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,26 @@
|
||||
@echo off
|
||||
chcp 936 >nul
|
||||
title 成套报价 CAD 插件卸载程序
|
||||
|
||||
REM 切换到批处理文件所在目录(支持从任意位置运行)
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM 检查 PowerShell 脚本是否存在
|
||||
if not exist "卸载CAD插件.ps1" (
|
||||
echo [错误] 未找到 卸载CAD插件.ps1
|
||||
echo 请确保本批处理文件与 卸载CAD插件.ps1 在同一目录。
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM 以管理员身份运行 PowerShell 脚本(避免 HKLM 删除提权弹窗中断)
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process powershell -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File','%~dp0卸载CAD插件.ps1' -Verb RunAs -Wait"
|
||||
|
||||
REM 若用户取消提权,则非提权运行(HKLM 删除会自动提权,acaddoc.lsp 和 TRUSTEDPATHS 无需管理员)
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo [提示] 管理员提权被取消,尝试非提权运行...
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "卸载CAD插件.ps1"
|
||||
)
|
||||
|
||||
pause
|
||||
@@ -0,0 +1,263 @@
|
||||
# ===========================================================================
|
||||
# 成套报价 CAD 插件 - 通用卸载脚本
|
||||
# 自动检测 AutoCAD 并移除所有安装时写入的配置
|
||||
# 用法:以管理员身份运行,或在 PowerShell 中执行本脚本
|
||||
# ===========================================================================
|
||||
|
||||
# --- 0. 环境与自检 ---
|
||||
$ErrorActionPreference = 'Stop'
|
||||
# $PSScriptRoot 在 -File 模式下可靠返回脚本所在目录(PS 3.0+)
|
||||
$scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path }
|
||||
$scriptDir = $scriptDir.TrimEnd('\') # 统一去除尾部反斜杠,便于比较
|
||||
|
||||
# 检测是否管理员(写 HKLM 需要)
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
if (-not $isAdmin) {
|
||||
Write-Host "[提示] 当前未以管理员身份运行,HKLM 注册表删除将自动提权。" -ForegroundColor Yellow
|
||||
Write-Host " acaddoc.lsp 和 TRUSTEDPATHS 无需管理员权限。" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host " 成套报价 CAD 插件卸载程序" -ForegroundColor Cyan
|
||||
Write-Host " 插件目录: $scriptDir" -ForegroundColor Cyan
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# --- 1. 检测已安装的 AutoCAD 版本 ---
|
||||
Write-Host "[1/4] 检测 AutoCAD 安装..." -ForegroundColor Green
|
||||
|
||||
$acadInstalls = @()
|
||||
$regRoots = @(
|
||||
'HKLM:\SOFTWARE\Autodesk\AutoCAD',
|
||||
'HKLM:\SOFTWARE\WOW6432Node\Autodesk\AutoCAD'
|
||||
)
|
||||
|
||||
foreach ($root in $regRoots) {
|
||||
if (-not (Test-Path $root)) { continue }
|
||||
Get-ChildItem $root -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -match '^R\d+\.0$' } | ForEach-Object {
|
||||
$verKey = $_.PSPath
|
||||
$verNum = $_.PSChildName
|
||||
Get-ChildItem $verKey -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -match '^ACAD-\d+:\d+$' } | ForEach-Object {
|
||||
$langKey = $_.PSPath
|
||||
$props = Get-ItemProperty -Path $langKey -ErrorAction SilentlyContinue
|
||||
$loc = $props.AcadLocation
|
||||
$prod = $props.ProductName
|
||||
if ($loc -and (Test-Path $loc)) {
|
||||
$acadInstalls += [PSCustomObject]@{
|
||||
Version = $verNum
|
||||
LangKey = $_.PSChildName
|
||||
Location = $loc
|
||||
Product = $prod
|
||||
HKLMKey = $langKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($acadInstalls.Count -eq 0) {
|
||||
Write-Host "[警告] 未检测到已安装的 AutoCAD,无法清理注册表。" -ForegroundColor Yellow
|
||||
Write-Host " 如有手动残留的 acaddoc.lsp,请手动检查。" -ForegroundColor Yellow
|
||||
if (-not $env:CADCJ_SILENT) { Read-Host "按回车退出" }
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Host "找到 $($acadInstalls.Count) 个 AutoCAD 安装:" -ForegroundColor Green
|
||||
for ($i = 0; $i -lt $acadInstalls.Count; $i++) {
|
||||
$a = $acadInstalls[$i]
|
||||
Write-Host (" [{0}] {1} ({2}) - {3}" -f $i, $a.Product, $a.Version, $a.Location) -ForegroundColor White
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# 若有多个,让用户选择
|
||||
$target = $acadInstalls[0]
|
||||
if ($acadInstalls.Count -gt 1) {
|
||||
if ($env:CADCJ_SILENT) {
|
||||
Write-Host "[静默模式] 检测到多个 AutoCAD,默认选择第一个: $($target.Product)" -ForegroundColor Yellow
|
||||
} else {
|
||||
do {
|
||||
$sel = Read-Host "请选择要卸载的 AutoCAD 序号 (0-$($acadInstalls.Count - 1)),默认 0"
|
||||
if ([string]::IsNullOrWhiteSpace($sel)) { $sel = '0' }
|
||||
} until ($sel -match '^\d+$' -and [int]$sel -lt $acadInstalls.Count)
|
||||
$target = $acadInstalls[[int]$sel]
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("目标: {0} ({1})" -f $target.Product, $target.Version) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# --- 2. 删除 HKLM 注册表 CADCJ 节点 ---
|
||||
Write-Host "[2/4] 删除 HKLM 自动加载注册表..." -ForegroundColor Green
|
||||
|
||||
$hklmPath = $target.HKLMKey -replace '^HKLM:\\', ''
|
||||
$appsPath = "HKLM:\$hklmPath\Apps\CADCJ"
|
||||
|
||||
if (-not (Test-Path $appsPath)) {
|
||||
Write-Host " HKLM 中无 CADCJ 节点,跳过" -ForegroundColor DarkGray
|
||||
} else {
|
||||
if ($isAdmin) {
|
||||
Remove-Item -Path $appsPath -Recurse -Force -ErrorAction SilentlyContinue
|
||||
if (-not (Test-Path $appsPath)) {
|
||||
Write-Host " 已删除 HKLM 注册表节点" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " [警告] 删除失败,请检查权限" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
# 提权删除
|
||||
$tmpPs1 = Join-Path $env:TEMP "cadcj_uninstall_hklm.ps1"
|
||||
$tmpContent = @"
|
||||
`$ErrorActionPreference = 'Stop'
|
||||
`$path = '$appsPath'
|
||||
if (Test-Path `$path) { Remove-Item -Path `$path -Recurse -Force }
|
||||
if (-not (Test-Path `$path)) { Write-Host ' HKLM 注册表节点已删除' } else { Write-Host ' [警告] 删除失败' }
|
||||
"@
|
||||
$tmpContent | Out-File -FilePath $tmpPs1 -Encoding UTF8 -Force
|
||||
$p = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile","-ExecutionPolicy","Bypass","-File",$tmpPs1 -Verb RunAs -Wait -PassThru
|
||||
if ($p.ExitCode -eq 0) {
|
||||
Write-Host " 已删除 HKLM 注册表节点" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " [警告] HKLM 删除失败(用户取消提权?)" -ForegroundColor Yellow
|
||||
}
|
||||
Remove-Item $tmpPs1 -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# --- 3. 从所有 TRUSTEDPATHS 移除插件目录 ---
|
||||
Write-Host "[3/4] 移除受信任路径(TRUSTEDPATHS)..." -ForegroundColor Green
|
||||
|
||||
$hkcuRoot = "HKCU:\SOFTWARE\Autodesk\AutoCAD\$($target.Version)\$($target.LangKey)"
|
||||
|
||||
$targets = @()
|
||||
$topVar = Join-Path $hkcuRoot 'Variables'
|
||||
if (Test-Path $topVar) { $targets += $topVar }
|
||||
$profRoot = Join-Path $hkcuRoot 'Profiles'
|
||||
if (Test-Path $profRoot) {
|
||||
Get-ChildItem $profRoot -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
$pv = Join-Path $_.PSPath 'Variables'
|
||||
if (Test-Path $pv) { $targets += $pv }
|
||||
}
|
||||
}
|
||||
|
||||
$removedCount = 0
|
||||
foreach ($t in $targets) {
|
||||
$cur = (Get-ItemProperty -Path $t -Name 'TRUSTEDPATHS' -ErrorAction SilentlyContinue).TRUSTEDPATHS
|
||||
if (-not $cur) { continue }
|
||||
# 分割后去除每段首尾空白,统一比较
|
||||
$allParts = $cur.Split(';') | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
|
||||
$keepParts = $allParts | Where-Object { -not $_.Equals($scriptDir, [StringComparison]::OrdinalIgnoreCase) }
|
||||
$hasOurs = ($allParts | Where-Object { $_.Equals($scriptDir, [StringComparison]::OrdinalIgnoreCase) }).Count -gt 0
|
||||
$shortName = $t -replace '.*\\Profiles\\','Profile\' -replace '\\Variables',''
|
||||
if (-not $hasOurs) {
|
||||
Write-Host " 未包含: $shortName 跳过" -ForegroundColor DarkGray
|
||||
continue
|
||||
}
|
||||
if ($keepParts.Count -eq 0) {
|
||||
# 全部都是本目录,删除整个值
|
||||
Remove-ItemProperty -Path $t -Name 'TRUSTEDPATHS' -ErrorAction SilentlyContinue
|
||||
Write-Host " 已清空: $shortName" -ForegroundColor Green
|
||||
} else {
|
||||
$newVal = $keepParts -join ';'
|
||||
Set-ItemProperty -Path $t -Name 'TRUSTEDPATHS' -Value $newVal -Type String -Force
|
||||
Write-Host " 已移除: $shortName" -ForegroundColor Green
|
||||
}
|
||||
$removedCount++
|
||||
}
|
||||
if ($removedCount -eq 0) {
|
||||
Write-Host " 无需修改(TRUSTEDPATHS 中未包含本目录)" -ForegroundColor DarkGray
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# --- 4. 清理 acaddoc.lsp 中的 CADCJ 加载逻辑 ---
|
||||
Write-Host "[4/4] 清理 acaddoc.lsp 自动加载脚本..." -ForegroundColor Green
|
||||
|
||||
$acadDirName = Split-Path $target.Location -Leaf
|
||||
$roamingBase = Join-Path $env:APPDATA "Autodesk\$acadDirName\$($target.Version)\chs\Support"
|
||||
$installSupport = Join-Path $target.Location "Support"
|
||||
|
||||
$candidateDirs = @($roamingBase, $installSupport) | Select-Object -Unique
|
||||
$supDir = $null
|
||||
foreach ($d in $candidateDirs) {
|
||||
if (Test-Path $d) { $supDir = $d; break }
|
||||
}
|
||||
|
||||
if (-not $supDir) {
|
||||
Write-Host " 未找到 Support 目录,跳过" -ForegroundColor DarkGray
|
||||
} else {
|
||||
$lspPath = Join-Path $supDir 'acaddoc.lsp'
|
||||
if (-not (Test-Path $lspPath)) {
|
||||
Write-Host " acaddoc.lsp 不存在,跳过" -ForegroundColor DarkGray
|
||||
} else {
|
||||
# 读取(尝试 GB2312,失败则用默认编码)
|
||||
$content = $null
|
||||
try {
|
||||
$content = [System.IO.File]::ReadAllText($lspPath, [System.Text.Encoding]::GetEncoding('GB2312'))
|
||||
} catch {
|
||||
$content = Get-Content $lspPath -Raw -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if (-not $content) {
|
||||
Write-Host " acaddoc.lsp 为空或无法读取,跳过" -ForegroundColor DarkGray
|
||||
} elseif ($content -notmatch 'CADCJ|cadcj') {
|
||||
Write-Host " acaddoc.lsp 中无 CADCJ 加载逻辑,跳过" -ForegroundColor DarkGray
|
||||
} else {
|
||||
# 判断是否为纯 CADCJ 文件(以 ;;; CADCJ 开头或整个文件就是 CADCJ 块)
|
||||
# 移除 CADCJ 加载块(从 ;;; CADCJ 标记开始到 (princ) 结束)
|
||||
$pattern = '(?s)\s*;;; CADCJ.*?\(princ\)\s*'
|
||||
$newContent = [regex]::Replace($content, $pattern, "`r`n")
|
||||
# 清理多余空行
|
||||
$newContent = $newContent -replace "(`r`n){3,}", "`r`n`r`n"
|
||||
$newContent = $newContent.TrimEnd()
|
||||
|
||||
try {
|
||||
if ([string]::IsNullOrWhiteSpace($newContent)) {
|
||||
# 文件清空了,删除文件
|
||||
Remove-Item $lspPath -Force
|
||||
Write-Host " 已删除: $lspPath(文件仅含 CADCJ 内容)" -ForegroundColor Green
|
||||
} else {
|
||||
# 保留其他内容,只移除 CADCJ 块
|
||||
[System.IO.File]::WriteAllText($lspPath, $newContent + "`r`n", [System.Text.Encoding]::GetEncoding('GB2312'))
|
||||
Write-Host " 已清理 CADCJ 加载逻辑,保留其他内容: $lspPath" -ForegroundColor Green
|
||||
}
|
||||
} catch {
|
||||
# 写入失败时提权
|
||||
Write-Host " 写入失败,尝试提权: $($_.Exception.Message)" -ForegroundColor Yellow
|
||||
$tmpLsp = Join-Path $env:TEMP 'cadcj_acaddoc_clean.lsp'
|
||||
if ([string]::IsNullOrWhiteSpace($newContent)) {
|
||||
$delCmd = "Remove-Item '$lspPath' -Force -ErrorAction SilentlyContinue"
|
||||
$p = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile","-Command",$delCmd -Verb RunAs -Wait -PassThru
|
||||
if ($p.ExitCode -eq 0) {
|
||||
Write-Host " 已删除(提权): $lspPath" -ForegroundColor Green
|
||||
}
|
||||
} else {
|
||||
[System.IO.File]::WriteAllText($tmpLsp, $newContent + "`r`n", [System.Text.Encoding]::GetEncoding('GB2312'))
|
||||
$copyCmd = "Copy-Item '$tmpLsp' '$lspPath' -Force"
|
||||
$p = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile","-Command",$copyCmd -Verb RunAs -Wait -PassThru
|
||||
if ($p.ExitCode -eq 0) {
|
||||
Write-Host " 已清理(提权): $lspPath" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
Remove-Item $tmpLsp -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# --- 完成 ---
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host " 卸载完成!" -ForegroundColor Green
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "卸载摘要:" -ForegroundColor White
|
||||
Write-Host " AutoCAD: $($target.Product) ($($target.Version))" -ForegroundColor White
|
||||
Write-Host " HKLM 注册: 已删除 CADCJ 节点" -ForegroundColor White
|
||||
Write-Host " TRUSTEDPATHS: 已移除 $scriptDir" -ForegroundColor White
|
||||
Write-Host " acaddoc.lsp: 已清理 CADCJ 加载逻辑" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "插件文件本身未删除(cadcj.dll 等),如需彻底删除请手动移除目录:" -ForegroundColor Yellow
|
||||
Write-Host " $scriptDir" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
if (-not $env:CADCJ_SILENT) { Read-Host "按回车退出" }
|
||||
@@ -0,0 +1,26 @@
|
||||
@echo off
|
||||
chcp 936 >nul
|
||||
title 成套报价 CAD 插件安装程序
|
||||
|
||||
REM 切换到批处理文件所在目录(支持从任意位置运行)
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM 检查 PowerShell 脚本是否存在
|
||||
if not exist "安装CAD插件.ps1" (
|
||||
echo [错误] 未找到 安装CAD插件.ps1
|
||||
echo 请确保本批处理文件与 安装CAD插件.ps1 在同一目录。
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM 以管理员身份运行 PowerShell 脚本(避免 HKLM 写入提权弹窗中断)
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process powershell -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File','%~dp0安装CAD插件.ps1' -Verb RunAs -Wait"
|
||||
|
||||
REM 若用户取消提权,则非提权运行(HKLM 写入会自动提权,acaddoc.lsp 和 TRUSTEDPATHS 无需管理员)
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo [提示] 管理员提权被取消,尝试非提权运行...
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File "安装CAD插件.ps1"
|
||||
)
|
||||
|
||||
pause
|
||||
@@ -0,0 +1,309 @@
|
||||
# ===========================================================================
|
||||
# 成套报价 CAD 插件 - 通用安装脚本
|
||||
# 适用于任何电脑、任何已安装的 AutoCAD 版本(2010+)
|
||||
# 用法:以管理员身份运行,或在 PowerShell 中执行本脚本
|
||||
# ===========================================================================
|
||||
|
||||
# --- 0. 环境与自检 ---
|
||||
$ErrorActionPreference = 'Stop'
|
||||
# $PSScriptRoot 在 -File 模式下可靠返回脚本所在目录(PS 3.0+)
|
||||
$scriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path }
|
||||
$scriptDir = $scriptDir.TrimEnd('\') # 统一去除尾部反斜杠,便于比较
|
||||
$dllPath = Join-Path $scriptDir 'cadcj.dll'
|
||||
|
||||
if (-not (Test-Path $dllPath)) {
|
||||
Write-Host "[错误] 未找到 cadcj.dll,请确保脚本与 cadcj.dll 在同一目录。" -ForegroundColor Red
|
||||
Write-Host " 当前目录: $scriptDir" -ForegroundColor Yellow
|
||||
if (-not $env:CADCJ_SILENT) { Read-Host "按回车退出" }
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 检测是否管理员(写 HKLM 需要)
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
if (-not $isAdmin) {
|
||||
Write-Host "[提示] 当前未以管理员身份运行,HKLM 注册表写入将自动提权。" -ForegroundColor Yellow
|
||||
Write-Host " acaddoc.lsp 和 TRUSTEDPATHS 无需管理员权限。" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host " 成套报价 CAD 插件安装程序" -ForegroundColor Cyan
|
||||
Write-Host " 插件目录: $scriptDir" -ForegroundColor Cyan
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# --- 1. 检测已安装的 AutoCAD 版本 ---
|
||||
Write-Host "[1/4] 检测 AutoCAD 安装..." -ForegroundColor Green
|
||||
|
||||
# 同时检测 64 位和 32 位注册表视图
|
||||
$acadInstalls = @()
|
||||
$regRoots = @(
|
||||
'HKLM:\SOFTWARE\Autodesk\AutoCAD',
|
||||
'HKLM:\SOFTWARE\WOW6432Node\Autodesk\AutoCAD'
|
||||
)
|
||||
|
||||
foreach ($root in $regRoots) {
|
||||
if (-not (Test-Path $root)) { continue }
|
||||
# 遍历每个 Rxx.0 版本(如 R22.0=2018, R23.0=2020, R24.0=2022...)
|
||||
Get-ChildItem $root -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -match '^R\d+\.0$' } | ForEach-Object {
|
||||
$verKey = $_.PSPath
|
||||
$verNum = $_.PSChildName # 如 R22.0
|
||||
# 遍历该版本下的语言变体(如 ACAD-1001:804)
|
||||
Get-ChildItem $verKey -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -match '^ACAD-\d+:\d+$' } | ForEach-Object {
|
||||
$langKey = $_.PSPath
|
||||
$props = Get-ItemProperty -Path $langKey -ErrorAction SilentlyContinue
|
||||
$loc = $props.AcadLocation
|
||||
$prod = $props.ProductName
|
||||
if ($loc -and (Test-Path $loc)) {
|
||||
$acadInstalls += [PSCustomObject]@{
|
||||
Version = $verNum
|
||||
LangKey = $_.PSChildName
|
||||
Location = $loc
|
||||
Product = $prod
|
||||
RegRoot = $root
|
||||
HKLMKey = $langKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($acadInstalls.Count -eq 0) {
|
||||
Write-Host "[错误] 未检测到已安装的 AutoCAD。" -ForegroundColor Red
|
||||
if (-not $env:CADCJ_SILENT) { Read-Host "按回车退出" }
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "找到 $($acadInstalls.Count) 个 AutoCAD 安装:" -ForegroundColor Green
|
||||
for ($i = 0; $i -lt $acadInstalls.Count; $i++) {
|
||||
$a = $acadInstalls[$i]
|
||||
Write-Host (" [{0}] {1} ({2}) - {3}" -f $i, $a.Product, $a.Version, $a.Location) -ForegroundColor White
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# 若有多个,让用户选择
|
||||
$target = $acadInstalls[0]
|
||||
if ($acadInstalls.Count -gt 1) {
|
||||
if ($env:CADCJ_SILENT) {
|
||||
# 静默模式:默认选第一个,不交互
|
||||
Write-Host "[静默模式] 检测到多个 AutoCAD,默认选择第一个: $($target.Product)" -ForegroundColor Yellow
|
||||
} else {
|
||||
do {
|
||||
$sel = Read-Host "请选择要安装的 AutoCAD 序号 (0-$($acadInstalls.Count - 1)),默认 0"
|
||||
if ([string]::IsNullOrWhiteSpace($sel)) { $sel = '0' }
|
||||
} until ($sel -match '^\d+$' -and [int]$sel -lt $acadInstalls.Count)
|
||||
$target = $acadInstalls[[int]$sel]
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("目标: {0} ({1})" -f $target.Product, $target.Version) -ForegroundColor Cyan
|
||||
Write-Host ("路径: {0}" -f $target.Location) -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# --- 2. 修正 MODULE 路径到 HKLM ---
|
||||
Write-Host "[2/4] 注册 AutoCAD 自动加载(HKLM)..." -ForegroundColor Green
|
||||
|
||||
# HKLM 路径(去除 HKLM: 前缀,得到 SOFTWARE\Autodesk\AutoCAD\Rxx.0\ACAD-xxxx:xxx\Apps\CADCJ)
|
||||
$hklmPath = $target.HKLMKey -replace '^HKLM:\\', ''
|
||||
$appsPath = "HKLM:\$hklmPath\Apps\CADCJ"
|
||||
|
||||
# 写入函数(管理员直接写,非管理员生成临时 ps1 提权执行)
|
||||
function Write-HklmReg {
|
||||
param($Path, $Dll)
|
||||
if (-not (Test-Path $Path)) { New-Item -Path $Path -Force | Out-Null }
|
||||
Set-ItemProperty -Path $Path -Name 'DESCRIPTION' -Value '成套报价插件' -Type String -Force
|
||||
Set-ItemProperty -Path $Path -Name 'LOADCTRLS' -Value 2 -Type DWord -Force
|
||||
Set-ItemProperty -Path $Path -Name 'MANAGED' -Value 1 -Type DWord -Force
|
||||
Set-ItemProperty -Path $Path -Name 'MODULE' -Value $Dll -Type String -Force
|
||||
}
|
||||
|
||||
if ($isAdmin) {
|
||||
Write-HklmReg -Path $appsPath -Dll $dllPath
|
||||
Write-Host " HKLM 注册完成" -ForegroundColor Green
|
||||
} else {
|
||||
# 生成临时 ps1 提权执行(避免字符串嵌套转义问题)
|
||||
$tmpPs1 = Join-Path $env:TEMP "cadcj_install_hklm.ps1"
|
||||
$tmpContent = @"
|
||||
`$ErrorActionPreference = 'Stop'
|
||||
`$path = '$appsPath'
|
||||
`$dll = '$dllPath'
|
||||
if (-not (Test-Path `$path)) { New-Item -Path `$path -Force | Out-Null }
|
||||
Set-ItemProperty -Path `$path -Name 'DESCRIPTION' -Value '成套报价插件' -Type String -Force
|
||||
Set-ItemProperty -Path `$path -Name 'LOADCTRLS' -Value 2 -Type DWord -Force
|
||||
Set-ItemProperty -Path `$path -Name 'MANAGED' -Value 1 -Type DWord -Force
|
||||
Set-ItemProperty -Path `$path -Name 'MODULE' -Value `$dll -Type String -Force
|
||||
Write-Host ' HKLM 注册完成'
|
||||
"@
|
||||
$tmpContent | Out-File -FilePath $tmpPs1 -Encoding UTF8 -Force
|
||||
$p = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile","-ExecutionPolicy","Bypass","-File",$tmpPs1 -Verb RunAs -Wait -PassThru
|
||||
if ($p.ExitCode -ne 0) {
|
||||
Write-Host "[警告] HKLM 写入失败(用户取消提权?)。不影响后续安装,acaddoc.lsp 会自动加载。" -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host " HKLM 注册完成" -ForegroundColor Green
|
||||
}
|
||||
Remove-Item $tmpPs1 -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# --- 3. 添加 TRUSTEDPATHS 到所有用户配置(HKCU) ---
|
||||
Write-Host "[3/4] 添加受信任路径(TRUSTEDPATHS)..." -ForegroundColor Green
|
||||
|
||||
# 确定 HKCU 根路径
|
||||
$hkcuRoot = "HKCU:\SOFTWARE\Autodesk\AutoCAD\$($target.Version)\$($target.LangKey)"
|
||||
|
||||
$targets = @()
|
||||
# 顶层 Variables
|
||||
$topVar = Join-Path $hkcuRoot 'Variables'
|
||||
if (Test-Path $topVar) { $targets += $topVar }
|
||||
# 各 Profile 下的 Variables
|
||||
$profRoot = Join-Path $hkcuRoot 'Profiles'
|
||||
if (Test-Path $profRoot) {
|
||||
Get-ChildItem $profRoot -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
$pv = Join-Path $_.PSPath 'Variables'
|
||||
if (Test-Path $pv) { $targets += $pv }
|
||||
}
|
||||
}
|
||||
|
||||
$addedCount = 0
|
||||
foreach ($t in $targets) {
|
||||
$cur = (Get-ItemProperty -Path $t -Name 'TRUSTEDPATHS' -ErrorAction SilentlyContinue).TRUSTEDPATHS
|
||||
if ($cur -and ($cur.Split(';') -contains $scriptDir)) {
|
||||
Write-Host " 已存在: $($t -replace '.*\\Profiles\\','Profile\') 跳过" -ForegroundColor DarkGray
|
||||
continue
|
||||
}
|
||||
if ($cur) {
|
||||
$newVal = "$cur;$scriptDir"
|
||||
} else {
|
||||
$newVal = $scriptDir
|
||||
}
|
||||
try {
|
||||
New-ItemProperty -Path $t -Name 'TRUSTEDPATHS' -Value $newVal -PropertyType String -Force | Out-Null
|
||||
$shortName = $t -replace '.*\\Profiles\\','Profile\' -replace '\\Variables',''
|
||||
Write-Host " 已添加: $shortName" -ForegroundColor Green
|
||||
$addedCount++
|
||||
} catch {
|
||||
Write-Host " 失败: $t - $($_.Exception.Message)" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
if ($addedCount -eq 0 -and $targets.Count -gt 0) {
|
||||
Write-Host " 所有配置已包含本目录,无需修改" -ForegroundColor DarkGray
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# --- 4. 创建 acaddoc.lsp 自动加载(核心机制) ---
|
||||
Write-Host "[4/4] 创建 acaddoc.lsp 自动加载脚本..." -ForegroundColor Green
|
||||
|
||||
# 确定 Support 目录(优先用户漫游目录,其次 AutoCAD 安装目录)
|
||||
# 漫游目录路径模式:%APPDATA%\Autodesk\AutoCAD 20XX\Rxx.0\chs\Support
|
||||
$acadDirName = Split-Path $target.Location -Leaf # 如 "AutoCAD 2018"
|
||||
$roamingBase = Join-Path $env:APPDATA "Autodesk\$acadDirName\$($target.Version)\chs\Support"
|
||||
$installSupport = Join-Path $target.Location "Support"
|
||||
|
||||
$candidateDirs = @($roamingBase, $installSupport) | Select-Object -Unique
|
||||
$supDir = $null
|
||||
foreach ($d in $candidateDirs) {
|
||||
if (Test-Path $d) { $supDir = $d; break }
|
||||
}
|
||||
if (-not $supDir) {
|
||||
# 创建漫游目录
|
||||
$supDir = $roamingBase
|
||||
New-Item -ItemType Directory -Path $supDir -Force | Out-Null
|
||||
Write-Host " 创建目录: $supDir" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
$lspPath = Join-Path $supDir 'acaddoc.lsp'
|
||||
|
||||
# LISP 代码(路径用双反斜杠转义)
|
||||
$dllPathEscaped = $dllPath -replace '\\','\\\\'
|
||||
$lispCode = @"
|
||||
;;; CADCJ 自动加载 - 由安装脚本生成
|
||||
;;; 每会话首次打开文档时通过 NETLOAD 加载成套报价插件
|
||||
;;; 使用内存全局变量,进程退出后自动清除,避免持久化导致跳过加载
|
||||
(vl-load-com)
|
||||
(if (not *cadcj-loaded*)
|
||||
(progn
|
||||
(setq *cadcj-loaded* t)
|
||||
(setq #cadcj-fd (getvar "FILEDIA"))
|
||||
(setvar "FILEDIA" 0)
|
||||
(vl-catch-all-apply
|
||||
'(lambda ()
|
||||
(command "NETLOAD" "$dllPathEscaped")
|
||||
)
|
||||
)
|
||||
(setvar "FILEDIA" #cadcj-fd)
|
||||
(vl-propagate '*cadcj-loaded*)
|
||||
)
|
||||
)
|
||||
(princ)
|
||||
"@
|
||||
|
||||
# 检查是否已有 acaddoc.lsp(避免覆盖用户现有内容)
|
||||
$needMerge = $false
|
||||
if (Test-Path $lspPath) {
|
||||
$existing = Get-Content $lspPath -Raw -ErrorAction SilentlyContinue
|
||||
if ($existing -and ($existing -match 'CADCJ|cadcj')) {
|
||||
Write-Host " acaddoc.lsp 已包含 CADCJ 加载逻辑,跳过" -ForegroundColor DarkGray
|
||||
} elseif ($existing) {
|
||||
$needMerge = $true
|
||||
Write-Host " 检测到现有 acaddoc.lsp,将追加 CADCJ 加载逻辑" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if ($needMerge) {
|
||||
# 追加到现有文件末尾
|
||||
$existing + "`r`n" + $lispCode | Out-File -FilePath $lspPath -Encoding Default -Append -Force
|
||||
} else {
|
||||
# GB2312 编码写入(AutoCAD 正确读取中文路径)
|
||||
[System.IO.File]::WriteAllText($lspPath, $lispCode, [System.Text.Encoding]::GetEncoding('GB2312'))
|
||||
}
|
||||
Write-Host " 已创建: $lspPath" -ForegroundColor Green
|
||||
} catch {
|
||||
# AppData 写入失败时提权
|
||||
Write-Host " 写入失败,尝试提权: $($_.Exception.Message)" -ForegroundColor Yellow
|
||||
$tmpLsp = Join-Path $env:TEMP 'cadcj_acaddoc.lsp'
|
||||
[System.IO.File]::WriteAllText($tmpLsp, $lispCode, [System.Text.Encoding]::GetEncoding('GB2312'))
|
||||
$copyCmd = "Copy-Item '$tmpLsp' '$lspPath' -Force"
|
||||
$p = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile","-Command",$copyCmd -Verb RunAs -Wait -PassThru
|
||||
if ($p.ExitCode -eq 0) {
|
||||
Write-Host " 已创建(提权): $lspPath" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " [错误] 无法创建 acaddoc.lsp" -ForegroundColor Red
|
||||
}
|
||||
Remove-Item $tmpLsp -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# --- 5. 同步更新 .reg 文件(便于记录) ---
|
||||
$regFile = Join-Path $scriptDir '注册CAD自动加载.reg'
|
||||
$regKeyHklm = $appsPath -replace '^HKLM:\\','HKEY_LOCAL_MACHINE\'
|
||||
$regContent = @"
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[$regKeyHklm]
|
||||
"DESCRIPTION"="成套报价插件"
|
||||
"LOADCTRLS"=dword:00000002
|
||||
"MANAGED"=dword:00000001
|
||||
"MODULE"="$($dllPath -replace '\\','\\')"
|
||||
"@
|
||||
try {
|
||||
$regContent | Out-File -FilePath $regFile -Encoding Default -Force
|
||||
Write-Host " 已更新: $regFile" -ForegroundColor DarkGray
|
||||
} catch {}
|
||||
Write-Host ""
|
||||
|
||||
# --- 完成 ---
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host " 安装完成!" -ForegroundColor Green
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "安装摘要:" -ForegroundColor White
|
||||
Write-Host " AutoCAD: $($target.Product) ($($target.Version))" -ForegroundColor White
|
||||
Write-Host " 插件DLL: $dllPath" -ForegroundColor White
|
||||
Write-Host " LSP文件: $lspPath" -ForegroundColor White
|
||||
Write-Host " 加载方式: acaddoc.lsp (NETLOAD)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "现在启动 AutoCAD 并打开任意图纸,插件会自动加载。" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
if (-not $env:CADCJ_SILENT) { Read-Host "按回车退出" }
|
||||
@@ -0,0 +1,71 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
REM ============================================
|
||||
REM 成套报价 CAD 插件 - 就地安装脚本
|
||||
REM 功能:把【本脚本所在目录】的 cadcj.dll 注册为 CAD 自动加载
|
||||
REM 原则:在哪个目录运行,就安装哪个目录(测试目录/安装目录通用)
|
||||
REM 用法:双击运行(无需管理员权限,写的是用户级注册表 HKCU)
|
||||
REM 更新 DLL 后:先关 CAD → 替换本目录 cadcj.dll → 再运行本脚本不需要,
|
||||
REM 注册表已指向本目录,直接重启 CAD 即加载新版
|
||||
REM ============================================
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM 取脚本所在目录(去掉尾部反斜杠)
|
||||
set "INSTDIR=%~dp0"
|
||||
if "%INSTDIR:~-1%"=="\" set "INSTDIR=%INSTDIR:~0,-1%"
|
||||
set "DLLPATH=%INSTDIR%\cadcj.dll"
|
||||
|
||||
REM 检查本目录是否有 cadcj.dll
|
||||
if not exist "%DLLPATH%" (
|
||||
echo [错误] 本目录没有 cadcj.dll,无法安装。
|
||||
echo 当前目录: %INSTDIR%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ==============================================
|
||||
echo 成套报价 CAD 插件 - 就地安装
|
||||
echo 插件目录: %INSTDIR%
|
||||
echo ==============================================
|
||||
echo.
|
||||
|
||||
REM 检查 CAD 是否在运行(在运行也能写注册表,但新DLL要重启CAD才生效)
|
||||
tasklist /FI "IMAGENAME eq acad.exe" 2>nul | find /I "acad.exe" >nul
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo [提示] AutoCAD 正在运行:注册完成后请重启 CAD 才能生效。
|
||||
echo.
|
||||
)
|
||||
|
||||
REM 枚举所有已安装的 AutoCAD 版本(HKCU 用户级,无需管理员)
|
||||
REM ★ findstr "^HKEY_" 只取子键行,过滤掉 CurVer 这类值行
|
||||
set FOUND=0
|
||||
for /f "delims=" %%K in ('reg query "HKCU\SOFTWARE\Autodesk\AutoCAD" 2^>nul ^| findstr /I /C:"HKEY_"') do (
|
||||
for /f "delims=" %%P in ('reg query "%%K" 2^>nul ^| findstr /I /C:"HKEY_" ^| findstr /I "ACAD-"') do (
|
||||
set "APPSKEY=%%P\Apps\CADCJ"
|
||||
reg add "!APPSKEY!" /v LOADCTRLS /t REG_DWORD /d 2 /f >nul
|
||||
reg add "!APPSKEY!" /v MANAGED /t REG_DWORD /d 1 /f >nul
|
||||
reg add "!APPSKEY!" /v MODULE /t REG_SZ /d "%DLLPATH%" /f >nul
|
||||
reg add "!APPSKEY!" /v DESCRIPTION /t REG_SZ /d "ChengTao BaoJia Plugin" /f >nul
|
||||
echo [OK] 已注册: !APPSKEY!
|
||||
echo MODULE = %DLLPATH%
|
||||
set FOUND=1
|
||||
)
|
||||
)
|
||||
|
||||
if %FOUND% EQU 0 (
|
||||
echo [错误] 未检测到已安装的 AutoCAD(R22.0=2018 等)。
|
||||
echo 请确认 AutoCAD 已安装后重试。
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ==============================================
|
||||
echo 安装完成!
|
||||
echo CAD 自动加载: %DLLPATH%
|
||||
echo 用户库CSV : 本目录下最新的 用户库_*.csv
|
||||
echo 数据库sjk : 本目录 sjk\ 下
|
||||
echo 重启 AutoCAD 即生效。
|
||||
echo ==============================================
|
||||
pause
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace MyApp
|
||||
{
|
||||
/// <summary>
|
||||
/// 新建项目 窗口(简化版,不依赖企业信息)。
|
||||
/// UI 布局见 新建项目.ui.cs,本文件只含交互逻辑。
|
||||
/// </summary>
|
||||
public partial class 新建项目 : Form
|
||||
{
|
||||
// 价格格式化选项(与主项目 价格格式化.cs 保持一致)
|
||||
private static readonly string[] 小数位选项 =
|
||||
{
|
||||
"保留整数", "保留1位小数", "保留2位小数", "保留3位小数",
|
||||
"保留4位小数", "保留5位小数", "保留6位小数"
|
||||
};
|
||||
private static readonly string[] 取整方式选项 =
|
||||
{
|
||||
"不取整", "四舍五入", "四舍五入到角", "四舍五入到元",
|
||||
"四舍五入到十元", "向上取整到元", "最近5元", "最近10元"
|
||||
};
|
||||
|
||||
private readonly string _dbPath;
|
||||
private readonly string _tableName;
|
||||
|
||||
public 新建项目() : this(null, null) { }
|
||||
|
||||
/// <summary>带数据库路径的构造函数(用于自动生成项目编号)。</summary>
|
||||
public 新建项目(string dbPath, string tableName)
|
||||
{
|
||||
InitializeComponent();
|
||||
_dbPath = dbPath;
|
||||
_tableName = tableName;
|
||||
InitPriceFormatCombos();
|
||||
this.Load += 新建项目_Load;
|
||||
}
|
||||
|
||||
private void InitPriceFormatCombos()
|
||||
{
|
||||
cmb单台价格.Items.Clear();
|
||||
cmb单台价格.Items.AddRange(小数位选项);
|
||||
cmb项目总价.Items.Clear();
|
||||
cmb项目总价.Items.AddRange(小数位选项);
|
||||
comboBox1.Items.Clear();
|
||||
comboBox1.Items.AddRange(取整方式选项);
|
||||
comboBox2.Items.Clear();
|
||||
comboBox2.Items.AddRange(取整方式选项);
|
||||
}
|
||||
|
||||
private void 新建项目_Load(object sender, EventArgs e)
|
||||
{
|
||||
FillAutoFields();
|
||||
}
|
||||
|
||||
/// <summary>自动填充项目日期(当天)和项目编号(yyyyMMdd+3位序号)。</summary>
|
||||
private void FillAutoFields()
|
||||
{
|
||||
DateTime today = DateTime.Today;
|
||||
txt项目日期.Text = today.ToString("yyyy-MM-dd");
|
||||
|
||||
string datePart = today.ToString("yyyyMMdd");
|
||||
int seq = GetNextSeqForDate(datePart);
|
||||
txt项目编号.Text = datePart + seq.ToString("000");
|
||||
txt项目编号.ReadOnly = true;
|
||||
txt项目编号.TabStop = false;
|
||||
txt项目编号.BackColor = System.Drawing.SystemColors.Control;
|
||||
}
|
||||
|
||||
/// <summary>查数据库当天最大序号+1,失败返回1。</summary>
|
||||
private int GetNextSeqForDate(string datePart)
|
||||
{
|
||||
if (string.IsNullOrEmpty(datePart) ||
|
||||
string.IsNullOrEmpty(_dbPath) ||
|
||||
string.IsNullOrEmpty(_tableName) ||
|
||||
!System.IO.File.Exists(_dbPath))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
try
|
||||
{
|
||||
string connStr = "Data Source=" + _dbPath + ";Version=3;";
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection(connStr))
|
||||
{
|
||||
conn.Open();
|
||||
string sql =
|
||||
"SELECT MAX(CAST(SUBSTR(项目编号, 9) AS INTEGER)) " +
|
||||
"FROM " + _tableName + " " +
|
||||
"WHERE SUBSTR(项目编号, 1, 8) = '" + datePart + "';";
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn))
|
||||
{
|
||||
object result = cmd.ExecuteScalar();
|
||||
if (result == null || result == DBNull.Value) return 1;
|
||||
int maxSeq;
|
||||
if (int.TryParse(result.ToString(), out maxSeq)) return maxSeq + 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { return 1; }
|
||||
}
|
||||
|
||||
/// <summary>收集对话框字段,以列名为键返回(供 Form3 写入数据库)。</summary>
|
||||
public Dictionary<string, string> GetInputValues()
|
||||
{
|
||||
var values = new Dictionary<string, string>();
|
||||
AddIfNotEmpty(values, "项目编号", txt项目编号.Text);
|
||||
AddIfNotEmpty(values, "项目日期", txt项目日期.Text);
|
||||
AddIfNotEmpty(values, "项目名称", txt项目名称.Text);
|
||||
AddIfNotEmpty(values, "客户名称", txt客户名称.Text);
|
||||
AddIfNotEmpty(values, "客户地址", txt客户地址.Text);
|
||||
AddIfNotEmpty(values, "联系人", txt联系人.Text);
|
||||
AddIfNotEmpty(values, "联系电话", txt联系电话.Text);
|
||||
AddIfNotEmpty(values, "备注", txt备注.Text);
|
||||
AddIfNotEmpty(values, "成套厂名称", txt成套厂名称.Text);
|
||||
AddIfNotEmpty(values, "成套厂地址", txt成套厂地址.Text);
|
||||
AddIfNotEmpty(values, "成套厂联系人", txt成套厂联系人.Text);
|
||||
AddIfNotEmpty(values, "成套厂联系电话", txt成套厂联系电话.Text);
|
||||
values["单台小数位"] = (cmb单台价格.SelectedItem ?? cmb单台价格.Text ?? "").ToString();
|
||||
values["项目小数位"] = (cmb项目总价.SelectedItem ?? cmb项目总价.Text ?? "").ToString();
|
||||
values["单台取整方式"] = (comboBox1.SelectedItem ?? comboBox1.Text ?? "").ToString();
|
||||
values["项目取整方式"] = (comboBox2.SelectedItem ?? comboBox2.Text ?? "").ToString();
|
||||
return values;
|
||||
}
|
||||
|
||||
private static void AddIfNotEmpty(Dictionary<string, string> dict, string key, string value)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value)) dict[key] = value.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_LOCAL_MACHINE\Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R22.0\ACAD-1001:804\Apps\CADCJ]
|
||||
"DESCRIPTION"="成套报价插件"
|
||||
"LOADCTRLS"=dword:00000002
|
||||
"MANAGED"=dword:00000001
|
||||
"MODULE"="C:\\Users\\099978\\Documents\\SharpDevelop Projects\\成套报价软件\\成套报价软件\\cadcj\\cadcj.dll"
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user