ObjectARX 两文档视图同步
本帖最后由 gzxl 于 2025-10-27 22:30 编辑源码就是下面所贴的,应该齐全
补上 FileNavDlgCmd 类
鼠标钩子
#if _MSC_VER <= 1900
BOOL filterMouse(MSG *pMsg)
#else
bool filterMouse(MSG *pMsg)
#endif
{
if (pMsg->message == WM_MBUTTONDOWN ||// 按下鼠标中键
pMsg->message == WM_MOUSEWHEEL) // 鼠标滚轮
{
// 当前活动文档
AcApDocument* pDoc = acDocManager->mdiActiveDocument();
const ACHAR* fileName = pDoc->fileName();
CString strActiveFileName = fileName;
// a="c:\\kele8\\shootman2\\vision\\123.exe" => "123"
strActiveFileName = GetFileTitleFromFileName(strActiveFileName, FALSE);
// 当前视图中心点、左下角、右上角坐标
AcGePoint3d ptLB;
AcGePoint3d ptRT;
AcGePoint3d ptCenter;
CViewUtil::GetViewWindow(ptLB, ptRT);
ptCenter.x = (ptLB.x + ptRT.x) * 0.5;
ptCenter.y = (ptLB.y + ptRT.y) * 0.5;
// 赋值
DocVars.docData().g_ptCenter = ptCenter;
DocVars.docData().g_ptLeftBottom = ptLB;
DocVars.docData().g_ptRightTop = ptRT;
DocVars.docData().g_strActiveFileName = strActiveFileName;
DocVars.docData().g_bZoomView = true;
}
else if (pMsg->message == WM_MBUTTONUP) // 释放鼠标中键
{
StartSynView(); // 开始同步两个文档
DocVars.docData().g_bZoomView = false;
}
#if _MSC_VER <= 1900
return FALSE;
#else
return false;
#endif
}
两文档视图中心点、左下角、右上角坐标同步
void StartSynView()
{
// 鼠标中键、滚轮
if (!DocVars.docData().g_bZoomView)
return;
// 视图左下角、右上角坐标(用户坐标系)
AcGePoint3d ptLB;
AcGePoint3d ptRT;
if (!CViewUtil::GetViewWindow(ptLB, ptRT))
return;
// 视图中心点
AcGePoint3d ptCenter;
ptCenter.x = (ptLB.x + ptRT.x) * 0.5;
ptCenter.y = (ptLB.y + ptRT.y) * 0.5;
ptCenter.z = 0.0;
if (CViewUtil::IsEqual(ptCenter, DocVars.docData().g_ptCenter))
return;
// 当前活动文档名称
CString strActiveFileName = DocVars.docData().g_strActiveFileName;
// 遍历文档
AcApDocumentIterator *pIter = NULL;
pIter = acDocManager->newAcApDocumentIterator();
for (; !pIter->done(); pIter->step())
{
AcApDocument *pDoc = pIter->document();
const ACHAR* fileName = pDoc->fileName();
CString strFileName = fileName;
// a="c:\\kele8\\shootman2\\vision\\123.exe" => "123"
strFileName = GetFileTitleFromFileName(strFileName, FALSE);
if (_tcsicmp(strActiveFileName, strFileName) != 0)
{
acDocManager->activateDocument(pDoc);
DocVars.docData().g_ptCenter = ptCenter;
DocVars.docData().g_ptLeftBottom = ptLB;
DocVars.docData().g_ptRightTop = ptRT;
DocVars.docData().g_strActiveFileName = strFileName;
acDocManager->sendStringToExecute(pDoc, _T("MyZoom\n"), true, false, false);
}
}
delete pIter;
pIter = NULL;
}
字符串处理
/*
a="c:\\kele8\\shootman2\\vision\\123.exe";
b=this->GetFileTitleFromFileName(a,TRUE);
c=this->GetFileTitleFromFileName(a,FALSE);
AfxMessageBox(b); = 123.exe
AfxMessageBox(c); = 123
*/
CString GetFileTitleFromFileName(CString FileName, BOOL Ext)
{
int Where;
Where = FileName.ReverseFind('\\');
if (Where == -1)
Where = FileName.ReverseFind('/');
CString FileTitle = FileName.Right(FileName.GetLength() - 1 - Where);
if (!Ext)
{
int Which = FileTitle.ReverseFind('.');
if (Which != -1)
FileTitle = FileTitle.Left(Which);
}
return FileTitle;
}
修改视图
static void MyGroupMyZoom()
{
AcGePoint3d ptCenter = DocVars.docData().g_ptCenter;
AcGePoint3d ptLB = DocVars.docData().g_ptLeftBottom;
AcGePoint3d ptRT = DocVars.docData().g_ptRightTop;
CViewUtil::SetCenter(ptCenter);
CViewUtil::Set(ptLB, ptRT);
}
AcGePoint2d CViewUtil::ToPoint2d(const AcGePoint3d &point3d)
{
return AcGePoint2d(point3d.x, point3d.y);
}
AcGePoint3d CViewUtil::WcsToDcsPoint(const AcGePoint3d &point)
{
// 转换成世界坐标
AcGePoint3d pt;
struct resbuf rbFrom, rbTo;
rbFrom.restype = RTSHORT;
rbFrom.resval.rint = 0; // from WCS
rbTo.restype = RTSHORT;
rbTo.resval.rint = 2; // to DCS
acedTrans(asDblArray(point), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(pt));
return pt;
}
// 获得当前的视图设置
void CViewUtil::GetCurrentView(AcDbViewTableRecord &view)
{
struct resbuf rb;
struct resbuf wcs, ucs, dcs; // 转换坐标时使用的坐标系统标记
wcs.restype = RTSHORT;
wcs.resval.rint = 0;
ucs.restype = RTSHORT;
ucs.resval.rint = 1;
dcs.restype = RTSHORT;
dcs.resval.rint = 2;
// 获得当前视口的"查看"模式
acedGetVar(_T("VIEWMODE"), &rb);
view.setPerspectiveEnabled((rb.resval.rint & 1) != 0);
view.setFrontClipEnabled((rb.resval.rint & 2) != 0);
view.setBackClipEnabled((rb.resval.rint & 4) != 0);
view.setFrontClipAtEye((rb.resval.rint & 16) == 0);
// 当前视口中视图的中心点(UCS坐标)
acedGetVar(_T("VIEWCTR"), &rb);
acedTrans(rb.resval.rpoint, &ucs, &dcs, 0, rb.resval.rpoint);
view.setCenterPoint(AcGePoint2d(rb.resval.rpoint, rb.resval.rpoint));
// 当前视口透视图中的镜头焦距长度(单位为毫米)
acedGetVar(_T("LENSLENGTH"), &rb);
view.setLensLength(rb.resval.rreal);
// 当前视口中目标点的位置(以 UCS 坐标表示)
acedGetVar(_T("TARGET"), &rb);
acedTrans(rb.resval.rpoint, &ucs, &wcs, 0, rb.resval.rpoint);
view.setTarget(AcGePoint3d(rb.resval.rpoint, rb.resval.rpoint, rb.resval.rpoint));
// 当前视口的观察方向(UCS)
acedGetVar(_T("VIEWDIR"), &rb);
acedTrans(rb.resval.rpoint, &ucs, &wcs, 1, rb.resval.rpoint);
view.setViewDirection(AcGeVector3d(rb.resval.rpoint, rb.resval.rpoint, rb.resval.rpoint));
// 当前视口的视图高度(图形单位)
acedGetVar(_T("VIEWSIZE"), &rb);
view.setHeight(rb.resval.rreal);
double height = rb.resval.rreal;
// 以像素为单位的当前视口的大小(X 和 Y 值)
acedGetVar(_T("SCREENSIZE"), &rb);
view.setWidth(rb.resval.rpoint / rb.resval.rpoint * height);
// 当前视口的视图扭转角
acedGetVar(_T("VIEWTWIST"), &rb);
view.setViewTwist(rb.resval.rreal);
// 将模型选项卡或最后一个布局选项卡置为当前
acedGetVar(_T("TILEMODE"), &rb);
int tileMode = rb.resval.rint;
// 设置当前视口的标识码
acedGetVar(_T("CVPORT"), &rb);
int cvport = rb.resval.rint;
// 是否是模型空间的视图
bool paperspace = ((tileMode == 0) && (cvport == 1)) ? true : false;
view.setIsPaperspaceView(paperspace);
if (!paperspace)
{
// 当前视口中前向剪裁平面到目标平面的偏移量
acedGetVar(_T("FRONTZ"), &rb);
view.setFrontClipDistance(rb.resval.rreal);
// 获得当前视口后向剪裁平面到目标平面的偏移值
acedGetVar(_T("BACKZ"), &rb);
view.setBackClipDistance(rb.resval.rreal);
}
else
{
view.setFrontClipDistance(0.0);
view.setBackClipDistance(0.0);
}
}
// 给定显示范围的最大、最小角点坐标和缩放比例,修改视图
void CViewUtil::Set(const AcGePoint3d &ptMin, const AcGePoint3d &ptMax, double scale)
{
AcDbViewTableRecord view;
GetCurrentView(view);
// 将参数的两个点从世界坐标系转换到显示坐标系
AcGePoint3d ptMinDcs = WcsToDcsPoint(ptMin);
AcGePoint3d ptMaxDcs = WcsToDcsPoint(ptMax);
// 设置视图的中心点
view.setCenterPoint(AcGePoint2d((ptMinDcs.x + ptMaxDcs.x) / 2, (ptMinDcs.y + ptMaxDcs.y) / 2));
// 设置视图的高度和宽度
view.setHeight(fabs(ptMinDcs.y - ptMaxDcs.y) * scale);
view.setWidth(fabs(ptMinDcs.x - ptMaxDcs.x) * scale);
// 将视图对象设置为当前视图
acedSetCurrentView(&view, NULL);
}
// 将视图移动到给定的中心点
void CViewUtil::SetCenter(const AcGePoint3d ¢er)
{
AcDbViewTableRecord view;
GetCurrentView(view);
// 将参数的点从世界坐标系转换到显示坐标系
AcGePoint3d centerDcs = WcsToDcsPoint(center);
// 设置视图的中心点
view.setCenterPoint(ToPoint2d(centerDcs));
// 将视图对象设置为当前视图
acedSetCurrentView(&view, NULL);
}
// 获取系统变量
// strName ----------- 设置对象
// ptPoint ----------- 返回值
BOOL CViewUtil::GetSysVar(CString strName, AcGePoint3d &ptPoint)
{
BOOL bRet = FALSE;
struct resbuf rb;
ptPoint.set(0.0, 0.0, 0.0);
if (acedGetVar(strName, &rb) == RTNORM)
{
ASSERT(rb.restype == RT3DPOINT);
ptPoint.set(rb.resval.rpoint,
rb.resval.rpoint,
rb.resval.rpoint);
bRet = TRUE;
}
return bRet;
}
// 获取系统变量
// strName ----------- 设置对象
// dVal -------------- 返回值
BOOL CViewUtil::GetSysVar(CString strName, double &dVal)
{
BOOL bRet = FALSE;
struct resbuf rb;
dVal = 0.0;
if (acedGetVar(strName, &rb) == RTNORM)
{
ASSERT(rb.restype == RTREAL);
dVal = rb.resval.rreal;
bRet = TRUE;
}
return bRet;
}
// 得到视图窗口大小(用户坐标系)
// ptPointLB当前视图左下角坐标
// ptPointRT当前视图右上角坐标
BOOL CViewUtil::GetViewWindow(AcGePoint3d &ptPointLB, AcGePoint3d &ptPointRT)
{
double dViewWidth = 0.0; // 视图宽度
double dViewHeight = 0.0; // 视图高度
AcGePoint3d ptCenter; // 视图中心点
AcGePoint3d ptPoint;
// 得到参数
if (!GetSysVar("VIEWCTR", ptCenter))//(用户坐标系)
{
return FALSE;
}
if (!GetSysVar("VIEWSIZE", dViewHeight))
{
return FALSE;
}
if (!GetSysVar("SCREENSIZE", ptPoint))
{
return FALSE;
}
dViewWidth = (ptPoint.x / ptPoint.y)*dViewHeight;
// 计算视图窗口大小
ptPointLB.x = ptCenter.x - dViewWidth / 2;
ptPointLB.y = ptCenter.y - dViewHeight / 2;
ptPointLB.z = 0.0;
ptPointRT.x = ptCenter.x + dViewWidth / 2;
ptPointRT.y = ptCenter.y + dViewHeight / 2;
ptPointRT.z = 0.0;
return TRUE;
}
测试
vector<CString> fileNames;
if (CFileNavDlgCmd::FileNavCmd(fileNames))
{
// 关闭开始页
int nStartmode = 0;
resbuf rb;
int nRes = acedGetVar(_T("STARTMODE"), &rb);
if (nRes != RTNORM)
return;
if (rb.restype == RTSHORT)
nStartmode = rb.resval.rint;
rb.rbnext = NULL;
rb.restype = RTSHORT;
rb.resval.rint = 0;
nRes = acedSetVar(_T("STARTMODE"), &rb);
// 打开 Dwg 文件, 命令标志为 ACRX_CMD_SESSION
for (size_t i = 0; i < fileNames.size(); i++)
{
CString strFile = fileNames;
if (acDocManager->isApplicationContext())
{
acDocManager->appContextOpenDocument((const TCHAR *)strFile);
}
}
// 遍历文档,关闭其他的Dwg文件(比如默认的 Drawing1)
AcApDocumentIterator *pIter1 = acDocManager->newAcApDocumentIterator();
for (; !pIter1->done(); pIter1->step())
{
AcApDocument *pDoc = pIter1->document();
acDocManager->activateDocument(pDoc);
acDocManager->setCurDocument(pDoc, AcAp::kWrite);
const ACHAR* fileName = pDoc->fileName();
CString strFileName = fileName;
CString str1 = GetFileTitleFromFileName(strFileName, FALSE);
bool bEqual = false;
for (size_t i = 0; i < fileNames.size(); i++)
{
CString str2 = GetFileTitleFromFileName(fileNames, FALSE);
if (_tcsicmp(str2, str1) == 0)
{
bEqual = true;
break;
}
}
if (!bEqual)
{
acDocManager->closeDocument(pDoc);
}
}
delete pIter1;
// 遍历文档,发送 SYSWINDOWS V 命令(垂直平铺两个文档)
AcApDocumentIterator *pIter2 = acDocManager->newAcApDocumentIterator();
for (; !pIter2->done(); pIter2->step())
{
AcApDocument *pDoc = pIter2->document();
acDocManager->activateDocument(pDoc);
acDocManager->setCurDocument(pDoc, AcAp::kWrite);
CString cmd = _T("SYSWINDOWS V ");
acDocManager->sendStringToExecute(curDoc(), cmd, true, false, false);
break;
}
delete pIter2;
// 注册鼠标消息钩子
acedRegisterFilterWinMsg(filterMouse);
// 还原开始页
rb.rbnext = NULL;
rb.restype = RTSHORT;
rb.resval.rint = nStartmode;
nRes = acedSetVar(_T("STARTMODE"), &rb);
}
卸载钩子
// 移除鼠标消息钩子
acedRemoveFilterWinMsg(filterMouse);
太神奇了 学习了 楼主能不能给最终的文件,让我们尝尝鲜。 太神奇了 膜拜 神奇文件同步,方便! 我在2019下测试,焦点没有切换回来,貌似仅支持鼠标平移,鼠标缩放未生效,键盘缩放未生效。
印象中高飞鸟版主曾经写过一个类似同步的,
edata 发表于 2025-10-27 22:22
我在2019下测试,焦点没有切换回来,貌似仅支持鼠标平移,鼠标缩放未生效,键盘缩放未生效。
印象中高飞鸟 ...
钩子里面没有写键盘事件的 一楼补上 FileNavDlgCmd 对话框类 acDocManager->sendStringToExecute(pDoc, _T("MyZoom\n"), true, false, false); 这里的"MyZoom" 是不是要改为“ MyGroupMyZoom”
页:
[1]