gzxl 发表于 2025-10-27 10:56:30

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);






qifeifei 发表于 2025-10-27 11:08:10

太神奇了 学习了

panliang9 发表于 2025-10-27 11:12:24

楼主能不能给最终的文件,让我们尝尝鲜。

tranque 发表于 2025-10-27 17:09:08

太神奇了 膜拜

jkop 发表于 2025-10-27 22:13:45

神奇文件同步,方便!

edata 发表于 2025-10-27 22:22:09

我在2019下测试,焦点没有切换回来,貌似仅支持鼠标平移,鼠标缩放未生效,键盘缩放未生效。
印象中高飞鸟版主曾经写过一个类似同步的,

gzxl 发表于 2025-10-27 22:26:24

edata 发表于 2025-10-27 22:22
我在2019下测试,焦点没有切换回来,貌似仅支持鼠标平移,鼠标缩放未生效,键盘缩放未生效。
印象中高飞鸟 ...

钩子里面没有写键盘事件的

gzxl 发表于 2025-10-27 22:31:21

一楼补上 FileNavDlgCmd 对话框类

lhg 发表于 2025-10-27 23:13:28

acDocManager->sendStringToExecute(pDoc, _T("MyZoom\n"), true, false, false); 这里的"MyZoom" 是不是要改为“ MyGroupMyZoom”
页: [1]
查看完整版本: ObjectARX 两文档视图同步