05-扩展数据与字典操作教程
第五章:扩展数据与字典操作教程
5.1 扩展数据基础
5.1.1 什么是扩展数据
在AutoCAD二次开发中,我们经常需要在图元上存储自定义数据。AutoCAD提供了两种主要的数据存储方式:
- 扩展数据(XData):直接附加在图元上的数据
- 扩展字典(XDictionary):通过字典对象存储的数据
扩展数据(Extended Data,简称XData)是一种将自定义信息附加到图元的机制。每个图元最多可以存储16KB的扩展数据。
5.1.2 扩展数据的结构
扩展数据由一系列TypedValue对象组成,每个TypedValue包含一个DXF组码和对应的值:
扩展数据结构:
┌─────────────────────────────────────────┐
│ 应用程序名 (1001) │
├─────────────────────────────────────────┤
│ 数据项1 (1000-1071) │
│ 数据项2 (1000-1071) │
│ ... │
└─────────────────────────────────────────┘
常用的扩展数据组码:
| 组码 | 数据类型 | 说明 |
|---|---|---|
| 1000 | 字符串 | 最多255个字符 |
| 1001 | 字符串 | 应用程序名(必须注册) |
| 1002 | 字符串 | 控制字符串("{" 或 "}") |
| 1003 | 图层名 | 图层名称 |
| 1010 | Point3d | 三维点 |
| 1040 | double | 实数 |
| 1041 | double | 距离(受比例影响) |
| 1070 | short | 16位整数 |
| 1071 | int | 32位整数 |
5.1.3 注册应用程序
在使用扩展数据之前,必须先注册应用程序名称:
using var tr = new DBTrans();
// 注册应用程序
string appName = "MyApplication";
if (!tr.RegAppTable.Has(appName))
{
tr.RegAppTable.Add(appName);
}
5.2 IFoxCAD的XDataList类
5.2.1 XDataList类设计
IFoxCAD提供了XDataList类来简化扩展数据的操作:
public class XDataList : List<TypedValue>
{
// 应用程序名
public string AppName { get; private set; }
// 构造函数
public XDataList(string appName);
public XDataList(ResultBuffer resultBuffer);
// 添加数据
public void Add(DxfCode code, object value);
public void Add(int code, object value);
// 转换方法
public ResultBuffer ToResultBuffer();
public TypedValue[] ToArray();
}
5.2.2 创建扩展数据
using var tr = new DBTrans();
// 确保应用程序已注册
string appName = "MyApp";
if (!tr.RegAppTable.Has(appName))
{
tr.RegAppTable.Add(appName);
}
// 创建扩展数据
var xdata = new XDataList(appName);
xdata.Add(DxfCode.ExtendedDataAsciiString, "项目编号");
xdata.Add(DxfCode.ExtendedDataAsciiString, "PRJ-001");
xdata.Add(DxfCode.ExtendedDataInteger32, 100);
xdata.Add(DxfCode.ExtendedDataReal, 3.14159);
// 选择图元并添加扩展数据
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status == PromptStatus.OK)
{
var ent = tr.GetObject<Entity>(result.ObjectId, OpenMode.ForWrite);
if (ent != null)
{
ent.XData = xdata.ToResultBuffer();
tr.Editor?.WriteMessage("\n扩展数据已添加!");
}
}
5.2.3 读取扩展数据
using var tr = new DBTrans();
// 选择图元
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status != PromptStatus.OK) return;
var ent = tr.GetObject<Entity>(result.ObjectId);
if (ent == null) return;
// 获取扩展数据
ResultBuffer? rb = ent.GetXDataForApplication("MyApp");
if (rb == null)
{
tr.Editor?.WriteMessage("\n该图元没有扩展数据!");
return;
}
// 读取数据
var xdata = new XDataList(rb);
tr.Editor?.WriteMessage("\n扩展数据内容:");
foreach (var tv in xdata)
{
tr.Editor?.WriteMessage($"\n 组码 {tv.TypeCode}: {tv.Value}");
}
5.2.4 修改扩展数据
using var tr = new DBTrans();
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status != PromptStatus.OK) return;
var ent = tr.GetObject<Entity>(result.ObjectId, OpenMode.ForWrite);
if (ent == null) return;
// 获取现有扩展数据
string appName = "MyApp";
ResultBuffer? rb = ent.GetXDataForApplication(appName);
if (rb != null)
{
var xdata = new XDataList(rb);
// 修改数据(找到并替换)
for (int i = 0; i < xdata.Count; i++)
{
if (xdata[i].TypeCode == (int)DxfCode.ExtendedDataAsciiString &&
xdata[i].Value.ToString() == "PRJ-001")
{
xdata[i] = new TypedValue(
(int)DxfCode.ExtendedDataAsciiString, "PRJ-002");
}
}
// 保存修改
ent.XData = xdata.ToResultBuffer();
tr.Editor?.WriteMessage("\n扩展数据已修改!");
}
5.2.5 删除扩展数据
using var tr = new DBTrans();
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status != PromptStatus.OK) return;
var ent = tr.GetObject<Entity>(result.ObjectId, OpenMode.ForWrite);
if (ent == null) return;
// 删除特定应用程序的扩展数据
string appName = "MyApp";
// 创建只包含应用程序名的空扩展数据
var emptyXdata = new ResultBuffer(
new TypedValue((int)DxfCode.ExtendedDataRegAppName, appName));
ent.XData = emptyXdata;
tr.Editor?.WriteMessage("\n扩展数据已删除!");
5.3 XRecordDataList类
5.3.1 什么是XRecord
XRecord(扩展记录)是另一种存储自定义数据的方式。与XData不同,XRecord没有16KB的大小限制,可以存储更多的数据。XRecord通常存储在字典中。
5.3.2 XRecordDataList类
IFoxCAD提供了XRecordDataList类来简化XRecord的操作:
public class XRecordDataList : List<TypedValue>
{
// 添加数据
public void Add(DxfCode code, object value);
public void Add(int code, object value);
// 转换方法
public ResultBuffer ToResultBuffer();
public static XRecordDataList FromResultBuffer(ResultBuffer rb);
}
5.3.3 创建和使用XRecord
using var tr = new DBTrans();
// 获取或创建自定义字典
DBDictionary myDict;
string dictName = "MyAppDict";
if (tr.NamedObjectsDict.Contains(dictName))
{
myDict = tr.GetObject<DBDictionary>(
tr.NamedObjectsDict.GetAt(dictName), OpenMode.ForWrite)!;
}
else
{
using (tr.NamedObjectsDict.ForWrite())
{
myDict = new DBDictionary();
tr.NamedObjectsDict.SetAt(dictName, myDict);
tr.Transaction.AddNewlyCreatedDBObject(myDict, true);
}
}
// 创建XRecord数据
var data = new XRecordDataList();
data.Add(DxfCode.Text, "配置项1");
data.Add(DxfCode.Text, "值1");
data.Add(DxfCode.Int32, 100);
data.Add(DxfCode.Real, 3.14159);
// 创建XRecord并存储
using (myDict.ForWrite())
{
var xrec = new Xrecord();
xrec.Data = data.ToResultBuffer();
myDict.SetAt("Settings", xrec);
tr.Transaction.AddNewlyCreatedDBObject(xrec, true);
}
tr.Editor?.WriteMessage("\n数据已保存!");
5.3.4 读取XRecord数据
using var tr = new DBTrans();
// 获取字典
string dictName = "MyAppDict";
if (!tr.NamedObjectsDict.Contains(dictName))
{
tr.Editor?.WriteMessage("\n找不到字典!");
return;
}
var myDict = tr.GetObject<DBDictionary>(
tr.NamedObjectsDict.GetAt(dictName))!;
// 读取XRecord
if (!myDict.Contains("Settings"))
{
tr.Editor?.WriteMessage("\n找不到设置!");
return;
}
var xrec = tr.GetObject<Xrecord>(myDict.GetAt("Settings"))!;
var data = xrec.Data;
if (data != null)
{
tr.Editor?.WriteMessage("\n读取到的数据:");
foreach (TypedValue tv in data)
{
tr.Editor?.WriteMessage($"\n 组码 {tv.TypeCode}: {tv.Value}");
}
}
5.4 图元扩展字典
5.4.1 扩展字典概念
每个DBObject(包括图元)都可以拥有自己的扩展字典(Extension Dictionary)。扩展字典可以存储任意数量的数据,不受XData 16KB的限制。
5.4.2 创建扩展字典
using var tr = new DBTrans();
// 选择图元
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status != PromptStatus.OK) return;
var ent = tr.GetObject<Entity>(result.ObjectId, OpenMode.ForWrite);
if (ent == null) return;
// 确保图元有扩展字典
ObjectId extDictId = ent.ExtensionDictionary;
DBDictionary extDict;
if (extDictId.IsNull)
{
// 创建扩展字典
ent.CreateExtensionDictionary();
extDict = tr.GetObject<DBDictionary>(
ent.ExtensionDictionary, OpenMode.ForWrite)!;
}
else
{
extDict = tr.GetObject<DBDictionary>(extDictId, OpenMode.ForWrite)!;
}
// 在扩展字典中存储数据
var data = new XRecordDataList();
data.Add(DxfCode.Text, "设备名称");
data.Add(DxfCode.Text, "电动机M1");
data.Add(DxfCode.Real, 5.5); // 功率
data.Add(DxfCode.Int32, 380); // 电压
using (extDict.ForWrite())
{
var xrec = new Xrecord();
xrec.Data = data.ToResultBuffer();
extDict.SetAt("DeviceInfo", xrec);
tr.Transaction.AddNewlyCreatedDBObject(xrec, true);
}
tr.Editor?.WriteMessage("\n设备信息已保存到图元扩展字典!");
5.4.3 读取扩展字典
using var tr = new DBTrans();
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status != PromptStatus.OK) return;
var ent = tr.GetObject<Entity>(result.ObjectId);
if (ent == null) return;
// 获取扩展字典
if (ent.ExtensionDictionary.IsNull)
{
tr.Editor?.WriteMessage("\n该图元没有扩展字典!");
return;
}
var extDict = tr.GetObject<DBDictionary>(ent.ExtensionDictionary)!;
// 读取数据
if (!extDict.Contains("DeviceInfo"))
{
tr.Editor?.WriteMessage("\n找不到设备信息!");
return;
}
var xrec = tr.GetObject<Xrecord>(extDict.GetAt("DeviceInfo"))!;
if (xrec.Data != null)
{
tr.Editor?.WriteMessage("\n设备信息:");
var tvs = xrec.Data.AsArray();
for (int i = 0; i < tvs.Length; i += 2)
{
if (i + 1 < tvs.Length)
{
tr.Editor?.WriteMessage($"\n {tvs[i].Value}: {tvs[i + 1].Value}");
}
}
}
5.5 命名对象字典操作
5.5.1 命名对象字典结构
命名对象字典(Named Objects Dictionary)是数据库的根字典,可以存储自定义的字典和数据。
命名对象字典
├── ACAD_GROUP (组字典)
├── ACAD_LAYOUT (布局字典)
├── ACAD_MATERIAL (材质字典)
├── MyAppDict (自定义字典)
│ ├── Settings
│ ├── UserData
│ └── ...
└── ...
5.5.2 创建自定义字典层次
using var tr = new DBTrans();
// 创建多层字典结构
// MyApp
// ├── Config
// │ ├── General
// │ └── Advanced
// └── Data
// ├── Projects
// └── Users
// 获取或创建根字典
DBDictionary GetOrCreateDict(DBDictionary parent, string name)
{
using (parent.ForWrite())
{
if (parent.Contains(name))
{
return tr.GetObject<DBDictionary>(parent.GetAt(name))!;
}
else
{
var dict = new DBDictionary();
parent.SetAt(name, dict);
tr.Transaction.AddNewlyCreatedDBObject(dict, true);
return dict;
}
}
}
// 创建字典结构
var rootDict = GetOrCreateDict(tr.NamedObjectsDict, "MyApp");
var configDict = GetOrCreateDict(rootDict, "Config");
var generalDict = GetOrCreateDict(configDict, "General");
var advancedDict = GetOrCreateDict(configDict, "Advanced");
var dataDict = GetOrCreateDict(rootDict, "Data");
var projectsDict = GetOrCreateDict(dataDict, "Projects");
var usersDict = GetOrCreateDict(dataDict, "Users");
tr.Editor?.WriteMessage("\n字典结构创建完成!");
5.5.3 存储复杂数据
using var tr = new DBTrans();
// 获取字典
var rootDict = tr.GetObject<DBDictionary>(
tr.NamedObjectsDict.GetAt("MyApp"), OpenMode.ForWrite)!;
var projectsDict = tr.GetObject<DBDictionary>(
tr.GetObject<DBDictionary>(rootDict.GetAt("Data"))!.GetAt("Projects"),
OpenMode.ForWrite)!;
// 存储项目数据
void SaveProject(string projectId, string name, string description,
double area, DateTime createDate)
{
var data = new XRecordDataList();
data.Add(DxfCode.Text, "Name");
data.Add(DxfCode.Text, name);
data.Add(DxfCode.Text, "Description");
data.Add(DxfCode.Text, description);
data.Add(DxfCode.Text, "Area");
data.Add(DxfCode.Real, area);
data.Add(DxfCode.Text, "CreateDate");
data.Add(DxfCode.Text, createDate.ToString("yyyy-MM-dd"));
using (projectsDict.ForWrite())
{
Xrecord xrec;
if (projectsDict.Contains(projectId))
{
xrec = tr.GetObject<Xrecord>(
projectsDict.GetAt(projectId), OpenMode.ForWrite)!;
}
else
{
xrec = new Xrecord();
projectsDict.SetAt(projectId, xrec);
tr.Transaction.AddNewlyCreatedDBObject(xrec, true);
}
xrec.Data = data.ToResultBuffer();
}
}
// 保存几个项目
SaveProject("PRJ001", "办公楼项目", "市中心办公楼设计", 5000.0, DateTime.Now);
SaveProject("PRJ002", "住宅项目", "高层住宅小区", 12000.0, DateTime.Now);
SaveProject("PRJ003", "商业中心", "购物中心设计", 8000.0, DateTime.Now);
tr.Editor?.WriteMessage("\n项目数据已保存!");
5.5.4 读取和遍历字典
using var tr = new DBTrans();
// 遍历所有项目
if (!tr.NamedObjectsDict.Contains("MyApp"))
{
tr.Editor?.WriteMessage("\n找不到应用数据!");
return;
}
var rootDict = tr.GetObject<DBDictionary>(
tr.NamedObjectsDict.GetAt("MyApp"))!;
if (!rootDict.Contains("Data")) return;
var dataDict = tr.GetObject<DBDictionary>(rootDict.GetAt("Data"))!;
if (!dataDict.Contains("Projects")) return;
var projectsDict = tr.GetObject<DBDictionary>(dataDict.GetAt("Projects"))!;
tr.Editor?.WriteMessage("\n项目列表:");
tr.Editor?.WriteMessage("\n" + new string('-', 60));
foreach (DBDictionaryEntry entry in projectsDict)
{
string projectId = entry.Key;
var xrec = tr.GetObject<Xrecord>(entry.Value)!;
if (xrec.Data != null)
{
var data = xrec.Data.AsArray();
// 解析数据
Dictionary<string, string> project = new();
for (int i = 0; i < data.Length; i += 2)
{
if (i + 1 < data.Length)
{
project[data[i].Value.ToString()!] = data[i + 1].Value.ToString()!;
}
}
tr.Editor?.WriteMessage($"\n项目ID: {projectId}");
tr.Editor?.WriteMessage($"\n 名称: {project.GetValueOrDefault("Name", "")}");
tr.Editor?.WriteMessage($"\n 描述: {project.GetValueOrDefault("Description", "")}");
tr.Editor?.WriteMessage($"\n 面积: {project.GetValueOrDefault("Area", "")}");
tr.Editor?.WriteMessage($"\n 创建日期: {project.GetValueOrDefault("CreateDate", "")}");
tr.Editor?.WriteMessage("\n");
}
}
5.6 实体与数据关联
5.6.1 建立图元数据关联
一种常见的需求是将图形中的图元与外部数据关联。可以使用XData存储标识符:
using var tr = new DBTrans();
// 确保应用程序已注册
string appName = "MyDataLink";
if (!tr.RegAppTable.Has(appName))
{
tr.RegAppTable.Add(appName);
}
// 为图元添加数据标识
void LinkEntityToData(Entity ent, string dataId)
{
using (ent.ForWrite())
{
var xdata = new XDataList(appName);
xdata.Add(DxfCode.ExtendedDataAsciiString, "DataLink");
xdata.Add(DxfCode.ExtendedDataAsciiString, dataId);
ent.XData = xdata.ToResultBuffer();
}
}
// 获取图元关联的数据ID
string? GetLinkedDataId(Entity ent)
{
var rb = ent.GetXDataForApplication(appName);
if (rb == null) return null;
var data = rb.AsArray();
for (int i = 0; i < data.Length - 1; i++)
{
if (data[i].Value.ToString() == "DataLink")
{
return data[i + 1].Value.ToString();
}
}
return null;
}
// 示例:为选择的图元添加关联
var result = tr.Editor?.GetSelection();
if (result?.Status == PromptStatus.OK)
{
int index = 1;
foreach (SelectedObject obj in result.Value)
{
if (obj == null) continue;
var ent = tr.GetObject<Entity>(obj.ObjectId, OpenMode.ForWrite);
if (ent != null)
{
string dataId = $"DATA_{index:D5}";
LinkEntityToData(ent, dataId);
index++;
}
}
tr.Editor?.WriteMessage($"\n已为 {result.Value.Count} 个图元添加数据关联!");
}
5.6.2 查找关联图元
using var tr = new DBTrans();
// 查找所有有数据关联的图元
string appName = "MyDataLink";
var filter = OpFilter.Build(e => e.Dxf(-3) == appName);
var result = tr.Editor?.SelectAll(filter);
if (result?.Status == PromptStatus.OK)
{
tr.Editor?.WriteMessage("\n关联的图元:");
foreach (SelectedObject obj in result.Value)
{
if (obj == null) continue;
var ent = tr.GetObject<Entity>(obj.ObjectId);
if (ent == null) continue;
var rb = ent.GetXDataForApplication(appName);
if (rb != null)
{
var data = rb.AsArray();
for (int i = 0; i < data.Length - 1; i++)
{
if (data[i].Value.ToString() == "DataLink")
{
tr.Editor?.WriteMessage(
$"\n {ent.GetType().Name} -> {data[i + 1].Value}");
break;
}
}
}
}
}
5.7 数据持久化方案
5.7.1 文档级数据存储
对于需要在文档中持久化的应用配置,可以使用命名对象字典:
public class DocumentDataManager
{
private const string RootDictName = "MyAppSettings";
public static void SaveSetting(string key, object value)
{
using var tr = new DBTrans();
// 获取或创建设置字典
DBDictionary settingsDict;
if (tr.NamedObjectsDict.Contains(RootDictName))
{
settingsDict = tr.GetObject<DBDictionary>(
tr.NamedObjectsDict.GetAt(RootDictName), OpenMode.ForWrite)!;
}
else
{
using (tr.NamedObjectsDict.ForWrite())
{
settingsDict = new DBDictionary();
tr.NamedObjectsDict.SetAt(RootDictName, settingsDict);
tr.Transaction.AddNewlyCreatedDBObject(settingsDict, true);
}
}
// 保存设置
var data = new ResultBuffer(
new TypedValue((int)DxfCode.Text, value.ToString()));
using (settingsDict.ForWrite())
{
Xrecord xrec;
if (settingsDict.Contains(key))
{
xrec = tr.GetObject<Xrecord>(
settingsDict.GetAt(key), OpenMode.ForWrite)!;
}
else
{
xrec = new Xrecord();
settingsDict.SetAt(key, xrec);
tr.Transaction.AddNewlyCreatedDBObject(xrec, true);
}
xrec.Data = data;
}
}
public static string? GetSetting(string key)
{
using var tr = new DBTrans();
if (!tr.NamedObjectsDict.Contains(RootDictName))
return null;
var settingsDict = tr.GetObject<DBDictionary>(
tr.NamedObjectsDict.GetAt(RootDictName))!;
if (!settingsDict.Contains(key))
return null;
var xrec = tr.GetObject<Xrecord>(settingsDict.GetAt(key))!;
if (xrec.Data == null) return null;
var tvs = xrec.Data.AsArray();
return tvs.Length > 0 ? tvs[0].Value.ToString() : null;
}
}
5.7.2 使用示例
[CommandMethod("SaveConfig")]
public void SaveConfiguration()
{
using var tr = new DBTrans();
// 保存各种设置
DocumentDataManager.SaveSetting("ProjectName", "示例项目");
DocumentDataManager.SaveSetting("Author", "张三");
DocumentDataManager.SaveSetting("Version", "1.0.0");
DocumentDataManager.SaveSetting("LastModified", DateTime.Now.ToString());
tr.Editor?.WriteMessage("\n配置已保存!");
}
[CommandMethod("LoadConfig")]
public void LoadConfiguration()
{
using var tr = new DBTrans();
tr.Editor?.WriteMessage("\n项目配置:");
tr.Editor?.WriteMessage($"\n 项目名称: {DocumentDataManager.GetSetting("ProjectName")}");
tr.Editor?.WriteMessage($"\n 作者: {DocumentDataManager.GetSetting("Author")}");
tr.Editor?.WriteMessage($"\n 版本: {DocumentDataManager.GetSetting("Version")}");
tr.Editor?.WriteMessage($"\n 最后修改: {DocumentDataManager.GetSetting("LastModified")}");
}
5.8 最佳实践
5.8.1 选择合适的存储方式
| 存储方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| XData | 小量图元数据 | 直接关联图元 | 16KB限制 |
| 扩展字典 | 大量图元数据 | 无大小限制 | 需要额外管理 |
| 命名对象字典 | 全局配置数据 | 易于访问 | 与图元无直接关联 |
5.8.2 数据版本管理
public class DataVersionManager
{
private const string VersionKey = "_DataVersion";
private const string CurrentVersion = "2.0";
public static bool NeedsMigration()
{
string? version = DocumentDataManager.GetSetting(VersionKey);
return version != CurrentVersion;
}
public static void MigrateData()
{
string? version = DocumentDataManager.GetSetting(VersionKey);
if (string.IsNullOrEmpty(version))
{
// 从无版本迁移
MigrateFromV0();
}
else if (version == "1.0")
{
// 从1.0迁移到2.0
MigrateFromV1();
}
// 更新版本号
DocumentDataManager.SaveSetting(VersionKey, CurrentVersion);
}
private static void MigrateFromV0()
{
// 迁移旧版本数据的逻辑
}
private static void MigrateFromV1()
{
// 从1.0迁移到2.0的逻辑
}
}
5.8.3 错误处理
public static class SafeDataAccess
{
public static T? GetData<T>(Func<T> accessor, T? defaultValue = default)
{
try
{
return accessor();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"数据访问错误: {ex.Message}");
return defaultValue;
}
}
public static bool TrySetData(Action setter, out string? error)
{
try
{
setter();
error = null;
return true;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
}
// 使用示例
var projectName = SafeDataAccess.GetData(
() => DocumentDataManager.GetSetting("ProjectName"),
"未命名项目");
if (!SafeDataAccess.TrySetData(
() => DocumentDataManager.SaveSetting("Key", "Value"),
out var error))
{
// 处理错误
}
5.9 本章小结
本章我们深入学习了IFoxCAD的扩展数据和字典操作:
- 扩展数据基础:理解了XData的概念、结构和组码
- XDataList类:学习了创建、读取、修改和删除扩展数据
- XRecordDataList类:掌握了XRecord的创建和使用
- 图元扩展字典:学习了如何为图元创建和使用扩展字典
- 命名对象字典:掌握了创建字典层次和存储复杂数据
- 实体数据关联:学习了如何建立图元与数据的关联
- 数据持久化:了解了文档级数据存储的方案
- 最佳实践:学习了存储方式选择、版本管理和错误处理
扩展数据和字典是CAD二次开发中数据存储的核心机制,掌握这些技术对于开发功能完整的CAD插件至关重要。下一章我们将学习IFoxCAD的扩展方法体系。

浙公网安备 33010602011771号