下载地址:
http://www.mxcad.net:2080/cpp/Tech-XData.zip
在...MxDraw52\Src\MxDraw5.2\samples\Edit的实例中,我们有以下的扩展数据演示功能:
此实例将进一步演示如何获取实体上的扩展数据并修改,演示程序运行如下图:
打开CAD图纸:
在“打开图纸”按钮的单击事件中,我们添加如下函数:
void CTechXDataDlg::OnBnClickedOpenMap() { acDocManager->sendStringToExecute(Mx::mcdbCurDwg()->GetDocument(), L"OpenDwg"); }
事件中的代码大意为调用我们之前准备的命令,接口如下:
// Summary: // 向控件发送一个命令,并执行该命令 // Parameters: // pAcTargetDocument - 执行命令的对象 // pszExecute - 执行命令的对象 // bActivate - 暂没有使用 // bWrapUpInactiveDoc - 暂没有使用 // bEchoString - 暂没有使用 // pParam - 执行命令时,可以带参数执行,用户不需要负责它的内存释放,将由sendStringToExecute函数自动释放 // Returns: // 如果成功返回Mcad::eOk,如果传递的数据非法则返回Mcad::eInvalidInput virtual Mcad::ErrorStatus sendStringToExecute(McApDocument* pAcTargetDocument, LPCTSTR pszExecute, bool bActivate = true, bool bWrapUpInactiveDoc = false, bool bEchoString = true, struct resbuf* pParam = NULL, bool bFunCall = false ) = 0;
而调用的命令,我们需要做出如下的准备,如“选择实体”按钮单击事件中的代码段:
void CTechXDataDlg::OnBnClickedChooseButton() { Mx::mcDocManager()->sendStringToExecute(MxDraw::GetDatabase(m_hDrawOcx)->GetDocument() , _T("CET")); InitToPanle(); }
我们调用的“CET”命令,该命令为我们的自定义命令,我们做出了如下的声明及实现:
命令代码声明如下:
static void ChooseEnt();
命令代码实现如下:
void CTechXDataDlg::ChooseEnt() { MrxDbgUiPrEntity mChooseEnt(L"选择一个实体"); if (MrxDbgUiPrBase::kOk == mChooseEnt.go()) { m_mId = mChooseEnt.objectId(); } else { m_mId.setNull(); } }
在完成命令的具体功能之后,我们将其注册到系统,以供调用,注册命命令声明如下:
static void RegisterCommand();
实现如下:
void CTechXDataDlg::RegisterCommand() { acedRegCmds->addCommand(_T("SysCmd"), _T("CET"), _T("CET"), MCRX_CMD_MODAL, ChooseEnt); }
我们在面板的初始化函数中调用它:
至此我们即完成了自定义命令的所有步骤,需要说明的是,与用户交互的操作,比如我们的自定义命令“ChooseEnt”即是提示用户选择一个实体,必须放在自定义命令中执行。
在 ...\MxDraw52\Src\MxDraw5.2\ArxInc\Mdsdef.h中,我们看到了链表的如下定义:
#ifndef _mdsdef_h #define _mdsdef_h 1 #define TRUE 1 #define FALSE 0 #define EOS '\0' #pragma pack(push, 8) typedef double mds_real; typedef mds_real mds_point[3]; typedef long mds_name[2]; typedef mds_real mds_matrix[4][4]; typedef mds_real *mds_pointp; typedef long *mds_namep; #ifdef X #undef X #endif #ifdef Y #undef Y #endif #ifdef Z #undef Z #endif #ifdef T #undef T #endif #ifndef _XYZT_DEFINED #define _XYZT_DEFINED enum { X = 0, Y = 1, Z = 2 }; enum { T = 3 }; #endif #define PAUSE "\\" enum { RSG_NONULL = 0x01, RSG_NOZERO = 0x02, RSG_NONEG = 0x04, RSG_NOLIM = 0x08, RSG_GETZ = 0x10, RSG_DASH = 0x20, RSG_2D = 0x40, RSG_OTHER = 0x80, RSG_DDISTFIRST = 0x100, RSG_NOXORDRAG = 0x200, RSG_DYNXYCOORDINPUT = 0x400, REG_DYNANGELINPUT = 0x800, REG_DISTANCEINPUT = 0x1000, RET_AUTOINPUT = 0x2000 }; enum { INP_NNULL = RSG_NONULL, INP_NZERO = RSG_NOZERO, INP_NNEG = RSG_NONEG, INP_NLIM = RSG_NOLIM, INP_DASH = RSG_DASH, INP_NZCOORD = RSG_2D }; struct mds_binary { short clen; char *buf; }; union mds_u_val { mds_real rreal; mds_real rpoint[3]; short rint; LPTSTR rstring; long rlname[2]; long rlong; struct mds_binary rbinary; unsigned char ihandle[8]; /*#ifdef _WIN64*/ __int64 objId; // #else // long objId; // #endif void* pObj; }; struct resbuf { struct resbuf *rbnext; short restype; union mds_u_val resval; resbuf() { restype = 5003 ; // RTSHORT rbnext = 0; resval.rint = 0; } }; typedef struct resbuf *pResbuf; typedef const struct resbuf *kpResbuf; #pragma pack(pop) #endif
可以看到的是,链表支持如,整形、句柄、指针、字符串的存储,而在存储中,对于每一个节点的类型设置“restype”必须与存储的数据类型(mds_u_val联合下的数据类型)对应,如此实例中的设置扩展数据代码段:
resbuf* pExDataRb = nullptr; std::vector <resbuf*> vBuf; resbuf* pLastNode = nullptr; auto CreateNewXData = [&]() { pLastNode = pExDataRb = acutBuildList(AcDb::kDxfRegAppName, sTemp, 0); m_mRealNum.GetWindowTextW(sTemp); if (sTemp.GetLength()) { auto pBuf = acutBuildList(AcDb::kDxfXdReal, _wtof(sTemp), 0); pLastNode->rbnext = pBuf; pLastNode = pLastNode->rbnext; vBuf.push_back(pBuf); } m_mShortNum.GetWindowTextW(sTemp); if (sTemp.GetLength()) { auto pBuf = acutBuildList(AcDb::kDxfXdInteger16, _wtoi(sTemp), 0); pLastNode->rbnext = pBuf; pLastNode = pLastNode->rbnext; vBuf.push_back(pBuf); } m_mLongNum.GetWindowTextW(sTemp); if (sTemp.GetLength()) { auto pBuf = acutBuildList(AcDb::kDxfXdInteger32, _wtoi(sTemp), 0); pLastNode->rbnext = pBuf; pLastNode = pLastNode->rbnext; vBuf.push_back(pBuf); } m_mString.GetWindowTextW(sTemp); if (sTemp.GetLength()) { auto pBuf = acutBuildList(AcDb::kDxfXdAsciiString, sTemp, 0); pLastNode->rbnext = pBuf; pLastNode = pLastNode->rbnext; vBuf.push_back(pBuf); } CString sTempY, sTempZ; { m_m3dVectorX.GetWindowTextW(sTemp); if (!sTemp.GetLength()) return; m_m3dVectorY.GetWindowTextW(sTempY); if (!sTempY.GetLength()) return; m_m3dVectorZ.GetWindowTextW(sTempZ); if (!sTempZ.GetLength()) return; mds_real vVec[] = { _wtof(sTemp),_wtof(sTempY),_wtof(sTempZ) }; auto pBuf = acutBuildList(AcDb::kDxfXdXCoord, vVec, 0); pLastNode->rbnext = pBuf; pLastNode = pLastNode->rbnext; vBuf.push_back(pBuf); } };
上述代码段中,我们再次使用到了acutBuildList宏,在构造选择集的介绍文章中我们已对该宏进行了介绍,再次不再赘述。而在构造扩展数据时,大致的结构如下:
可以看到的是,数据都是以程序名(即 kDxfRegAppName = 1001)开始,而在后挂在相关的信息。
获取、修改、删除扩展数据
获取扩展数据的接口如下:
virtual struct resbuf* xData ( LPCTSTR pszRegappName = NULL - 扩展数据应用名,如果为空返回所有扩展数据 ) const;
设置扩展数据的接口如下:
virtual Mcad::ErrorStatus setXData( const struct resbuf* xdata - 扩展数据链表指针,在不使用时调用Mx::mcutRelRb释放链表 );
需要注意的是:如果我们创建了已有的应用程序名并设置到扩展数据,将会覆盖之前该应用名下的数据,在本实例中,我们通过提示用户的方式,选择覆盖、加入尾部(合并)的方式来添加,代码如下:
McDbObjectPointer<AcDbEntity> spEnt(m_mId, AcDb::kForWrite); if (spEnt.openStatus() == Acad::eOk) { auto pData = spEnt->xData(sTemp); if (pData) { auto iResult = MxDraw::MxMessageBox(L"已有该程序名的扩展数据,YES[覆盖] NO[合并] CANCLE[取消]", MB_YESNOCANCEL); if (IDYES == iResult) { CreateNewXData(); } else if (IDNO == iResult) { CreateNewXData(); pLastNode->rbnext = pData->rbnext; pData->rbnext = pExDataRb; } else { return; } } else { CreateNewXData(); } spEnt->setXData(pExDataRb); acutRelRb(pData); }
仅需将已有扩展数据的最后一个数据的rbnext指针指向我们新添加的节点即可
删除扩展数据的接口如下:
virtual Mcad::ErrorStatus delXData( LPCTSTR pzsAppName = NULL - 删除的扩展数据名称,如果为空,删除所有扩展数据。 );