Creating a Document's Window - Part 2-如何创建多视图窗口
摘要
通过本用例你将掌握的内容
- CAAAfrCustomWindow 用例介绍
- CAAAfrCustomWindow 功能说明
- CAAAfrCustomWindow 启动方式
- CAAAfrCustomWindow 源码路径
- 操作步骤详解
- 内容小结
- 参考文献
本用例学习目标
CAAAfrCustomWindow 用例
CAAAfrCustomWindow 功能说明

启动 CAAAfrCustomWindow 方法
- 选择 文件 → 新建(File->New)
- 在新建弹窗中选中 CAASample,点击 确定(OK)
CAAAfrCustomWindow 代码文件位置
安装根目录\CAAApplicationFrame.edu\CAAAfrGeoWindows.m\安装根目录/CAAApplicationFrame.edu/CAAAfrGeoWindows.m/
其中,安装根目录 为 CAA 安装包的安装路径。
- 头文件
CAAAfrCustomWindow.h:存放于 CAAApplicationFrame.edu 框架的PrivateInterfaces目录 - 源文件
CAAAfrCustomWindow.cpp:存放于src目录
分步实现
| 序号 | 步骤 | 所在位置 |
|---|---|---|
| 1 | 创建自定义窗口类 | 本地接口目录 与 源码目录 |
| 2 | 定义对话框对象行为 | Build 方法 |
| 3 | 定义 MDI 文档窗口行为 | Build 方法 |
| 4 | 窗口复制 | Duplicate 方法 |
| 5 | 窗口销毁 | DeleteWindow 方法 与 析构函数 |
创建自定义窗口类
CAAAfrCustomWindow,其头文件定义如下:class CAAAfrCustomWindow: public CATFrmWindow
{
public:
CAAAfrCustomWindow(const CATString & iName, CATFrmEditor * iEditor=NULL);
virtual ~CAAAfrCustomWindow();
virtual CATFrmWindow * DuplicateWindow();
virtual void DeleteWindow();
void Build();
void GetViewers(CATNavigation3DViewer ** oV1,
CATNavigation3DViewer ** oV2,
CATNavigation2DViewer ** oV3);
...
private:
CATNavigation3DViewer * _pViewer1;
CATNavigation3DViewer * _pViewer2;
CATNavigation2DViewer * _pViewer3;
CATPathElement * _pRootObjectPath;
};
CAAAfrCustomWindow 包含以下成员:- 构造函数与析构函数;
- DuplicateWindow 方法:供窗口菜单栏的「新建窗口」命令调用;
- DeleteWindow 方法:用户关闭窗口或关闭文档时自动调用;
- Build 方法:所有对话框类均具备该方法;
- GetViewers 方法:用于获取三个视图的指针。
该类的构造函数实现逻辑简洁。
// 构造函数:初始化自定义窗口
CAAAfrCustomWindow::CAAAfrCustomWindow(const CATString & iName,
CATFrmEditor * iEditor)
: CATFrmWindow(iName, iEditor), // 调用父类构造函数
_pViewer1(NULL), _pViewer2(NULL), _pViewer3(NULL), // 初始化3个视图指针为空
_pRootObjectPath(NULL) // 初始化根对象路径指针为空
{
}
实现界面对象行为
- 实例化所需的各类显示视图
- 在文档窗口中排布这些视图
- 设置当前激活视图
实例化各类显示视图
void CAAAfrCustomWindow::Build()
{
int width, height;
CATString ViewerName;
// 获取视图父级框架窗口
CATDlgFrame * FrameWindow = GetViewerFrame() ;
CATString FrameName = "FrameName";
// 创建自定义对话框容器框架,启用网格布局、无边框样式
CATDlgFrame * Frame = new CATDlgFrame(FrameWindow, FrameName,
CATDlgGridLayout|CATDlgFraNoFrame);
// 创建一号3D导航视图
ViewerName = "Viewer3Dnum1";
width = 200 ; height = 200;
_pViewer1 = new CATNavigation3DViewer(Frame, ViewerName, width, height);
// 创建二号3D导航视图
ViewerName = "Viewer3Dnum2" ;
width = 200 ; height = 300 ;
_pViewer2 = new CATNavigation3DViewer(Frame, ViewerName, width, height);
// 创建2D导航视图
ViewerName = "Viewer2D" ;
width = 200 ; height = 500 ;
_pViewer3 = new CATNavigation2DViewer(Frame, ViewerName, width, height);
...
}
视图尺寸单位为像素。本例中名为 Frame 的多文档应用程序窗口,是所有视图的父容器。该容器框架专门创建于:CATFrmWindow 原生框架(FrameWindow)与各类视图之间。借助该容器,我们可以使用网格布局排布视图,而非实现更为复杂的标签布局[3]。原生 FrameWindow 框架并未采用 CATDlgGridLayout(网格布局)样式 [1]。
在文档窗口中排布视图
...
// 将自定义框架四边绑定至父级窗口
FrameWindow->Attach4Sides(Frame);
// 设置3D视图1网格布局约束
_pViewer1->SetGridConstraints(0,0,1,1,CATGRID_4SIDES);
// 设置3D视图2网格布局约束
_pViewer2->SetGridConstraints(0,1,1,1,CATGRID_4SIDES);
// 设置2D视图网格布局约束
_pViewer3->SetGridConstraints(1,0,2,1,CATGRID_4SIDES);
// 设置网格行、列支持自适应拉伸
Frame->SetGridRowResizable(0,1);
Frame->SetGridRowResizable(1,1);
Frame->SetGridColumnResizable(0,1);
Frame->SetGridColumnResizable(1,1);
...
Attach4Sides 方法用于将自定义框架对象四边贴合绑定至父级 FrameWindow 窗口。该方法为标签布局的专用接口。
随后,作用于 _Viewer1 的 SetGridConstraints 方法,将该视图放置在第 0 行、第 0 列交叉的网格单元格内,横向跨越 1 行、纵向跨越 1 列。
SetGridxxResizable 方法中,以第 0 行、第 1 行及第 0 列、第 1 列作为入参;由于第二个参数赋值为 1,代表上述行列均设为可拉伸。反之,参数为 0 则代表不可拉伸。

设置当前视图
...
_pViewer1->Reframe();
SetViewer(_pViewer1);
...
Reframe 方法用于完整显示 _pViewer1 视图中的全部内容;SetViewer 方法则将 _pViewer1 设置为当前激活视图。
CATFrmWindow 类的 GetViewer 方法获取该当前视图。至此,自定义文档窗口已创建完成,但目前仅具备基础界面,缺少交互功能。接下来我们就来配置各类交互机制。
实现 MDI 文档窗口行为
- 获取可视化管理器与文档根对象;
- 要求文档创建自身图形表达,并将其挂载至可视化管理器;
- 维护上述对象与所有交互相关对象之间的关联关系。
获取可视化管理器与文档根对象
CATFrmWindow 类的 GetEditor 方法,你可以获取构造类对应的编辑器。 CATFrmEditor * pEditor = GetEditor();
CATVisManager * pVisuManager = CATVisManager::GetVisManager();
// 此处为获取根模型 pRootObject 的代码
本文中不会讲解获取待显示模型的代码,因为该代码对于真实的 V5 文档并无实际用途。
通知文档创建图形显示对象,并将其挂载到可视化管理器
if ( (NULL != pEditor ) && (NULL != pRootObject ) && (NULL != pVisuManager) )
{
_pRootObjectPath = new CATPathElement(pRootObject);
CATCommand * CommandSelector = (CATCommand*) pEditor->GetCommandSelector();
list<IID> ListIVisu3d, ListIVisu2d;
IID visu3d = IID_CATI3DGeoVisu;
IID visu2d = IID_CATI2DGeoVisu;
ListIVisu3d.fastadd(&visu3d);
ListIVisu2d.fastadd(&visu2d);
CATViewpoint * pViewPoint1 = NULL;
CATViewpoint * pViewPoint2 = NULL;
CATViewpoint * pViewPoint3 = NULL;
pViewPoint1 = (CATViewpoint*) &(_pViewer1->GetMain3DViewpoint());
pVisuManager->AttachTo(_pRootObjectPath,pViewPoint1,ListIVisu3d,CommandSelector);
pViewPoint2 = (CATViewpoint*) &(_pViewer2->GetMain3DViewpoint());
pVisuManager->AttachTo(_pRootObjectPath,pViewPoint2,ListIVisu3d,CommandSelector);
pViewPoint3 = (CATViewpoint*) &(_pViewer3->GetMain2DViewpoint());
pVisuManager->AttachTo(_pRootObjectPath,pViewPoint3,ListIVisu2d,CommandSelector);
如果成功获取到编辑器、可视化管理器和根对象,就会将根对象封装为 CATPathElement 对象,同时获取命令选择器(即位于命令树顶端的命令 [4]),并创建文档对象所实现的 2D 和 3D 接口列表。随后,为每个视图获取其主观察视角,并将文档根对象、接口列表和命令选择器一并挂载到可视化管理器。
管理这些对象与所有交互功能对象之间的关联
CATPSO * pPSO = pEditor->GetPSO() ; // 预选择对象集
CATHSO * pHSO = pEditor->GetHSO() ; // 高亮显示对象集
pVisuManager->AttachPSOTo(pPSO, pViewPoint1);
pVisuManager->AttachPSOTo(pPSO, pViewPoint2);
pVisuManager->AttachPSOTo(pPSO, pViewPoint3);
pVisuManager->AttachHSOTo(pHSO, pViewPoint1);
pVisuManager->AttachHSOTo(pHSO, pViewPoint2);
pVisuManager->AttachHSOTo(pHSO, pViewPoint3);
CATISO * pISO = pEditor->GetISO();
pISO->AddViewer(_pViewer1);
pISO->AddViewer(_pViewer2);
pISO->AddViewer(_pViewer3);
}
}
}
这段代码从编辑器中获取预选择对象集(PSO)和高亮显示对象集(HSO),并通知可视化控制器将对应的观察视角与 PSO、HSO 进行绑定。
至此,文档窗口的 Build 方法已全部实现完成。
复制文档窗口
...
CATFrmWindow * CAAAfrCustomWindow :: DuplicateWindow()
{
CATString NameOfThis = GetBaseName().CastToCharPtr();
CAAAfrCustomWindow * pWindowToReturn = NULL;
pWindowToReturn = new CAAAfrCustomWindow(NameOfThis,GetEditor());
pWindowToReturn->Build();
pWindowToReturn->SetBaseName(GetBaseName());
float r,v,b ;
if (NULL != _pViewer1) _pViewer1->GetBackgroundColor(&r, &v, &b);
CATNavigation3DViewer * pV1 = NULL;
CATNavigation3DViewer * pV2 = NULL;
CATNavigation2DViewer * pV3 = NULL;
pWindowToReturn->GetViewers(&pV1, &pV2, &pV3);
if (NULL != pV1) pV1->SetBackgroundColor(r, v, b);
return pWindowToReturn;
}
...
- 创建一个与当前窗口类型完全相同的新窗口
- 设置与当前窗口相同的基础名称。如果仅存在一个窗口,
SetBaseName方法会修改该窗口名称;在基础名称后,会自动追加一个数字序号 [2] - 以 RGB 三元组格式获取第一个视图的背景色
- 获取新创建窗口中的所有视图对象
- 将获取到的背景色,应用到新窗口的第一个视图上
该方法使用下方的 GetViewers 方法:
void CAAAfrCustomWindow :: GetViewers(CATNavigation3DViewer ** oV1,
CATNavigation3DViewer ** oV2,
CATNavigation2DViewer ** oV3)
{
*oV1 = _pViewer1;
*oV2 = _pViewer2;
*oV3 = _pViewer3;
}
- 将第一个 3D 视图
_pViewer1赋值给输出参数oV1 - 将第二个 3D 视图
_pViewer2赋值给输出参数oV2 - 将 2D 视图
_pViewer3赋值给输出参数oV3
窗口销毁
void CAAAfrCustomWindow::DeleteWindow()
{
if ( (NULL != GetEditor()) && (NULL != _pViewer1) &&
(NULL != _pViewer2) && (NULL != _pViewer3) )
{
// 从交互对象集(ISO)中解绑视图
CATISO *pISO = NULL;
pISO = GetEditor()->GetISO();
if (NULL != pISO) pISO->RemoveViewer(_pViewer1);
if (NULL != pISO) pISO->RemoveViewer(_pViewer2);
if (NULL != pISO) pISO->RemoveViewer(_pViewer3);
// 获取各视图的观察视点
CATViewpoint * pViewpoint1 = NULL;
CATViewpoint * pViewpoint2 = NULL;
CATViewpoint * pViewpoint3 = NULL;
pViewpoint1 = (CATViewPoint *) &(_pViewer1->GetMain3DViewpoint());
pViewpoint2 = (CATViewPoint *) &(_pViewer2->GetMain3DViewpoint());
pViewpoint3 = (CATViewPoint *) &(_pViewer3->GetMain2DViewpoint());
// 获取全局唯一可视化管理器
CATVisManager *pVisuManager = CATVisManager::GetVisManager();
if ( (NULL != pViewpoint1) &&
(NULL != pViewpoint2) &&
(NULL != pViewpoint3) && (NULL != pVisuManager) )
{
// 从预选择对象集、高亮对象集中解绑视点
pVisuManager->DetachPSOFrom(pViewpoint1);
pVisuManager->DetachPSOFrom(pViewpoint2);
pVisuManager->DetachPSOFrom(pViewpoint3);
pVisuManager->DetachHSOFrom(pViewpoint1);
pVisuManager->DetachHSOFrom(pViewpoint2);
pVisuManager->DetachHSOFrom(pViewpoint3);
// 从可视化管理器中解绑视点与根对象路径
pVisuManager->DetachFrom(_pRootObjectPath, pViewpoint1);
pVisuManager->DetachFrom(_pRootObjectPath, pViewpoint2);
pVisuManager->DetachFrom(_pRootObjectPath, pViewpoint3);
}
}
// 调用父类窗口销毁方法
CATFrmWindow::DeleteWindow();
}
// 析构函数
CAAAfrCustomWindow::~CAAAfrCustomWindow()
{
// 释放根对象路径内存
if (NULL != _pRootObjectPath) 