Initial working version
This commit is contained in:
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Build results
|
||||
|
||||
bin/
|
||||
obj/
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Rider
|
||||
.idea/
|
||||
|
||||
# VS Code
|
||||
.code/
|
||||
|
||||
# Rhino plugin output (should be .rhp, not tracked)
|
||||
*.rhp
|
||||
*.dll
|
||||
|
||||
# OS
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
27
CHANGELOG.md
Normal file
27
CHANGELOG.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# 更新日志
|
||||
|
||||
所有重要版本更新会记录在此文件。
|
||||
|
||||
## [0.1.0] - 2026-05-01
|
||||
|
||||
### 新增
|
||||
- 项目骨架创建(插件主类、命令入口)
|
||||
- XML 配置文件解析器
|
||||
- 动态表单 UI:根据 XML 生成 TextBox / ComboBox 控件
|
||||
- 支持 string 类型字段(TextBox)
|
||||
- 支持 list 类型字段(ComboBox 下拉选择)
|
||||
- 多 XML 模板支持:可同时加载多个 XML 文件
|
||||
- 四种导入合并模式:追加、替代、合并更新、智能合并
|
||||
- `XmlImportDialog`:导入时弹出模式选择对话框
|
||||
- 模板持久化:Rhino 重启后自动恢复已加载的模板
|
||||
- `TemplateManager`:管理模板生命周期和 index 文件
|
||||
- 默认 XML 模板内嵌在 DLL 中,首次启动自动提取
|
||||
- 命令名:`UserstringProperty`
|
||||
|
||||
### 功能
|
||||
- 选择 Block 后弹出属性编辑窗口
|
||||
- 属性保存到 Rhino Block 的 UserString(键值对存储)
|
||||
- 分隔标题自动识别(属性名含 `---` 时显示为分组标题)
|
||||
|
||||
### 文档
|
||||
- README、LICENSE、.gitignore、CONTRIBUTING、CHANGELOG
|
||||
17
CLAUDE.md
Normal file
17
CLAUDE.md
Normal file
@@ -0,0 +1,17 @@
|
||||
> 本文件仅保留高优先级、易混淆、跨会话必须一致的约束。
|
||||
> 本文件必须极精简,占用大模型注意力视为失效。
|
||||
|
||||
## 项目一句话
|
||||
|
||||
Rhino 8 C# 插件(.rhp),管理 Block 的 UserString 属性,支持 XML 模板导入。
|
||||
|
||||
## 自动化约定
|
||||
|
||||
- 禁止自行 commit / push git,用户下达指令再执行
|
||||
- 完成任务后清理临时文件
|
||||
|
||||
## 文档入口
|
||||
|
||||
- PLAN.md:待办事项
|
||||
- CHANGELOG.md:版本记录
|
||||
- CLAUDE.md:仅包含本文件(不要混入项目说明)
|
||||
31
CONTRIBUTING.md
Normal file
31
CONTRIBUTING.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 贡献指南
|
||||
|
||||
欢迎提交 Issue 和 Pull Request!
|
||||
|
||||
## 如何贡献
|
||||
|
||||
### 报告问题
|
||||
|
||||
发现 Bug 或有新功能想法?请先搜索 [Issues](https://gitea.com/user/project/issues) 确保不是重复提交,然后按以下格式描述:
|
||||
|
||||
- **问题描述**:清晰说明问题或建议
|
||||
- **复现步骤**:如果是 Bug,列出具体步骤
|
||||
- **环境信息**:Rhino 版本、操作系统、.NET SDK 版本
|
||||
|
||||
### 提交代码
|
||||
|
||||
1. Fork 本项目
|
||||
2. 创建分支 (`git checkout -b feature/your-feature`)
|
||||
3. 确保代码符合项目风格,编译通过
|
||||
4. 提交时请写清楚改动内容和原因
|
||||
5. Push 到你的 Fork,发起 Pull Request
|
||||
|
||||
### 代码规范
|
||||
|
||||
- C# 代码遵循 [C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)
|
||||
- 公共方法需有 XML 注释
|
||||
- 新功能请附带测试或更新文档
|
||||
|
||||
## 问题解答
|
||||
|
||||
如有问题可以在 [Discussions](https://gitea.com/user/project/discussions) 中讨论。
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
123
README.md
Normal file
123
README.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# UserStringProperty
|
||||
|
||||
Rhino 插件,根据用户自定义 XML 配置,给 Block 实例附加属性(UserString)。
|
||||
|
||||
## 功能
|
||||
|
||||
- 根据 XML 配置文件动态生成属性编辑界面
|
||||
- 支持 `string` 类型字段(TextBox)和 `list` 类型字段(ComboBox)
|
||||
- 自动保存到 Rhino Block 的 UserString
|
||||
- 支持多个 XML 模板同时加载、合并
|
||||
- 模板自动持久化,Rhino 重启后自动恢复
|
||||
- 导入时检测属性集冲突,覆盖或新增
|
||||
|
||||
## 安装
|
||||
|
||||
### 编译
|
||||
|
||||
```bash
|
||||
cd UserStringProperty
|
||||
dotnet build
|
||||
```
|
||||
|
||||
输出:`UserStringProperty/bin/UserStringProperty.dll`
|
||||
|
||||
### 加载到 Rhino
|
||||
|
||||
1. 将 `bin/UserStringProperty.dll` 重命名为 `UserStringProperty.rhp`
|
||||
2. Rhino → `Options` → `Plug-ins` → `Install` → 选择 `.rhp` 文件
|
||||
|
||||
### 使用
|
||||
|
||||
1. 在 Rhino 中输入命令:`UserstringProperty`
|
||||
2. 选择要编辑的 Block 实例
|
||||
3. 默认模板首次启动自动加载;点 **加载XML** 导入自定义模板
|
||||
4. 在弹出的对话框中选择 cmig 和属性集,若同名已存在则需确认覆盖
|
||||
5. 在下拉框中选择属性集,编辑各字段值
|
||||
6. 点击 **写入属性**
|
||||
|
||||
## 模板导入
|
||||
|
||||
加载 XML 时,会弹出对话框让选择 cmig 和属性集,检测同名冲突:
|
||||
|
||||
- **同名属性集不存在** → 直接保存为新模板
|
||||
- **同名属性集已存在** → 需确认是否覆盖
|
||||
|
||||
## XML 配置格式
|
||||
|
||||
插件根据 XML 动态生成 UI。示例 XML 格式如下:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<cmig name="构件属性">
|
||||
<propertyset name="构件属性-基本" exclusive="false">
|
||||
<!-- string 类型 → TextBox -->
|
||||
<property name="0-00-02-年代" type="string" default="PRC"/>
|
||||
|
||||
<!-- list 类型 → ComboBox -->
|
||||
<property name="0-00-06-构件类型" type="list" default="DS-JG">
|
||||
<option>DS-JG</option>
|
||||
<option>DS-WH</option>
|
||||
<option>DS-SB</option>
|
||||
</property>
|
||||
|
||||
<!-- 分隔标题(default 值为一串 dash 且 type="string") -->
|
||||
<property name="0-01-00-构件基本属性" type="string" default="--------------------" />
|
||||
</propertyset>
|
||||
</cmig>
|
||||
```
|
||||
|
||||
### 字段类型
|
||||
|
||||
| type | 控件 | 说明 |
|
||||
|------|------|------|
|
||||
| `string` | TextBox | 自由文本输入 |
|
||||
| `list` | ComboBox | 下拉选项(通过 `<option>` 定义) |
|
||||
|
||||
### 分隔标题
|
||||
|
||||
属性名的 `default` 值以 `---` 开头时(即 `default="--------------------"`),该属性显示为分组标题栏。
|
||||
|
||||
## 模板存储
|
||||
|
||||
已加载的 XML 模板会复制一份保存到插件目录,重启 Rhino 后自动恢复:
|
||||
|
||||
```
|
||||
%APPDATA%\McNeel\Rhinoceros\8.0\plugins\UserStringProperty\
|
||||
├── template_index.txt # 记录已加载的 XML 路径
|
||||
└── *.xml # 模板副本
|
||||
```
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
UserStringProperty/
|
||||
├── CLAUDE.md # agent 指示
|
||||
├── PLAN.md # 待办事项与开发计划
|
||||
├── UserStringProperty.csproj
|
||||
├── UserStringProperty.cs # 插件主类
|
||||
├── Core/
|
||||
│ ├── XmlPropertyConfig.cs # XML 解析器
|
||||
│ └── TemplateManager.cs # 模板管理与持久化
|
||||
├── Commands/
|
||||
│ └── CmdUserstringProperty.cs # 命令入口
|
||||
├── UI/
|
||||
│ ├── MainDialog.cs # 主窗口
|
||||
│ └── XmlImportDialog.cs # 导入模式选择对话框
|
||||
└── Resources/
|
||||
└── EmbeddedTemplates.cs # 内嵌默认 XML 模板
|
||||
```
|
||||
|
||||
## 编译环境
|
||||
|
||||
- **.NET SDK 8.0**
|
||||
- **Rhino 8**(包含 RhinoCommon.dll SDK 引用)
|
||||
- Windows 10/11
|
||||
|
||||
## 运行时要求
|
||||
|
||||
- Rhino 8
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
43
UserStringProperty/Commands/CmdUserstringProperty.cs
Normal file
43
UserStringProperty/Commands/CmdUserstringProperty.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Rhino;
|
||||
using Rhino.Commands;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Input;
|
||||
|
||||
namespace UserStringProperty.Commands
|
||||
{
|
||||
public class CmdUserstringProperty : Command
|
||||
{
|
||||
public CmdUserstringProperty()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public static CmdUserstringProperty Instance { get; private set; }
|
||||
|
||||
public override string EnglishName => "UserstringProperty";
|
||||
|
||||
protected override Result RunCommand(RhinoDoc doc, RunMode mode)
|
||||
{
|
||||
var result = RhinoGet.GetOneObject(
|
||||
"选择 Block 实例",
|
||||
false,
|
||||
ObjectType.InstanceReference,
|
||||
out var objref);
|
||||
|
||||
if (result != Result.Success)
|
||||
return result;
|
||||
|
||||
var instanceObject = objref.Object() as InstanceObject;
|
||||
if (instanceObject == null)
|
||||
{
|
||||
RhinoApp.WriteLine("所选对象不是 Block 实例");
|
||||
return Result.Failure;
|
||||
}
|
||||
|
||||
var dialog = new UI.MainDialog(instanceObject);
|
||||
dialog.ShowDialog();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
330
UserStringProperty/Core/TemplateManager.cs
Normal file
330
UserStringProperty/Core/TemplateManager.cs
Normal file
@@ -0,0 +1,330 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Rhino;
|
||||
using UserStringProperty.Core;
|
||||
using UserStringProperty.Resources;
|
||||
using UserStringProperty.UI;
|
||||
|
||||
namespace UserStringProperty
|
||||
{
|
||||
public class TemplateManager
|
||||
{
|
||||
private static TemplateManager _instance;
|
||||
|
||||
// 当前加载的所有模板(合并后)
|
||||
private List<XmlPropertyConfig> _configs = new List<XmlPropertyConfig>();
|
||||
private List<string> _loadedPaths = new List<string>();
|
||||
|
||||
// 插件目录(存储模板副本)
|
||||
private string _pluginFolder;
|
||||
|
||||
public static TemplateManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = new TemplateManager();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> LoadedPaths => _loadedPaths;
|
||||
public bool IsLoaded => _configs.Count > 0 && _configs.Any(c => c.PropertySets.Count > 0);
|
||||
|
||||
public TemplateManager()
|
||||
{
|
||||
_pluginFolder = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"McNeel", "Rhinoceros", "8.0", "Plug-ins",
|
||||
"UserStringProperty (C286E052-06F6-475F-A882-1C1AA31744CF)"
|
||||
);
|
||||
if (!Directory.Exists(_pluginFolder))
|
||||
Directory.CreateDirectory(_pluginFolder);
|
||||
}
|
||||
|
||||
// 获取插件目录下的模板副本文件列表
|
||||
public List<string> GetLocalTemplateFiles()
|
||||
{
|
||||
return Directory.GetFiles(_pluginFolder, "*.xml")
|
||||
.OrderBy(f => File.GetCreationTime(f))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// 用户选择 XML 文件后的预处理——返回导入对话框让用户选择模式
|
||||
public void PrepareImport(string filePath, List<PropertySet> existingSets, out XmlPropertyConfig incomingConfig)
|
||||
{
|
||||
incomingConfig = XmlPropertyConfig.Load(filePath);
|
||||
}
|
||||
|
||||
// 执行导入
|
||||
public void Import(string filePath, string cmigName, string propertysetName)
|
||||
{
|
||||
RebuildIndexIfMissing();
|
||||
|
||||
var config = XmlPropertyConfig.Load(filePath);
|
||||
|
||||
// 检查文件是否已存在
|
||||
var fileName = config.GetStorageFileName(config.PropertySets.FirstOrDefault(p => p.Name == propertysetName) ?? config.PropertySets.First());
|
||||
var destPath = Path.Combine(_pluginFolder, fileName);
|
||||
|
||||
if (File.Exists(destPath))
|
||||
{
|
||||
// 文件已存在,询问是否替换(对话框已在外面处理,这里直接覆盖)
|
||||
}
|
||||
|
||||
// 提取指定的 propertset 并保存
|
||||
var ps = config.PropertySets.FirstOrDefault(p => p.Name == propertysetName);
|
||||
if (ps == null) return;
|
||||
|
||||
// 保存文件
|
||||
var xmlContent = ExtractPropertySetXml(filePath, propertysetName);
|
||||
if (!string.IsNullOrEmpty(xmlContent))
|
||||
{
|
||||
File.WriteAllText(destPath, xmlContent);
|
||||
_loadedPaths.Add(destPath);
|
||||
|
||||
// 将新导入的 config 加入内存列表
|
||||
var newConfig = XmlPropertyConfig.Load(destPath);
|
||||
if (newConfig.PropertySets.Count > 0)
|
||||
{
|
||||
_configs.Add(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
SaveIndex();
|
||||
}
|
||||
|
||||
public void LoadFromFile(string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
throw new FileNotFoundException($"XML 文件不存在: {filePath}");
|
||||
|
||||
if (_loadedPaths.Contains(filePath))
|
||||
return;
|
||||
|
||||
var config = XmlPropertyConfig.Load(filePath);
|
||||
_configs.Add(config);
|
||||
_loadedPaths.Add(filePath);
|
||||
SaveIndex();
|
||||
}
|
||||
|
||||
public void LoadDefault()
|
||||
{
|
||||
_configs.Clear();
|
||||
_loadedPaths.Clear();
|
||||
|
||||
// 确保插件目录存在
|
||||
if (!Directory.Exists(_pluginFolder))
|
||||
Directory.CreateDirectory(_pluginFolder);
|
||||
|
||||
// 如果 index 不存在,先提取默认模板
|
||||
var indexPath = GetIndexFilePath();
|
||||
if (!File.Exists(indexPath))
|
||||
{
|
||||
EmbeddedTemplates.ExtractToFolder(_pluginFolder);
|
||||
}
|
||||
|
||||
// 加载 index 中记录的路径(这些路径指向插件目录副本)
|
||||
if (File.Exists(indexPath))
|
||||
{
|
||||
var lines = File.ReadAllLines(indexPath);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var trimmed = line.Trim();
|
||||
if (!string.IsNullOrEmpty(trimmed) && File.Exists(trimmed))
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = XmlPropertyConfig.Load(trimmed);
|
||||
if (config.PropertySets.Count > 0)
|
||||
{
|
||||
_configs.Add(config);
|
||||
_loadedPaths.Add(trimmed);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 扫描插件目录下所有 XML,检查是否有未加载的文件(防止有模板被复制进去但不在 index)
|
||||
var existingFiles = Directory.GetFiles(_pluginFolder, "*.xml");
|
||||
foreach (var file in existingFiles)
|
||||
{
|
||||
if (_loadedPaths.Contains(file))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
var config = XmlPropertyConfig.Load(file);
|
||||
if (config.PropertySets.Count > 0)
|
||||
{
|
||||
_configs.Add(config);
|
||||
_loadedPaths.Add(file);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// 如果没有任何模板,重新提取并重试
|
||||
if (_configs.Count == 0)
|
||||
{
|
||||
EmbeddedTemplates.ExtractToFolder(_pluginFolder);
|
||||
foreach (var file in Directory.GetFiles(_pluginFolder, "*.xml"))
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = XmlPropertyConfig.Load(file);
|
||||
if (config.PropertySets.Count > 0)
|
||||
{
|
||||
_configs.Add(config);
|
||||
_loadedPaths.Add(file);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// 保存 index(确保 index 与实际加载状态一致)
|
||||
SaveIndex();
|
||||
}
|
||||
|
||||
private string GetIndexFilePath()
|
||||
{
|
||||
return Path.Combine(_pluginFolder, "template_index.txt");
|
||||
}
|
||||
|
||||
private void SaveIndex()
|
||||
{
|
||||
File.WriteAllLines(GetIndexFilePath(), _loadedPaths);
|
||||
}
|
||||
|
||||
private void RebuildIndexIfMissing()
|
||||
{
|
||||
var indexPath = GetIndexFilePath();
|
||||
if (!File.Exists(indexPath))
|
||||
{
|
||||
var files = GetLocalTemplateFiles();
|
||||
_loadedPaths.Clear();
|
||||
foreach (var file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = XmlPropertyConfig.Load(file);
|
||||
if (config.PropertySets.Count > 0)
|
||||
{
|
||||
_configs.Add(config);
|
||||
_loadedPaths.Add(file);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
SaveIndex();
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveTemplatesToPluginFolder()
|
||||
{
|
||||
// 按 [cmig name]-[propertyset name].xml 格式保存
|
||||
foreach (var config in _configs)
|
||||
{
|
||||
foreach (var ps in config.PropertySets)
|
||||
{
|
||||
var fileName = config.GetStorageFileName(ps);
|
||||
var destPath = Path.Combine(_pluginFolder, fileName);
|
||||
|
||||
// 如果 config 有原始内容,使用原始内容;否则从 config 重新生成 XML
|
||||
if (!string.IsNullOrEmpty(config.SourcePath) && File.Exists(config.SourcePath))
|
||||
{
|
||||
// 从原始文件提取对应的 propertset 部分来保存
|
||||
var xmlContent = ExtractPropertySetXml(config.SourcePath, ps.Name);
|
||||
if (!string.IsNullOrEmpty(xmlContent))
|
||||
File.WriteAllText(destPath, xmlContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从原始 XML 中提取指定 propertset 的完整 XML
|
||||
private string ExtractPropertySetXml(string sourcePath, string propertysetName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = XDocument.Load(sourcePath);
|
||||
var cmig = doc.Root;
|
||||
if (cmig == null) return null;
|
||||
|
||||
var psElem = cmig.Elements("propertyset")
|
||||
.FirstOrDefault(p => p.Attribute("name")?.Value == propertysetName);
|
||||
|
||||
if (psElem == null) return null;
|
||||
|
||||
// 重新构建只包含这个 propertset 的 XML
|
||||
var newDoc = new XDocument(new XDeclaration("1.0", "utf-8", null));
|
||||
var newCmig = new XElement(cmig.Name, cmig.Attributes());
|
||||
newCmig.Add(new XElement(psElem));
|
||||
newDoc.Add(newCmig);
|
||||
|
||||
return newDoc.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_configs.Clear();
|
||||
_loadedPaths.Clear();
|
||||
var indexPath = GetIndexFilePath();
|
||||
if (File.Exists(indexPath))
|
||||
File.Delete(indexPath);
|
||||
}
|
||||
|
||||
public List<PropertySet> GetAllPropertySets()
|
||||
{
|
||||
var result = new List<PropertySet>();
|
||||
foreach (var config in _configs)
|
||||
{
|
||||
foreach (var ps in config.PropertySets)
|
||||
{
|
||||
result.Add(ps);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<string> GetCmigNames()
|
||||
{
|
||||
return _configs.Select(c => c.CmigName).Distinct().ToList();
|
||||
}
|
||||
|
||||
public List<string> GetPropertySetNames(string cmigName)
|
||||
{
|
||||
var config = _configs.FirstOrDefault(c => c.CmigName == cmigName);
|
||||
if (config == null) return new List<string>();
|
||||
return config.PropertySets.Select(ps => ps.Name).ToList();
|
||||
}
|
||||
|
||||
public PropertySet GetPropertySet(string cmigName, string psName)
|
||||
{
|
||||
var config = _configs.FirstOrDefault(c => c.CmigName == cmigName);
|
||||
if (config == null) return null;
|
||||
return config.PropertySets.FirstOrDefault(ps => ps.Name == psName);
|
||||
}
|
||||
|
||||
public List<string> GetPropertySetNames()
|
||||
{
|
||||
return GetAllPropertySets().Select(ps => ps.Name).ToList();
|
||||
}
|
||||
|
||||
public string GetPluginFolderPath()
|
||||
{
|
||||
return _pluginFolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
124
UserStringProperty/Core/XmlPropertyConfig.cs
Normal file
124
UserStringProperty/Core/XmlPropertyConfig.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace UserStringProperty.Core
|
||||
{
|
||||
public class PropertyDefinition
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; } // "string" or "list"
|
||||
public string DefaultValue { get; set; }
|
||||
public List<string> Options { get; set; } = new List<string>();
|
||||
public bool IsSeparator => DefaultValue != null && DefaultValue.Length >= 10 && DefaultValue.All(c => c == '-') && Type == "string";
|
||||
|
||||
public PropertyDefinition Clone()
|
||||
{
|
||||
return new PropertyDefinition
|
||||
{
|
||||
Name = this.Name,
|
||||
Type = this.Type,
|
||||
DefaultValue = this.DefaultValue,
|
||||
Options = new List<string>(this.Options)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class PropertySet
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Picture { get; set; }
|
||||
public bool Exclusive { get; set; }
|
||||
public List<PropertyDefinition> Properties { get; set; } = new List<PropertyDefinition>();
|
||||
|
||||
public PropertySet Clone()
|
||||
{
|
||||
var clone = new PropertySet
|
||||
{
|
||||
Name = this.Name,
|
||||
Picture = this.Picture,
|
||||
Exclusive = this.Exclusive,
|
||||
Properties = new List<PropertyDefinition>()
|
||||
};
|
||||
foreach (var p in this.Properties)
|
||||
clone.Properties.Add(p.Clone());
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
public class XmlPropertyConfig
|
||||
{
|
||||
public string CmigName { get; set; } // cmig 的 name,即容器名称
|
||||
public string Name { get; set; }
|
||||
public string SourcePath { get; set; }
|
||||
public List<PropertySet> PropertySets { get; set; } = new List<PropertySet>();
|
||||
|
||||
// 获取存储文件名:[cmig name]-[propertyset name].xml
|
||||
public string GetStorageFileName(PropertySet ps)
|
||||
{
|
||||
return $"{CmigName}-{ps.Name}.xml";
|
||||
}
|
||||
|
||||
public static XmlPropertyConfig Load(string filePath)
|
||||
{
|
||||
var config = new XmlPropertyConfig();
|
||||
|
||||
if (!System.IO.File.Exists(filePath))
|
||||
return config;
|
||||
|
||||
var doc = XDocument.Load(filePath);
|
||||
var cmig = doc.Root;
|
||||
if (cmig == null) return config;
|
||||
|
||||
config.CmigName = cmig.Attribute("name")?.Value ?? "未命名";
|
||||
config.Name = config.CmigName;
|
||||
config.SourcePath = filePath;
|
||||
|
||||
foreach (var psElem in cmig.Elements("propertyset"))
|
||||
{
|
||||
var ps = new PropertySet
|
||||
{
|
||||
Name = psElem.Attribute("name")?.Value ?? "",
|
||||
Picture = psElem.Attribute("picture")?.Value,
|
||||
Exclusive = psElem.Attribute("exclusive")?.Value == "true"
|
||||
};
|
||||
|
||||
foreach (var propElem in psElem.Elements("property"))
|
||||
{
|
||||
var prop = new PropertyDefinition
|
||||
{
|
||||
Name = propElem.Attribute("name")?.Value ?? "",
|
||||
Type = propElem.Attribute("type")?.Value ?? "string",
|
||||
DefaultValue = propElem.Attribute("default")?.Value
|
||||
};
|
||||
|
||||
foreach (var opt in propElem.Elements("option"))
|
||||
{
|
||||
prop.Options.Add(opt.Value);
|
||||
}
|
||||
|
||||
ps.Properties.Add(prop);
|
||||
}
|
||||
|
||||
config.PropertySets.Add(ps);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public XmlPropertyConfig Clone()
|
||||
{
|
||||
var clone = new XmlPropertyConfig
|
||||
{
|
||||
CmigName = this.CmigName,
|
||||
Name = this.Name,
|
||||
SourcePath = this.SourcePath,
|
||||
PropertySets = new List<PropertySet>()
|
||||
};
|
||||
foreach (var ps in this.PropertySets)
|
||||
clone.PropertySets.Add(ps.Clone());
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
224
UserStringProperty/Resources/EmbeddedTemplates.cs
Normal file
224
UserStringProperty/Resources/EmbeddedTemplates.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using System.IO;
|
||||
|
||||
namespace UserStringProperty.Resources
|
||||
{
|
||||
public static class EmbeddedTemplates
|
||||
{
|
||||
public const string BaseXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<cmig name=""构件属性"">
|
||||
|
||||
<propertyset name=""构件属性-基本"" exclusive=""false"" picture=""基础-任意实体.png"">
|
||||
<property name=""0-00-00-构件编码属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""0-00-01-星球"" type=""list"" default=""E"">
|
||||
<option>E</option><option>M</option><option>V</option><option>J</option><option>S</option><option>U</option><option>N</option><option>L</option>
|
||||
</property>
|
||||
<property name=""0-00-02-年代"" type=""string"" default=""PRC""/>
|
||||
<property name=""0-00-03-国家"" type=""string"" default=""156"" />
|
||||
<property name=""0-00-04-地区"" type=""string"" default=""130723"" />
|
||||
<property name=""0-00-05-项目楼栋"" type=""list"" default=""数据库自动生成后导入"" />
|
||||
<property name=""0-00-06-构件类型"" type=""list"" default=""DS-JG"">
|
||||
<option>DS-JG</option><option>DS-WH</option><option>DS-SB</option><option>DS-NZ</option><option>DS-HJ</option>
|
||||
<option>DX-FH</option><option>DX-JG</option><option>DX-WH</option><option>DX-SB</option><option>DX-NZ</option>
|
||||
</property>
|
||||
<property name=""0-00-07-楼层号"" type=""string"" default=""1""/>
|
||||
<property name=""0-00-08-标高信息"" type=""string"" default=""0.000""/>
|
||||
<property name=""0-00-09-轴号"" type=""string"" default=""1/A""/>
|
||||
<property name=""0-00-10-位置"" type=""string"" default=""H0V0""/>
|
||||
<property name=""0-00-11-坐标"" type=""string"" default=""0,0,0""/>
|
||||
<property name=""0-01-00-构件基本属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""0-01-01-构件id"" type=""string"" default=""数据库自动生成后导入""/>
|
||||
<property name=""0-01-02-构件名称"" type=""string"" default=""标准拱""/>
|
||||
<property name=""0-01-03-所属项目名称"" type=""string"" default=""SFC""/>
|
||||
<property name=""0-01-04-所属楼栋名称"" type=""string"" default=""SolarArk5.0""/>
|
||||
<property name=""0-01-05-孪生属性"" type=""list"" default=""模拟"">
|
||||
<option>模拟</option><option>实际</option>
|
||||
</property>
|
||||
<property name=""0-01-06-总体积"" type=""string"" default=""0.00""/>
|
||||
<property name=""0-01-07-总体积单位"" type=""string"" default=""m³""/>
|
||||
<property name=""0-01-08-材料(用,分隔)"" type=""string"" default=""钢,木""/>
|
||||
<property name=""0-01-09-各材料体积"" type=""string"" default=""0,0""/>
|
||||
<property name=""0-01-10-密度"" type=""string"" default=""0.00""/>
|
||||
<property name=""0-01-11-密度单位"" type=""string"" default=""kg/m³""/>
|
||||
<property name=""0-01-12-重量"" type=""string"" default=""0.00""/>
|
||||
<property name=""0-01-13-重量单位"" type=""string"" default=""kg""/>
|
||||
<property name=""0-01-14-表面积"" type=""string"" default=""0.00""/>
|
||||
<property name=""0-01-15-结构层体积"" type=""string"" default=""0.00""/>
|
||||
<property name=""0-02-00-性能属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""0-02-01-热阻"" type=""string"" default=""0.00""/>
|
||||
<property name=""0-02-02-热阻单位"" type=""string"" default=""m²·K/W""/>
|
||||
<property name=""0-02-03-热容"" type=""string"" default=""0.00""/>
|
||||
<property name=""0-02-04-热容单位"" type=""string"" default=""J/(kg·K)""/>
|
||||
<property name=""1-00-00-材料制备阶段构件属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""1-01-01-所属材料制备工序"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""1-01-02-材料制备序号"" type=""string"" default=""1"" />
|
||||
<property name=""1-01-03-材料制备类型"" type=""string"" default=""钢材加工"" />
|
||||
<property name=""1-01-04-材料制备用时(天)"" type=""string"" default=""15"" />
|
||||
<property name=""1-01-05-材料制备开始日期"" type=""string"" default=""20260515"" />
|
||||
<property name=""1-01-06-材料制备结束日期"" type=""string"" default=""20260530"" />
|
||||
<property name=""2-00-00-生产制造阶段构件属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""2-01-01-所属生产工序"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""2-01-02-生产序号"" type=""string"" default=""1"" />
|
||||
<property name=""2-01-03-并行工序组"" type=""string"" default=""1"" />
|
||||
<property name=""2-01-04-生产类型"" type=""string"" default=""焊接"" />
|
||||
<property name=""2-01-05-生产用时(天)"" type=""string"" default=""30"" />
|
||||
<property name=""2-01-06-生产开始日期"" type=""string"" default=""20260601"" />
|
||||
<property name=""2-01-07-生产结束日期"" type=""string"" default=""20260701"" />
|
||||
<property name=""3-00-00-物流运输阶段构件属性"" type=""string"" default=""--------------------""/>
|
||||
<property name=""3-01-01-所属运输工序"" type=""string"" default=""数据库自动生成后导入""/>
|
||||
<property name=""3-01-02-所属运输装备"" type=""string"" default=""苏A00000""/>
|
||||
<property name=""3-01-03-运输方式"" type=""list"" default=""轻型汽油货车运输"">
|
||||
<option>轻型汽油货车运输</option><option>中型汽油货车运输</option><option>重型汽油货车运输</option>
|
||||
</property>
|
||||
<property name=""3-01-04-运输用时(天)"" type=""string"" default=""3"" />
|
||||
<property name=""3-01-05-运输开始日期"" type=""string"" default=""20260701"" />
|
||||
<property name=""3-01-06-运输结束日期"" type=""string"" default=""20260705"" />
|
||||
<property name=""4-00-00-装配施工阶段构件属性"" type=""string"" default=""--------------------""/>
|
||||
<property name=""4-01-01-所属装配工序"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""4-01-02-装配序号"" type=""string"" default=""1"" />
|
||||
<property name=""4-01-03-并行工序组"" type=""string"" default=""1"" />
|
||||
<property name=""4-01-04-装配类型"" type=""string"" default=""吊装"" />
|
||||
<property name=""4-01-05-装配用时"" type=""string"" default=""0:00"" />
|
||||
<property name=""4-01-06-装配日期"" type=""string"" default=""20250710"" />
|
||||
<property name=""5-00-00-运营维护阶段构件属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""5-01-01-所属维护工序"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""6-00-00-改造再利用阶段构件属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""6-01-01-所属改造工序"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""7-00-00-拆除再利用阶段构件属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""7-01-01-所属拆除工序"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
</propertyset>
|
||||
|
||||
</cmig>";
|
||||
|
||||
public const string CarbonXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<cmig name=""构件碳排放属性"">
|
||||
|
||||
<propertyset name=""构件碳排放属性"" exclusive=""false"" picture=""碳排放.png"">
|
||||
<property name=""1-02-00-材料制备碳排放属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""1-02-01-构件材料制备碳排放因子"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""1-02-02-构件材料制备碳排放值"" type=""string"" default=""0.00"" />
|
||||
<property name=""1-02-03-构件材料制备碳排放值单位"" type=""string"" default=""kgCO2e"" />
|
||||
<property name=""2-02-00-生产制造碳排放属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""2-02-01-构件生产制造碳排放因子"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""2-02-02-构件生产制造碳排放值"" type=""string"" default=""0.00"" />
|
||||
<property name=""2-02-03-构件生产制造碳排放值单位"" type=""string"" default=""kgCO2e"" />
|
||||
<property name=""2-02-04-生产制造产生废料比例"" type=""string"" default=""10%"" />
|
||||
<property name=""2-02-05-生产制造废料碳排放因子"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""2-02-06-生产制造废料碳排放值"" type=""string"" default=""0.00"" />
|
||||
<property name=""3-02-00-物流运输碳排放属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""3-02-01-构件物流运输碳排放因子"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""3-02-02-构件物流运输碳排放值"" type=""string"" default=""0.00""/>
|
||||
<property name=""3-02-03-构件物流运输碳排放值单位"" type=""string"" default=""kgCO2e"" />
|
||||
<property name=""3-02-04-构件物流运输距离(km)"" type=""string"" default=""500""/>
|
||||
<property name=""4-02-00-装配施工碳排放属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""4-02-01-构件装配施工碳排放因子"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""4-02-02-构件装配施工碳排放值"" type=""string"" default=""0.00"" />
|
||||
<property name=""4-02-03-构件装配施工碳排放值单位"" type=""string"" default=""kgCO2e"" />
|
||||
<property name=""4-02-04-构件装配施工产生废料比例"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""4-02-05-构件装配施工废料碳排放因子"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""4-02-06-构件装配施工单位工人数量"" type=""string"" default=""1"" />
|
||||
</propertyset>
|
||||
|
||||
</cmig>";
|
||||
|
||||
public const string ContractXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<!--
|
||||
构件合同属性 XML
|
||||
插件化设计 - 可与基础属性组合使用
|
||||
前缀: 1-03-xx, 2-03-xx, 3-03-xx, 4-03-xx
|
||||
-->
|
||||
<cmig name=""构件合同属性"">
|
||||
|
||||
<propertyset name=""构件合同属性"" exclusive=""false"" picture=""合同.png"">
|
||||
<!-- 材料制备阶段合同属性 -->
|
||||
<property name=""1-03-00-材料制备合同属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""1-03-01-构件材料制备合同编码"" type=""string"" default="""" />
|
||||
<property name=""1-03-02-构件材料制备合作单位"" type=""string"" default=""筑晓龙商贸"" />
|
||||
<property name=""1-03-03-构件材料制备合同合作形式"" type=""list"" default=""采购"" >
|
||||
<option>赞助</option><option>采购</option><option>租赁</option><option>技术研发分包</option><option>劳务</option>
|
||||
</property>
|
||||
<property name=""1-03-04-构件材料制备质量"" type=""list"" default=""合格"">
|
||||
<option>合格</option><option>不合格</option>
|
||||
</property>
|
||||
<property name=""1-03-05-构件材料制备造价因子"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""1-03-06-构件材料制备造价"" type=""string"" default=""0.00"" />
|
||||
<property name=""1-03-07-构件材料制备造价单位"" type=""string"" default=""元"" />
|
||||
<property name=""1-03-08-构件材料制备存在高空作业"" type=""string"" default=""否"" />
|
||||
<property name=""1-03-09-构件材料制备高空作业率"" type=""string"" default=""0"" />
|
||||
<property name=""1-03-10-构件材料制备状态"" type=""list"" default=""未制备"">
|
||||
<option>未制备</option><option>制备中</option><option>制备完成</option><option>验收完成</option>
|
||||
</property>
|
||||
<property name=""1-03-11-构件材料制备进度%"" type=""string"" default=""0"" />
|
||||
|
||||
<!-- 生产制造阶段合同属性 -->
|
||||
<property name=""2-03-00-生产制造合同属性"" type=""string"" default=""--------------------"" />
|
||||
<property name=""2-03-01-构件生产制造合同编码"" type=""string"" default=""0"" />
|
||||
<property name=""2-03-02-构件生产制造合作单位"" type=""string"" default=""筑新技术集团"" />
|
||||
<property name=""2-03-03-构件生产制造合同合作形式"" type=""list"" default=""技术研发分包"" >
|
||||
<option>赞助</option><option>采购</option><option>租赁</option><option>技术研发分包</option><option>劳务</option>
|
||||
</property>
|
||||
<property name=""2-03-04-构件生产制造质量"" type=""list"" default=""合格"">
|
||||
<option>合格</option><option>不合格</option>
|
||||
</property>
|
||||
<property name=""2-03-05-构件生产制造造价因子"" type=""string"" default=""数据库自动生成后导入"" />
|
||||
<property name=""2-03-06-构件生产制造造价"" type=""string"" default=""0.00"" />
|
||||
<property name=""2-03-07-构件生产制造造价单位"" type=""string"" default=""元"" />
|
||||
<property name=""2-03-08-构件生产制造存在高空作业"" type=""string"" default=""是"" />
|
||||
<property name=""2-03-09-构件生产制造高空作业率"" type=""string"" default=""0"" />
|
||||
<property name=""2-03-10-构件生产制造状态"" type=""list"" default=""未生产"">
|
||||
<option>未生产</option><option>生产中</option><option>生产完成</option><option>验收完成</option>
|
||||
</property>
|
||||
<property name=""2-03-11-构件生产制造进度%"" type=""string"" default=""0"" />
|
||||
|
||||
<!-- 物流运输阶段合同属性 -->
|
||||
<property name=""3-03-00-物流运输合同属性"" type=""string"" default=""--------------------""/>
|
||||
<property name=""3-03-01-构件物流运输合同编码"" type=""string"" default=""0""/>
|
||||
<property name=""3-03-02-构件物流运输合作单位"" type=""string"" default=""南京瑞新供应链""/>
|
||||
<property name=""3-03-03-构件物流运输合同合作形式"" type=""list"" default=""采购"" >
|
||||
<option>赞助</option><option>采购</option><option>租赁</option><option>技术研发分包</option><option>劳务</option>
|
||||
</property>
|
||||
<property name=""3-03-04-构件物流运输质量"" type=""list"" default=""合格"">
|
||||
<option>合格</option><option>不合格</option>
|
||||
</property>
|
||||
<property name=""3-03-05-构件物流运输造价因子"" type=""string"" default=""数据库自动生成后导入""/>
|
||||
<property name=""3-03-06-构件物流运输造价"" type=""string"" default=""0.00""/>
|
||||
<property name=""3-03-07-构件物流运输造价单位"" type=""string"" default=""元""/>
|
||||
<property name=""3-03-08-构件物流运输存在高空作业"" type=""string"" default=""否"" />
|
||||
<property name=""3-03-09-构件物流运输高空作业率"" type=""string"" default=""0"" />
|
||||
<property name=""3-03-10-构件物流运输状态"" type=""list"" default=""未运输"">
|
||||
<option>未运输</option><option>装车中</option><option>运输中</option><option>运输完成</option><option>已堆场</option>
|
||||
</property>
|
||||
<property name=""3-03-11-构件物流运输进度%"" type=""string"" default=""0"" />
|
||||
|
||||
<!-- 装配施工阶段合同属性 -->
|
||||
<property name=""4-03-00-装配施工合同属性"" type=""string"" default=""--------------------""/>
|
||||
<property name=""4-03-01-构件装配施工合同编码"" type=""string"" default=""0""/>
|
||||
<property name=""4-03-02-构件装配施工合作单位"" type=""string"" default=""内蒙古大元建筑""/>
|
||||
<property name=""4-03-03-构件装配施工合同合作形式"" type=""list"" default=""技术研发分包"" >
|
||||
<option>赞助</option><option>采购</option><option>租赁</option><option>技术研发分包</option><option>劳务</option>
|
||||
</property>
|
||||
<property name=""4-03-04-构件装配施工质量"" type=""list"" default=""合格"">
|
||||
<option>合格</option><option>不合格</option>
|
||||
</property>
|
||||
<property name=""4-03-05-构件装配施工造价因子"" type=""string"" default=""数据库自动生成后导入""/>
|
||||
<property name=""4-03-06-构件装配施工造价"" type=""string"" default=""0.00""/>
|
||||
<property name=""4-03-07-构件装配施工造价单位"" type=""string"" default=""元""/>
|
||||
<property name=""4-03-08-构件装配施工存在高空作业"" type=""string"" default=""是"" />
|
||||
<property name=""4-03-09-构件装配施工高空作业率"" type=""string"" default=""0"" />
|
||||
<property name=""4-03-10-构件装配施工状态"" type=""list"" default=""未装配施工"">
|
||||
<option>未装配施工</option><option>装配施工中</option><option>装配施工完成</option><option>验收完成</option>
|
||||
</property>
|
||||
<property name=""4-03-11-构件装配施工进度%"" type=""string"" default=""0"" />
|
||||
|
||||
</propertyset>
|
||||
|
||||
</cmig>";
|
||||
|
||||
public static void ExtractToFolder(string folder)
|
||||
{
|
||||
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
|
||||
File.WriteAllText(Path.Combine(folder, "构件属性-构件属性-基本.xml"), BaseXml);
|
||||
File.WriteAllText(Path.Combine(folder, "构件碳排放属性-构件碳排放属性.xml"), CarbonXml);
|
||||
File.WriteAllText(Path.Combine(folder, "构件合同属性-构件合同属性.xml"), ContractXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
504
UserStringProperty/UI/MainDialog.cs
Normal file
504
UserStringProperty/UI/MainDialog.cs
Normal file
@@ -0,0 +1,504 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Rhino.DocObjects;
|
||||
using UserStringProperty.Core;
|
||||
|
||||
namespace UserStringProperty.UI
|
||||
{
|
||||
public enum ApplyMode
|
||||
{
|
||||
Replace, // 替代:清空现有属性后应用新模板
|
||||
Append // 附加:合并,重复 key 时询问
|
||||
}
|
||||
|
||||
public enum ConflictResolution
|
||||
{
|
||||
ReplaceDuplicates, // 替代重复的 key
|
||||
SkipDuplicates // 跳过重复的 key
|
||||
}
|
||||
|
||||
public partial class MainDialog : Form
|
||||
{
|
||||
private InstanceObject _instanceObject;
|
||||
private ComboBox _cmbCmig;
|
||||
private ComboBox _cmbPropertySet;
|
||||
private Panel _panelFields;
|
||||
private Dictionary<string, Control> _fieldControls = new Dictionary<string, Control>();
|
||||
private Button _btnSave;
|
||||
private Button _btnCancel;
|
||||
private Button _btnLoadXml;
|
||||
private Label _lblTemplateInfo;
|
||||
|
||||
public MainDialog(InstanceObject instanceObject)
|
||||
{
|
||||
_instanceObject = instanceObject;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.Text = "属性编辑";
|
||||
this.Size = new Size(550, 580);
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
this.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
|
||||
// Cmig 选择
|
||||
var lblCmig = new Label
|
||||
{
|
||||
Text = "模板类型:",
|
||||
Location = new Point(10, 10),
|
||||
Size = new Size(80, 20)
|
||||
};
|
||||
|
||||
_cmbCmig = new ComboBox
|
||||
{
|
||||
Location = new Point(95, 8),
|
||||
Size = new Size(180, 20),
|
||||
DropDownStyle = ComboBoxStyle.DropDownList
|
||||
};
|
||||
_cmbCmig.SelectedIndexChanged += CmbCmig_SelectedIndexChanged;
|
||||
this.Controls.Add(lblCmig);
|
||||
this.Controls.Add(_cmbCmig);
|
||||
|
||||
// PropertySet 选择
|
||||
var lblPropertySet = new Label
|
||||
{
|
||||
Text = "属性集:",
|
||||
Location = new Point(285, 10),
|
||||
Size = new Size(80, 20)
|
||||
};
|
||||
|
||||
_cmbPropertySet = new ComboBox
|
||||
{
|
||||
Location = new Point(370, 8),
|
||||
Size = new Size(160, 20),
|
||||
DropDownStyle = ComboBoxStyle.DropDownList
|
||||
};
|
||||
_cmbPropertySet.SelectedIndexChanged += CmbPropertySet_SelectedIndexChanged;
|
||||
this.Controls.Add(lblPropertySet);
|
||||
this.Controls.Add(_cmbPropertySet);
|
||||
|
||||
_btnLoadXml = new Button
|
||||
{
|
||||
Text = "加载XML",
|
||||
Location = new Point(10, 38),
|
||||
Size = new Size(90, 25)
|
||||
};
|
||||
_btnLoadXml.Click += BtnLoadXml_Click;
|
||||
|
||||
_lblTemplateInfo = new Label
|
||||
{
|
||||
Text = "",
|
||||
Location = new Point(200, 41),
|
||||
Size = new Size(330, 15),
|
||||
ForeColor = Color.Gray,
|
||||
Font = new System.Drawing.Font(Font.FontFamily, 8)
|
||||
};
|
||||
|
||||
var lblFields = new Label
|
||||
{
|
||||
Text = "字段:",
|
||||
Location = new Point(10, 75),
|
||||
Size = new Size(60, 20)
|
||||
};
|
||||
|
||||
_panelFields = new Panel
|
||||
{
|
||||
Location = new Point(10, 95),
|
||||
Size = new Size(510, 370),
|
||||
AutoScroll = true,
|
||||
BorderStyle = BorderStyle.FixedSingle
|
||||
};
|
||||
|
||||
_btnSave = new Button { Text = "写入属性", Location = new Point(370, 480), Size = new Size(90, 30) };
|
||||
_btnSave.Click += BtnSave_Click;
|
||||
|
||||
_btnCancel = new Button { Text = "取消", Location = new Point(270, 480), Size = new Size(90, 30) };
|
||||
_btnCancel.Click += (s, e) => this.Close();
|
||||
|
||||
this.Controls.AddRange(new Control[] {
|
||||
_btnLoadXml, _lblTemplateInfo,
|
||||
lblCmig, _cmbCmig, lblPropertySet, _cmbPropertySet,
|
||||
lblFields, _panelFields, _btnSave, _btnCancel
|
||||
});
|
||||
|
||||
RefreshCmigCombo();
|
||||
UpdateTemplateInfo();
|
||||
}
|
||||
|
||||
private void UpdateTemplateInfo()
|
||||
{
|
||||
var tm = TemplateManager.Instance;
|
||||
if (!tm.IsLoaded)
|
||||
{
|
||||
_lblTemplateInfo.Text = "未加载模板";
|
||||
}
|
||||
else
|
||||
{
|
||||
var paths = tm.LoadedPaths;
|
||||
if (paths.Count == 1)
|
||||
_lblTemplateInfo.Text = $"已加载: {System.IO.Path.GetFileName(paths[0])}";
|
||||
else
|
||||
_lblTemplateInfo.Text = $"已加载 {paths.Count} 个模板文件";
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshCmigCombo()
|
||||
{
|
||||
_cmbCmig.Items.Clear();
|
||||
_cmbPropertySet.Items.Clear();
|
||||
|
||||
var cmigNames = TemplateManager.Instance.GetCmigNames();
|
||||
if (cmigNames.Count == 0)
|
||||
{
|
||||
_cmbCmig.Items.Add("(请先加载XML模板)");
|
||||
_cmbCmig.SelectedIndex = 0;
|
||||
_cmbCmig.Enabled = false;
|
||||
_cmbPropertySet.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cmbCmig.Items.AddRange(cmigNames.ToArray());
|
||||
_cmbCmig.Enabled = true;
|
||||
if (_cmbCmig.Items.Count > 0)
|
||||
_cmbCmig.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshPropertySetCombo(string cmigName)
|
||||
{
|
||||
_cmbPropertySet.Items.Clear();
|
||||
|
||||
var psNames = TemplateManager.Instance.GetPropertySetNames(cmigName);
|
||||
if (psNames.Count == 0)
|
||||
{
|
||||
_cmbPropertySet.Items.Add("(无属性集)");
|
||||
_cmbPropertySet.SelectedIndex = 0;
|
||||
_cmbPropertySet.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cmbPropertySet.Items.AddRange(psNames.ToArray());
|
||||
_cmbPropertySet.Enabled = true;
|
||||
if (_cmbPropertySet.Items.Count > 0)
|
||||
_cmbPropertySet.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void CmbCmig_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
var cmigName = _cmbCmig.SelectedItem?.ToString();
|
||||
if (!string.IsNullOrEmpty(cmigName))
|
||||
{
|
||||
RefreshPropertySetCombo(cmigName);
|
||||
}
|
||||
}
|
||||
|
||||
private void CmbPropertySet_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
var cmigName = _cmbCmig.SelectedItem?.ToString();
|
||||
var psName = _cmbPropertySet.SelectedItem?.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(cmigName) || string.IsNullOrEmpty(psName))
|
||||
return;
|
||||
|
||||
// 直接加载字段显示,让用户先看到/编辑现有内容
|
||||
// 替代/附加逻辑在保存时才处理
|
||||
BuildFieldsForPropertySet(cmigName, psName);
|
||||
}
|
||||
|
||||
private ApplyMode AskApplyMode()
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
"Block 已有属性。\n\n选择「是」替代现有属性(清空后应用新模板)\n选择「否」附加到现有属性(合并)",
|
||||
"应用模式",
|
||||
MessageBoxButtons.YesNoCancel,
|
||||
MessageBoxIcon.Question);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
return ApplyMode.Replace;
|
||||
else if (result == DialogResult.No)
|
||||
return ApplyMode.Append;
|
||||
else
|
||||
return ApplyMode.Replace; // 默认替代
|
||||
}
|
||||
|
||||
private ConflictResolution AskConflictResolution(List<string> duplicateKeys)
|
||||
{
|
||||
var suffix = duplicateKeys.Count > 5 ? "..." : "";
|
||||
var result = MessageBox.Show(
|
||||
$"发现 {duplicateKeys.Count} 个重复的属性 key:\n{string.Join(", ", duplicateKeys.Take(5))}{suffix}\n\n选择「是」替代这些 key 的值\n选择「否」跳过这些 key",
|
||||
"属性冲突",
|
||||
MessageBoxButtons.YesNoCancel,
|
||||
MessageBoxIcon.Warning);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
return ConflictResolution.ReplaceDuplicates;
|
||||
else
|
||||
return ConflictResolution.SkipDuplicates;
|
||||
}
|
||||
|
||||
private string GetExistingPropertyValue(string key)
|
||||
{
|
||||
var userStrings = _instanceObject.Attributes?.GetUserStrings();
|
||||
if (userStrings != null && userStrings[key] != null)
|
||||
{
|
||||
return userStrings[key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetFieldValue(string key, string value)
|
||||
{
|
||||
if (_fieldControls.ContainsKey(key))
|
||||
{
|
||||
var ctrl = _fieldControls[key];
|
||||
if (ctrl is ComboBox cmb)
|
||||
{
|
||||
if (cmb.Items.Contains(value))
|
||||
cmb.SelectedItem = value;
|
||||
}
|
||||
else if (ctrl is TextBox txt)
|
||||
{
|
||||
txt.Text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> GetExistingPropertyKeys()
|
||||
{
|
||||
var keys = new List<string>();
|
||||
var userStrings = _instanceObject.Attributes?.GetUserStrings();
|
||||
if (userStrings != null)
|
||||
{
|
||||
keys.AddRange(userStrings.AllKeys);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
private void BtnLoadXml_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (var dlg = new OpenFileDialog())
|
||||
{
|
||||
dlg.Filter = "XML文件|*.xml";
|
||||
dlg.Title = "选择属性配置文件";
|
||||
if (dlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
var filePath = dlg.FileName;
|
||||
|
||||
XmlPropertyConfig incomingConfig;
|
||||
try
|
||||
{
|
||||
incomingConfig = XmlPropertyConfig.Load(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"XML 解析失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (incomingConfig.PropertySets.Count == 0)
|
||||
{
|
||||
MessageBox.Show("XML 中没有找到属性集", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var pluginFolder = TemplateManager.Instance.GetPluginFolderPath();
|
||||
using (var importDlg = new XmlImportDialog(incomingConfig, pluginFolder))
|
||||
{
|
||||
if (importDlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
TemplateManager.Instance.Import(
|
||||
filePath,
|
||||
importDlg.SelectedCmigName,
|
||||
importDlg.SelectedPropertySetName
|
||||
);
|
||||
RefreshCmigCombo();
|
||||
UpdateTemplateInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildFieldsForPropertySet(string cmigName, string psName)
|
||||
{
|
||||
_panelFields.Controls.Clear();
|
||||
_fieldControls.Clear();
|
||||
|
||||
var ps = TemplateManager.Instance.GetPropertySet(cmigName, psName);
|
||||
if (ps == null) return;
|
||||
|
||||
int y = 10;
|
||||
|
||||
foreach (var prop in ps.Properties)
|
||||
{
|
||||
if (prop.IsSeparator)
|
||||
{
|
||||
var sep = new Label
|
||||
{
|
||||
Text = prop.Name,
|
||||
Location = new Point(10, y),
|
||||
Size = new Size(480, 20),
|
||||
Font = new System.Drawing.Font(Font.FontFamily, 9, FontStyle.Bold),
|
||||
ForeColor = Color.DarkBlue,
|
||||
BackColor = Color.AliceBlue
|
||||
};
|
||||
_panelFields.Controls.Add(sep);
|
||||
y += 25;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 只使用模板的默认值,不读取 block 现有属性
|
||||
AddFieldControl(prop, y, prop.DefaultValue ?? "");
|
||||
y += 28;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFieldControl(PropertyDefinition prop, int y, string value)
|
||||
{
|
||||
var lbl = new Label
|
||||
{
|
||||
Text = prop.Name,
|
||||
Location = new Point(10, y),
|
||||
Size = new Size(200, 20)
|
||||
};
|
||||
_panelFields.Controls.Add(lbl);
|
||||
|
||||
Control ctrl;
|
||||
if (prop.Type == "list" && prop.Options.Count > 0)
|
||||
{
|
||||
var cmb = new ComboBox
|
||||
{
|
||||
Location = new Point(220, y),
|
||||
Size = new Size(260, 20),
|
||||
DropDownStyle = ComboBoxStyle.DropDownList
|
||||
};
|
||||
cmb.Items.AddRange(prop.Options.ToArray());
|
||||
cmb.SelectedItem = prop.Options.Contains(value) ? value : prop.DefaultValue;
|
||||
ctrl = cmb;
|
||||
}
|
||||
else
|
||||
{
|
||||
var txt = new TextBox
|
||||
{
|
||||
Location = new Point(220, y),
|
||||
Size = new Size(260, 20),
|
||||
Text = value
|
||||
};
|
||||
ctrl = txt;
|
||||
}
|
||||
|
||||
_panelFields.Controls.Add(ctrl);
|
||||
_fieldControls[prop.Name] = ctrl;
|
||||
}
|
||||
|
||||
private void BtnSave_Click(object sender, EventArgs e)
|
||||
{
|
||||
var existingKeys = GetExistingPropertyKeys();
|
||||
|
||||
if (existingKeys.Count > 0)
|
||||
{
|
||||
// block 已有属性,询问 Replace 或 Append
|
||||
var result = MessageBox.Show(
|
||||
"Block 已有属性。\n\n选择「是」替代现有属性(清空后应用新模板)\n选择「否」附加到现有属性(合并)",
|
||||
"应用模式",
|
||||
MessageBoxButtons.YesNoCancel,
|
||||
MessageBoxIcon.Question);
|
||||
|
||||
if (result == DialogResult.Cancel)
|
||||
return;
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
// 替代模式:清空所有现有属性
|
||||
ClearAllProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 附加模式:检查重复 key
|
||||
var cmigName = _cmbCmig.SelectedItem?.ToString();
|
||||
var psName = _cmbPropertySet.SelectedItem?.ToString();
|
||||
if (!string.IsNullOrEmpty(cmigName) && !string.IsNullOrEmpty(psName))
|
||||
{
|
||||
var ps = TemplateManager.Instance.GetPropertySet(cmigName, psName);
|
||||
if (ps != null)
|
||||
{
|
||||
var duplicateKeys = ps.Properties
|
||||
.Where(p => existingKeys.Contains(p.Name) && !p.IsSeparator)
|
||||
.Select(p => p.Name)
|
||||
.ToList();
|
||||
|
||||
if (duplicateKeys.Count > 0)
|
||||
{
|
||||
var suffix = duplicateKeys.Count > 5 ? "..." : "";
|
||||
var res = MessageBox.Show(
|
||||
$"发现 {duplicateKeys.Count} 个重复的属性 key:\n{string.Join(", ", duplicateKeys.Take(5))}{suffix}\n\n选择「是」替代这些 key 的值\n选择「否」跳过这些 key",
|
||||
"属性冲突",
|
||||
MessageBoxButtons.YesNoCancel,
|
||||
MessageBoxIcon.Warning);
|
||||
|
||||
if (res == DialogResult.Cancel)
|
||||
return;
|
||||
|
||||
if (res == DialogResult.Yes)
|
||||
{
|
||||
// 替代重复的 key - 不做特殊处理,直接保存会用模板值覆盖
|
||||
}
|
||||
else
|
||||
{
|
||||
// 跳过重复的 key - 需要恢复原值后再保存
|
||||
foreach (var key in duplicateKeys)
|
||||
{
|
||||
var existingValue = GetExistingPropertyValue(key);
|
||||
if (!string.IsNullOrEmpty(existingValue))
|
||||
{
|
||||
SetFieldValue(key, existingValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存字段
|
||||
var attrs = _instanceObject.Attributes;
|
||||
|
||||
foreach (var kvp in _fieldControls)
|
||||
{
|
||||
string value = "";
|
||||
if (kvp.Value is ComboBox cmb)
|
||||
value = cmb.SelectedItem?.ToString() ?? "";
|
||||
else if (kvp.Value is TextBox txt)
|
||||
value = txt.Text;
|
||||
|
||||
attrs.SetUserString(kvp.Key, value);
|
||||
}
|
||||
|
||||
Rhino.RhinoApp.WriteLine($"已保存 {_fieldControls.Count} 个属性到 Block");
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void ClearAllProperties()
|
||||
{
|
||||
var attrs = _instanceObject.Attributes;
|
||||
if (attrs == null) return;
|
||||
|
||||
var userStrings = attrs.GetUserStrings();
|
||||
if (userStrings == null) return;
|
||||
|
||||
// 清除所有现有属性
|
||||
foreach (var key in userStrings.AllKeys)
|
||||
{
|
||||
attrs.SetUserString(key, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
206
UserStringProperty/UI/XmlImportDialog.cs
Normal file
206
UserStringProperty/UI/XmlImportDialog.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using UserStringProperty.Core;
|
||||
|
||||
namespace UserStringProperty.UI
|
||||
{
|
||||
public enum ImportMode
|
||||
{
|
||||
Append, // 添加新的(如果同名已存在则提示替换)
|
||||
Replace // 完全替代现有同名属性集
|
||||
}
|
||||
|
||||
public class XmlImportDialog : Form
|
||||
{
|
||||
private XmlPropertyConfig _incomingConfig;
|
||||
private ComboBox _cmbCmig;
|
||||
private ComboBox _cmbPropertySet;
|
||||
private Label _lblIncomingInfo;
|
||||
private Label _lblConflictWarning;
|
||||
private Button _btnOK;
|
||||
private Button _btnCancel;
|
||||
private string _pluginFolder;
|
||||
|
||||
public ImportMode SelectedMode { get; private set; }
|
||||
public string SelectedCmigName { get; private set; }
|
||||
public string SelectedPropertySetName { get; private set; }
|
||||
public bool ShouldReplace { get; private set; }
|
||||
|
||||
public XmlImportDialog(XmlPropertyConfig incoming, string pluginFolder)
|
||||
{
|
||||
_incomingConfig = incoming;
|
||||
_pluginFolder = pluginFolder;
|
||||
InitializeComponent();
|
||||
UpdateConflictWarning();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.Text = "导入属性集";
|
||||
this.Size = new Size(550, 250);
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
this.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
|
||||
int y = 15;
|
||||
|
||||
// 选择 Cmig
|
||||
var lblCmig = new Label
|
||||
{
|
||||
Text = "模板类型 (cmig):",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(120, 20)
|
||||
};
|
||||
this.Controls.Add(lblCmig);
|
||||
|
||||
_cmbCmig = new ComboBox
|
||||
{
|
||||
Location = new Point(140, y),
|
||||
Size = new Size(380, 20),
|
||||
DropDownStyle = ComboBoxStyle.DropDownList
|
||||
};
|
||||
_cmbCmig.Items.Add(_incomingConfig.CmigName);
|
||||
_cmbCmig.SelectedIndex = 0;
|
||||
this.Controls.Add(_cmbCmig);
|
||||
|
||||
y += 30;
|
||||
|
||||
// 选择 PropertySet
|
||||
var lblPropertySet = new Label
|
||||
{
|
||||
Text = "属性集 (propertyset):",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(120, 20)
|
||||
};
|
||||
this.Controls.Add(lblPropertySet);
|
||||
|
||||
_cmbPropertySet = new ComboBox
|
||||
{
|
||||
Location = new Point(140, y),
|
||||
Size = new Size(380, 20),
|
||||
DropDownStyle = ComboBoxStyle.DropDownList
|
||||
};
|
||||
foreach (var ps in _incomingConfig.PropertySets)
|
||||
{
|
||||
_cmbPropertySet.Items.Add(ps.Name);
|
||||
}
|
||||
if (_cmbPropertySet.Items.Count > 0)
|
||||
_cmbPropertySet.SelectedIndex = 0;
|
||||
_cmbPropertySet.SelectedIndexChanged += (s, e) => UpdateConflictWarning();
|
||||
this.Controls.Add(_cmbPropertySet);
|
||||
|
||||
y += 35;
|
||||
|
||||
// 即将导入的信息
|
||||
_lblIncomingInfo = new Label
|
||||
{
|
||||
Text = "",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(500, 20),
|
||||
ForeColor = Color.DarkGreen,
|
||||
Font = new Font(Font.FontFamily, 9, FontStyle.Bold)
|
||||
};
|
||||
this.Controls.Add(_lblIncomingInfo);
|
||||
|
||||
y += 25;
|
||||
|
||||
// 冲突警告
|
||||
_lblConflictWarning = new Label
|
||||
{
|
||||
Text = "",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(500, 20)
|
||||
};
|
||||
this.Controls.Add(_lblConflictWarning);
|
||||
|
||||
y += 40;
|
||||
|
||||
// 按钮
|
||||
_btnOK = new Button
|
||||
{
|
||||
Text = "确定导入",
|
||||
Location = new Point(350, y),
|
||||
Size = new Size(90, 30)
|
||||
};
|
||||
_btnOK.Click += (s, e) =>
|
||||
{
|
||||
var cmigName = _cmbCmig.SelectedItem?.ToString() ?? "";
|
||||
var psName = _cmbPropertySet.SelectedItem?.ToString() ?? "";
|
||||
var fileName = $"{cmigName}-{psName}.xml";
|
||||
var existingPath = Path.Combine(_pluginFolder, fileName);
|
||||
|
||||
if (File.Exists(existingPath))
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
$"文件 {fileName} 已存在,是否覆盖?",
|
||||
"确认覆盖",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning);
|
||||
if (result != DialogResult.Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedCmigName = cmigName;
|
||||
SelectedPropertySetName = psName;
|
||||
this.DialogResult = DialogResult.OK;
|
||||
this.Close();
|
||||
};
|
||||
this.Controls.Add(_btnOK);
|
||||
|
||||
_btnCancel = new Button
|
||||
{
|
||||
Text = "取消",
|
||||
Location = new Point(245, y),
|
||||
Size = new Size(90, 30)
|
||||
};
|
||||
_btnCancel.Click += (s, e) =>
|
||||
{
|
||||
this.DialogResult = DialogResult.Cancel;
|
||||
this.Close();
|
||||
};
|
||||
this.Controls.Add(_btnCancel);
|
||||
}
|
||||
|
||||
private void UpdateConflictWarning()
|
||||
{
|
||||
var cmigName = _cmbCmig.SelectedItem?.ToString() ?? "";
|
||||
var psName = _cmbPropertySet.SelectedItem?.ToString() ?? "";
|
||||
|
||||
if (string.IsNullOrEmpty(cmigName) || string.IsNullOrEmpty(psName))
|
||||
{
|
||||
_lblIncomingInfo.Text = "";
|
||||
_lblConflictWarning.Text = "";
|
||||
return;
|
||||
}
|
||||
|
||||
var fileName = $"{cmigName}-{psName}.xml";
|
||||
var existingPath = Path.Combine(_pluginFolder, fileName);
|
||||
var exists = File.Exists(existingPath);
|
||||
|
||||
_lblIncomingInfo.Text = $"待导入: {cmigName}-{psName}";
|
||||
|
||||
if (exists)
|
||||
{
|
||||
_lblConflictWarning.Text = $"⚠ 同名属性集已存在,将覆盖现有文件";
|
||||
_lblConflictWarning.ForeColor = Color.DarkOrange;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lblConflictWarning.Text = $"✓ 同名属性集不存在,将作为新文件保存";
|
||||
_lblConflictWarning.ForeColor = Color.DarkGreen;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckReplace()
|
||||
{
|
||||
var fileName = $"{SelectedCmigName}-{SelectedPropertySetName}.xml";
|
||||
var existingPath = Path.Combine(_pluginFolder, fileName);
|
||||
return File.Exists(existingPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
UserStringProperty/UserStringProperty.cs
Normal file
36
UserStringProperty/UserStringProperty.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Rhino;
|
||||
using Rhino.PlugIns;
|
||||
|
||||
[assembly: Guid("C286E052-06F6-475F-A882-1C1AA31744CF")]
|
||||
|
||||
namespace UserStringProperty
|
||||
{
|
||||
public class UserStringPropertyPlugin : PlugIn
|
||||
{
|
||||
public UserStringPropertyPlugin()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public static UserStringPropertyPlugin Instance { get; private set; }
|
||||
|
||||
protected override LoadReturnCode OnLoad(ref string errorMessage)
|
||||
{
|
||||
// 首次启动时,从内嵌资源提取默认 XML 到插件目录
|
||||
Resources.EmbeddedTemplates.ExtractToFolder(
|
||||
TemplateManager.Instance.GetPluginFolderPath()
|
||||
);
|
||||
|
||||
// 启动时自动加载已保存的模板
|
||||
TemplateManager.Instance.LoadDefault();
|
||||
return LoadReturnCode.Success;
|
||||
}
|
||||
|
||||
protected override void OnShutdown()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
51
UserStringProperty/UserStringProperty.csproj
Normal file
51
UserStringProperty/UserStringProperty.csproj
Normal file
@@ -0,0 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<OutputType>Library</OutputType>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<AssemblyName>UserStringProperty</AssemblyName>
|
||||
<RootNamespace>UserStringProperty</RootNamespace>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<StartupObject />
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<!-- Rhino 插件配置 -->
|
||||
<AssemblyName>UserStringProperty</AssemblyName>
|
||||
<Version>1.0</Version>
|
||||
<Company>YourCompany</Company>
|
||||
<Description>User String Property Plugin</Description>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="RhinoCommon">
|
||||
<HintPath>C:\Program Files\Rhino 8\System\RhinoCommon.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Rhino.UI">
|
||||
<HintPath>C:\Program Files\Rhino 8\System\Rhino.UI.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="UserStringProperty.cs" />
|
||||
<Compile Include="Commands\CmdUserstringProperty.cs" />
|
||||
<Compile Include="UI\MainDialog.cs" />
|
||||
<Compile Include="UI\XmlImportDialog.cs" />
|
||||
<Compile Include="Core\XmlPropertyConfig.cs" />
|
||||
<Compile Include="Core\TemplateManager.cs" />
|
||||
<Compile Include="Resources\EmbeddedTemplates.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user