文 档/ 视 结 构
随 着MFC2.0 的 问 世, 一 种 应 用 程 序 结 构 的 新 方 式--MFC 文 档/ 视 结 构 出 现 了。 在 这 种 结 构 中,CFrameWnd 繁 重 的 任 务 被 委 派 给 几 个 不 同 类, 实 现 了 数 据 存 储 和 显 示 的 分 离。 一 般 情 况 下, 采 用 文 档/ 视 结 构 的 应 用 程 序 至 少 应 由 以 下 对 象 组 成: 应 用 程 序 是 一 个CwinApp 派 生 对 象, 它 充 当 全 部 应 用 程 序 的 容 器。 应 用 程 序 沿 消 息 映 射 网 络 分 配 消 息 给 它 的 所 有 子 程 序。 框 架 窗 口 是 一CfrmeWnd 派 生 对 象。 文 档 是 一 个CDocument 派 生 对 象, 它 存 储 应 用 程 序 的 数 据, 并 把 这 些 信 息 提 供 给 应 用 程 序 的 其 余 部 分。 视 窗 是Cview 派 生 对 象, 它 与 其 父 框 架 窗 口 用 户 区 对 齐。 视 窗 接 受 用 户 对 应 用 程 序 的 输 入 并 显 示 相 关 联 的 文 档 数 据。
MFC 调 用 命 令 处 理 程 序 以 响 应 发 生 在 应 用 程 序 中 的 事 件。 命 令 发 送 的 优 先 级 是:
活 动 的 视 图 框 架 窗 口 → 文 档 → 应 用 程 序 → 默 认 窗 口 过 程(DefWindowsProc)
与 文 档/ 视 结 构 有 关 的在 文 档/ 视 应 用 程 序 中,CWinApp 对 象 拥 有 并 控 制 文 档 模 板, 后 者 产 生 文 档、 框 架 窗 口 及 视 窗。 这 种 相 互 关 系 如 图1 所 示:
图1 应 程 序、 文 档 模 板、 文 档、 框 架 窗 口
及 视 窗 对 象 间 的 关 系
在MDI 应 用 程 序 中, 可 以 处 理 多 个 文 档 类 型, 即 多 个 文 档 模 板, 每 个 模 板 又 可 以 有 多 个 文 档, 每 个 文 档 又 可 以 多 视 显 示。 为 管 理 方 便, 上 一 级 往 往 保 留 了 下 一 级 的 指 针 列 表, 如 图2 所 示:
图2 MDI 中 的 指 针 列 表
图3 存 取 关 系( 箭 头 表 示 获 得 关 系)
下 面 一 段 代 码, 就 是 利 用CDocTemplate、CDocument 和CView 之 间 的 存 取 关 系, 遍 历 整 个 文 档 模 板、 文 档 以 及 视。
CMyApp * pMyApp = (CMyApp *)AfxGetApp(); POSITION p = pMyApp ->GetFirstDocTemplatePosition(); while(p!= NULL) { CDocTemplate * pDocTemplate = pMyApp →GetNextDocTemplate(p); POSITION p1 = pDocTemplate ->GetFirstDocPosition(); while(p1 != NULL) { CDocument * pDocument = pDocTemplate ->GetNextDoc(p1); POSITION p2 = pDocument ->GetFirstViewPosition(); while(p2 != NULL){ CView * pView = pDocument ->GetNextView(p2); } } }CwinApp::OnFileNew、CwinApp::
1. CwinApp::OnFileNew 和CwinApp::OnFileOpen 函 数 的 简 单 流 程。
图4 File New/Open 流 程 图
核 心 操 作 是CDocTemplate::OpenDocument 函 数。 其 函 数 原 型 为:
virtual CDocument * CDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE ) = 0;
2. Window/New 命 令 的 程 序 流 程。
当 主 框 架 窗 口 上 有 子 窗 口 时, 选 择Window/New 命 令 可 以 生 成 该 活 动 子 窗 口 的 影 像。 它 们 有 相 同 的 文 档 模 板、 相 同 的 文 档。 其 流 程 如 下:
几 种 情 况 的 讨 论
1. 如 何 根 据 自 己 的 要 求 来 选 择 文 档 模 板 及 相 应 的 视 和 文 档。
如 果 应 用 程 序 需 要 处 理 多 种 类 型 的 文 档, 并 且 何 时 打 开 何 种 文 档 均 需 程 序 员 手 工 控 制, 程 序 员 必 须 对 文 档 模 板 进 行 编 程。
例 如, 需 要 处 理AVI 和BMP 两 种 文 件 类 型。AVI 和BMP 的 数 据 存 放 格 式 不 同, 不 能 用 同 一 的 数 据 结 构 来 描 述, 同 时, 由 于AVI 是 图 像 序 列,BMP 仅 是 一 幅 图 像, 它 们 的 显 示 是 肯 定 不 一 样 的, 基 于 此, 笔 者 分 别 建 立 两 套 文 档 模 板, 两 套 框 架 窗 口, 两 套 文 档 和 两 套 视, 分 别 用 于AVI 和BMP 的 数 据 存 放 和 显 示。 具 体 步 骤 如 下:
(Step 1): 在 应 用 程 序 类(CWinApp) 的 派 生 类 中 增 加 文 档 模 板 成 员 变 量。
class C3dlcsApp : public CWinApp { public: CMultiDocTemplate * m_pAVIDocTemplate; CMultiDocTemplate * m_pBMPDocTemplate; } (Step 2): 在 主 框 架 中 增 加 菜 单 响 应: void CMainFrame::OnFileOpen() { CFileDialog my(true); if(my.DoModal()==IDOK) { CString FileName = my.GetPathName(); CString FileExt = my.GetFileExt(); if((FileExt=="AVI")||(FileExt=="avi")){ CString FileName=my.GetPathName(); CMultiDocTemplate *pAVIDocTemplate=pMyApp →m_pAVIDocTemplate; pAVIDocTemplate →OpenDocumentFile(FileName) } else if((FileExt == "BMP") || (FileExt == "bmp")) { CMyApp * p3dlcsApp = (CMyApp *)AfxGetApp(); CMultiDocTemplate *pDATDocTemplate=pMyApp →m_pBMPDocTemplate; pDATDocTemplate ->OpenDocumentFile(FileName); } else{ AfxMessageBox("Yor select a file not supported!");retum } } }2. 切 分 窗 口 与 文 档/ 视 结 构。
一 个 文 档 可 以 有 多 个 视, 切 分 窗 口 即 是 表 示 多 视 的 一 种 方 法。 切 分 窗 口 是 通 过 类CSplitterWnd 来 表 示 的, 对Window 来 说,CSplitterWnd 对 象 是 一 个 真 正 的 窗 口, 它 完 全 占 据 了 框 架 窗 口 的 客 户 区 域, 而 视 窗 口 则 占 据 了 切 分 窗 口 的 窗 片 区 域。 切 分 窗 口 并 不 参 与 命 令 传 递 机 制,( 窗 片 中) 活 动 的 视 窗 从 逻 辑 上 来 看 直 接 被 连 到 了 它 的 框 架 窗 口 中。
切 分 窗 口 可 以 分 为 动 态 和 静 态 两 种。 前 者 较 简 单, 本 文 仅 讨 论 后 者。 创 建 切 分 窗 口 的 步 骤 如 下:
(Step 1): 在 自 己 的 框 架 窗 口 中 声 明 成 员 变 量。
class CMyFrame : public CMDIChildWnd { CSplitterWnd m_Splitter; CSplitterWnd m_Splitter2; } (Step 2): 重 载CMDIChildWnd::OnCreateClient 函 数, 创 建 切 分 窗 口。 BOOL CMyFrame::OnCreateClient (LPCREATESTRUCT lpcs, CCreateContext * pContext) { BOOL btn = m_Splitter.CreateStatic(this,1,2); btn |= m_Splitter.CreateView(0,0, RUNTIME_CLASS(CAVIDispView), m_Splitter2.CreateStatic( &m_Splitter,2,1, WS_CHILD|WS_VISIBLE|WS_BORDER, m_Splitter.IdFromRowCol(0,1)) btn |= m_Splitter2.CreateView (0, 0, RUNTIME_CLASS(CBMPView),CSize(100,100),pContext); btn |= m_Splitter2.CreateView (1, 0, RUNTIME_CLASS(CAVIView), CSize(100,100),pContext); return btn; }CFrameWnd::OnCreateClient 函 数 原 形 为:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CcreateContext * pContext);
缺 省 的CMDIChildWnd::OnCreateClient 函 数 根 据pContext 参 数 提 供 的 信 息, 调 用CFrameWnd::CreateView 函 数 创 建 一 个 视。 可 以 重 载 该 函 数, 加 载CCreateContext 对 象 中 传 递 的 值, 或 改 变 框 架 窗 口 主 客 户 区 中 控 制 的 创 建 方 式。