Add plugin GUID, fix template import and dialog improvements
- Add [assembly: Guid(...)] for proper Rhino plugin registration - Fix Import() to add new config to _configs after importing XML - Simplify XmlImportDialog: remove section labels, add conflict confirmation - Move plugin data folder to standard Plug-ins path with GUID suffix - Add ContractXml as third embedded template - Update README with correct import flow and build requirements - Fix csproj duplicate PropertyGroup issue
This commit is contained in:
39
README.md
39
README.md
@@ -1,6 +1,6 @@
|
||||
# UserStringProperty
|
||||
|
||||
Rhino 插件,用于根据用户自定义 XML 配置,给 Block 实例附加构件属性(UserString)。
|
||||
Rhino 插件,根据用户自定义 XML 配置,给 Block 实例附加属性(UserString)。
|
||||
|
||||
## 功能
|
||||
|
||||
@@ -9,7 +9,7 @@ Rhino 插件,用于根据用户自定义 XML 配置,给 Block 实例附加
|
||||
- 自动保存到 Rhino Block 的 UserString
|
||||
- 支持多个 XML 模板同时加载、合并
|
||||
- 模板自动持久化,Rhino 重启后自动恢复
|
||||
- 导入时提供四种合并模式(追加、替代、合并更新、智能合并)
|
||||
- 导入时检测属性集冲突,覆盖或新增
|
||||
|
||||
## 安装
|
||||
|
||||
@@ -32,28 +32,25 @@ dotnet build
|
||||
1. 在 Rhino 中输入命令:`UserstringProperty`
|
||||
2. 选择要编辑的 Block 实例
|
||||
3. 默认模板首次启动自动加载;点 **加载XML** 导入自定义模板
|
||||
4. 在下拉框中选择属性集,编辑各字段值
|
||||
5. 点击 **保存**
|
||||
4. 在弹出的对话框中选择 cmig 和属性集,若同名已存在则需确认覆盖
|
||||
5. 在下拉框中选择属性集,编辑各字段值
|
||||
6. 点击 **写入属性**
|
||||
|
||||
## 模板导入模式
|
||||
## 模板导入
|
||||
|
||||
加载 XML 时,如果已有模板,会弹出对话框让选择导入模式:
|
||||
加载 XML 时,会弹出对话框让选择 cmig 和属性集,检测同名冲突:
|
||||
|
||||
| 模式 | 说明 |
|
||||
|------|------|
|
||||
| 添加为新属性集 | 追加到现有列表末尾 |
|
||||
| 完全替代现有模板 | 清空所有现有属性集 |
|
||||
| 合并更新 | 同名属性集覆盖,其他保留 |
|
||||
| 智能合并 | 同名覆盖,同时追加新属性集 |
|
||||
- **同名属性集不存在** → 直接保存为新模板
|
||||
- **同名属性集已存在** → 需确认是否覆盖
|
||||
|
||||
## XML 配置格式
|
||||
|
||||
插件根据 XML 动态生成 UI。示例参见 `Component_Property_base.xml` 和 `Component_Property_carbon.xml`。
|
||||
插件根据 XML 动态生成 UI。示例 XML 格式如下:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<cmig name="构件编码">
|
||||
<propertyset name="构件属性-基础" exclusive="false">
|
||||
<cmig name="构件属性">
|
||||
<propertyset name="构件属性-基本" exclusive="false">
|
||||
<!-- string 类型 → TextBox -->
|
||||
<property name="0-00-02-年代" type="string" default="PRC"/>
|
||||
|
||||
@@ -95,7 +92,7 @@ dotnet build
|
||||
|
||||
```
|
||||
UserStringProperty/
|
||||
├── CLAUDE.md # agent 指示(不要当作文档)
|
||||
├── CLAUDE.md # agent 指示
|
||||
├── PLAN.md # 待办事项与开发计划
|
||||
├── UserStringProperty.csproj
|
||||
├── UserStringProperty.cs # 插件主类
|
||||
@@ -111,11 +108,15 @@ UserStringProperty/
|
||||
└── EmbeddedTemplates.cs # 内嵌默认 XML 模板
|
||||
```
|
||||
|
||||
## 环境要求
|
||||
## 编译环境
|
||||
|
||||
- **.NET SDK 8.0**
|
||||
- **Rhino 8**(包含 RhinoCommon.dll SDK 引用)
|
||||
- Windows 10/11
|
||||
|
||||
## 运行时要求
|
||||
|
||||
- Rhino 8
|
||||
- .NET Framework 4.8
|
||||
- .NET SDK 8.0(编译用)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ namespace UserStringProperty
|
||||
{
|
||||
_pluginFolder = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"McNeel", "Rhinoceros", "8.0", "plugins", "UserStringProperty"
|
||||
"McNeel", "Rhinoceros", "8.0", "Plug-ins",
|
||||
"UserStringProperty (C286E052-06F6-475F-A882-1C1AA31744CF)"
|
||||
);
|
||||
if (!Directory.Exists(_pluginFolder))
|
||||
Directory.CreateDirectory(_pluginFolder);
|
||||
@@ -61,6 +62,8 @@ namespace UserStringProperty
|
||||
// 执行导入
|
||||
public void Import(string filePath, string cmigName, string propertysetName)
|
||||
{
|
||||
RebuildIndexIfMissing();
|
||||
|
||||
var config = XmlPropertyConfig.Load(filePath);
|
||||
|
||||
// 检查文件是否已存在
|
||||
@@ -82,6 +85,13 @@ namespace UserStringProperty
|
||||
{
|
||||
File.WriteAllText(destPath, xmlContent);
|
||||
_loadedPaths.Add(destPath);
|
||||
|
||||
// 将新导入的 config 加入内存列表
|
||||
var newConfig = XmlPropertyConfig.Load(destPath);
|
||||
if (newConfig.PropertySets.Count > 0)
|
||||
{
|
||||
_configs.Add(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
SaveIndex();
|
||||
@@ -192,6 +202,30 @@ namespace UserStringProperty
|
||||
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 格式保存
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace UserStringProperty.Resources
|
||||
public const string BaseXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<cmig name=""构件属性"">
|
||||
|
||||
<propertyset name=""构件属性-基础"" exclusive=""false"" picture=""基础-任意实体.png"">
|
||||
<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>
|
||||
@@ -118,13 +118,107 @@ namespace UserStringProperty.Resources
|
||||
<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"), BaseXml);
|
||||
File.WriteAllText(Path.Combine(folder, "构件碳排放属性-构件碳排放属性.xml"), CarbonXml);
|
||||
File.WriteAllText(Path.Combine(folder, "构件合同属性-构件合同属性.xml"), ContractXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace UserStringProperty.UI
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.Text = "构件属性编辑";
|
||||
this.Text = "属性编辑";
|
||||
this.Size = new Size(550, 580);
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
this.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
@@ -68,7 +68,7 @@ namespace UserStringProperty.UI
|
||||
// PropertySet 选择
|
||||
var lblPropertySet = new Label
|
||||
{
|
||||
Text = "属性模板:",
|
||||
Text = "属性集:",
|
||||
Location = new Point(285, 10),
|
||||
Size = new Size(80, 20)
|
||||
};
|
||||
@@ -91,19 +91,6 @@ namespace UserStringProperty.UI
|
||||
};
|
||||
_btnLoadXml.Click += BtnLoadXml_Click;
|
||||
|
||||
var btnResetTemplate = new Button
|
||||
{
|
||||
Text = "重置模板",
|
||||
Location = new Point(105, 38),
|
||||
Size = new Size(90, 25)
|
||||
};
|
||||
btnResetTemplate.Click += (s, e) =>
|
||||
{
|
||||
TemplateManager.Instance.Clear();
|
||||
RefreshCmigCombo();
|
||||
UpdateTemplateInfo();
|
||||
};
|
||||
|
||||
_lblTemplateInfo = new Label
|
||||
{
|
||||
Text = "",
|
||||
@@ -135,7 +122,7 @@ namespace UserStringProperty.UI
|
||||
_btnCancel.Click += (s, e) => this.Close();
|
||||
|
||||
this.Controls.AddRange(new Control[] {
|
||||
_btnLoadXml, btnResetTemplate, _lblTemplateInfo,
|
||||
_btnLoadXml, _lblTemplateInfo,
|
||||
lblCmig, _cmbCmig, lblPropertySet, _cmbPropertySet,
|
||||
lblFields, _panelFields, _btnSave, _btnCancel
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace UserStringProperty.UI
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.Text = "导入属性集";
|
||||
this.Size = new Size(550, 350);
|
||||
this.Size = new Size(550, 250);
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
this.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
@@ -49,22 +49,10 @@ namespace UserStringProperty.UI
|
||||
|
||||
int y = 15;
|
||||
|
||||
// 第一段:选择属性集
|
||||
var lblSection1 = new Label
|
||||
{
|
||||
Text = "第一段:选择属性集",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(200, 20),
|
||||
Font = new Font(Font.FontFamily, 10, FontStyle.Bold)
|
||||
};
|
||||
this.Controls.Add(lblSection1);
|
||||
|
||||
y += 30;
|
||||
|
||||
// 选择 Cmig
|
||||
var lblCmig = new Label
|
||||
{
|
||||
Text = "属性容器 (cmig):",
|
||||
Text = "模板类型 (cmig):",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(120, 20)
|
||||
};
|
||||
@@ -108,30 +96,6 @@ namespace UserStringProperty.UI
|
||||
|
||||
y += 35;
|
||||
|
||||
// 分隔线
|
||||
var separator = new Label
|
||||
{
|
||||
Text = "────────────────────────────────────────────────────",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(500, 15),
|
||||
ForeColor = Color.Gray
|
||||
};
|
||||
this.Controls.Add(separator);
|
||||
|
||||
y += 25;
|
||||
|
||||
// 第二段:导入模式
|
||||
var lblSection2 = new Label
|
||||
{
|
||||
Text = "第二段:检测冲突",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(200, 20),
|
||||
Font = new Font(Font.FontFamily, 10, FontStyle.Bold)
|
||||
};
|
||||
this.Controls.Add(lblSection2);
|
||||
|
||||
y += 30;
|
||||
|
||||
// 即将导入的信息
|
||||
_lblIncomingInfo = new Label
|
||||
{
|
||||
@@ -150,8 +114,7 @@ namespace UserStringProperty.UI
|
||||
{
|
||||
Text = "",
|
||||
Location = new Point(15, y),
|
||||
Size = new Size(500, 30),
|
||||
ForeColor = Color.DarkOrange
|
||||
Size = new Size(500, 20)
|
||||
};
|
||||
this.Controls.Add(_lblConflictWarning);
|
||||
|
||||
@@ -166,8 +129,24 @@ namespace UserStringProperty.UI
|
||||
};
|
||||
_btnOK.Click += (s, e) =>
|
||||
{
|
||||
SelectedCmigName = _cmbCmig.SelectedItem?.ToString();
|
||||
SelectedPropertySetName = _cmbPropertySet.SelectedItem?.ToString();
|
||||
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();
|
||||
};
|
||||
@@ -203,16 +182,16 @@ namespace UserStringProperty.UI
|
||||
var existingPath = Path.Combine(_pluginFolder, fileName);
|
||||
var exists = File.Exists(existingPath);
|
||||
|
||||
_lblIncomingInfo.Text = $"待导入: {fileName}";
|
||||
_lblIncomingInfo.Text = $"待导入: {cmigName}-{psName}";
|
||||
|
||||
if (exists)
|
||||
{
|
||||
_lblConflictWarning.Text = $"⚠ 文件已存在: {fileName}\n是否替换现有文件?";
|
||||
_lblConflictWarning.ForeColor = Color.Red;
|
||||
_lblConflictWarning.Text = $"⚠ 同名属性集已存在,将覆盖现有文件";
|
||||
_lblConflictWarning.ForeColor = Color.DarkOrange;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lblConflictWarning.Text = $"✓ 文件不存在,将作为新文件保存";
|
||||
_lblConflictWarning.Text = $"✓ 同名属性集不存在,将作为新文件保存";
|
||||
_lblConflictWarning.ForeColor = Color.DarkGreen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
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
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
<Version>1.0</Version>
|
||||
<Company>YourCompany</Company>
|
||||
<Description>User String Property Plugin</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user