在VC 中 处 理 动 态 菜 单 消 息 的 一 种 方 法

转自:http://www2.ccw.com.cn/tips/9901/01303_03.asp

      Windows 应 用 程 序 的 菜 单 可 以 分 成 两 大 类: 程 序 窗 口 上 方 菜 单 条 上 的 下 拉 式 菜 单 和 可 以 在 任 意 位 置 显 示 的 弹 出 式 菜 单。 用MFC 建 立 下 拉 菜 单 的 方 法 基 本 上 是 相 同 的, 但 建 立 弹 出 式 菜 单 的 方 法 有 很 多。 其 中 较 常 用 的 是:

  1. 说 明 一 个CMenu 对 象;
  2. 用LoadMenu 成 员 函 数 装 入 菜 单 资 源;
  3. 用TrackPopupMenu 在 指 定 位 置 显 示 菜 单 并 发 送 所 选 菜 单 项 的ID;
  4. 用DestroyMenu 销 毁 菜 单 释 放 资 源。

      在 某 些 应 用 程 序 中, 只 有 在 程 序 运 行 时 才 能 根 据 情 况 确 定 菜 单 项 的 内 容 和 菜 单 项 的 数 目。 这 样 就 无 法 在 编 程 时 建 立 菜 单 资 源, 也 就 不 能 象 上 面 所 述 的 那 样 用LoadMenu 装 入 资 源。 需 要 用 另 一 种 方 法 建 立 菜 单:

  1. 说 明 一 个CMenu 对 象;
  2. 用CreatePopupMenu 建 立 一 个 空 菜 单, 再 用InsertMenu 或AppendMenu 加 入 菜 单 项;
  3. 用TrackPopupMenu 在 指 定 位 置 显 示 菜 单 并 发 送 所 选 菜 单 项 的ID;
  4. 用DestroyMenu 销 毁 菜 单 释 放 资 源。

      虽 然 这 种 方 法 可 以 动 态 地 建 立 菜 单, 但 是 能 够 向 动 态 菜 单 中 加 入 的 菜 单 项 被 限 制 在 一 个 有 限 的 范 围 内, 并 且 必 须 在 编 程 时 就 确 定 它 们 的ID。 这 是 因 为 按 照MFC 常 规 的 菜 单 处 理 机 制, 每 个 菜 单 项 都 要 有 一 个 处 理 函 数, 用MFC 的 消 息 映 射 使 每 个 菜 单ID 对 应 到 相 应 的 处 理 函 数, 从 而 建 立 菜 单 项 与 处 理 函 数 的 对 应 关 系。 用 户 选 中 某 一 菜 单 项, 就 会 触 发 相 应 的 处 理 函 数。 这 样 一 来, 我 们 必 须 在 编 程 时 就 知 道 所 有 的 菜 单 项, 它 们 的ID 与 对 应 的 处 理 函 数, 并 用 消 息 映 射 静 态 地 一 一 建 立 对 应 关 系。

      在 设 计 程 序 时, 确 实 可 能 出 现 菜 单 项 数 目 无 法 限 制, 菜 单ID 事 先 不 能 确 定 的 情 况。 例 如, 一 个 应 用 程 序 有 两 个 文 档 类A 与B, 每 个 文 档 都 可 以 同 时 打 开 任 意 次。 我 们 希 望 在 一 个 动 态 菜 单 中, 只 列 出 显 示A 类 文 档 的 窗 口 标 题, 另 一 个 动 态 菜 单 中 只 列 出 显 示B 类 文 档 的 窗 口 标 题。 当 选 定 一 个 菜 单 项 时, 对 应 的 文 档 窗 口 显 示 在 最 上 面。 由 于 我 们 不 能 限 制 打 开 的 窗 口 数 目, 也 不 能 预 知 窗 口 标 题, 所 以 就 无 法 确 定 应 该 编 写 多 少 处 理 函 数 以 及 会 有 哪 些 菜 单ID, 当 然 也 就 无 法 用 消 息 映 射 建 立 对 应 关 系。 这 时, 我 们 需 要 一 种 不 使 用 消 息 映 射 的 方 法。 但 是, 不 使 用 消 息 映 射 我 们 如 何 知 道 用 户 选 中 了 哪 一 个 菜 单 项 呢 ? 答 案 是CMenu 类 的 成 员 函 数TrackPopupMenu 可 以 告 诉 我 们。

      我 们 可 以 从 手 册 或 联 机 帮 助 中 找 到 关 于 该 函 数 的 介 绍:

BOOL TrackPopupMenu(UINT nFlag,int x,int
       y,CWnd* pWnd,LPCRECT lpRect=NULL);

---- 参 数nFlag 用 于 指 定 弹 出 式 菜 单 的 位 置 标 志 与 有 效 鼠 标 键。 可 以 取 的 值 为 下 列 的 一 个 或 多 个:

TPM_CENTERALIGN,TPM_LEFTALIGN,TPM_RIGHTALIGN,
      TPM_LEFTBUTTON,TPM_RIGHTBUTTON

---- 参 数x,y 为 弹 出 式 菜 单 的 位 置, 参 数pWnd 为 父 窗 口 指 针。

---- 该 函 数 在 指 定 位 置 显 示 一 个 弹 出 式 菜 单, 并 发 送 以 选 中 菜 单 项ID 标 识 的 消 息。 执 行 成 功 返 回 非 零 值, 否 则 返 回 零。

---- 这 并 不 是 关 于TrackPopupMenu 的 全 部 信 息。 该 函 数 至 少 有 两 个 重 要 特 性 没 有 列 入 文 档, 而 它 们 对 于 解 决 我 们 的 问 题 是 至 关 重 要 的。 第 一 个 未 写 入 文 档 的 特 性 是: 该 函 数 的 第 一 个 参 数nFlag 还 可 以 取 另 外 两 个 值:TPM_ NONOTIFY 与TPM_RETURNCMD。 从 它 们 的 名 字 可 以 看 出 来,TPM_ NONOTIFY 的 作 用 是 使TrackPopupMenu 函 数 不 发 送 菜 单 消 息 通 知 应 用 程 序。TPM_RETURNCMD 的 作 用 是 使 函 数 将 本 该 发 送 出 去 的 菜 单 消 息 作 为 返 回 值 返 回。 这 时 函 数 的 返 回 值 是UINT 类 型 的, 就 是 选 中 的 菜 单ID。 这 就 是 该 函 数 第 二 个 未 写 入 文 档 的 特 性

---- 下 面, 我 们 通 过 一 个 例 子 详 细 解 释TrackPopupMenu 的 这 种 用 法。 这 个 例 子 解 决 了 上 面 提 到 的 选 择 两 类 文 档 窗 口 的 问 题, 为 了 简 捷, 只 给 出 了 一 个 关 键 的 函 数PopupAMenuAndDisplay(int x,int y,CString sDocType)。

---- 该 函 数 的 前 两 个 参 数 给 出 了 菜 单 的 位 置, 第 三 个 参 数 是 文 档 的 类 型 名。 函 数 根 据 类 型 名, 找 到 所 有 打 开 的 该 类 文 档。 将 文 档 窗 口 标 题 加 入 弹 出 式 菜 单, 同 时 将 指 向 这 个 文 档 的 指 针 加 入 指 针 数 组ptrDoc。 这 时 要 动 态 地 分 配 给 菜 单 项 一 个ID, 本 函 数 的 菜 单ID 从1 开 始, 依 次 增 加 下 去。 注 意 菜 单ID 不 能 从 零 开 始, 不 然 当 用 户 选 择 菜 单 的 第 一 项 与 一 项 也 未 选 时 函 数 的 返 回 值 都 是 零。

---- 接 着 是 关 键 的 一 步: 用 函 数TrackPopupMenu 显 示 菜 单。 根 据 该 函 数 的 参 数 设 置, 如 果 函 数 返 回 零, 表 示 用 户 未 选 中 任 何 菜 单 项。 否 则, 函 数 返 回 选 中 的 菜 单 项ID。 实 际 上 就 是 选 中 的 文 档 窗 口 对 应 的 指 针 在 数 组ptrDoc 中 的 下 标 加1。 剩 下 的 事 情 就 很 简 单 了。 函 数 返 回 值 大 于 零 时, 从ptrDoc 中 取 得 选 中 的 文 档 类 指 针。 从 文 档 类 可 以 得 到 视 类, 再 从 视 类 得 到 框 架 类, 最 后 激 活 该 框 架 就 达 到 了 要 求。

---- 无 论 用 户 打 开 了 多 少 文 档 窗 口PopupAMenuAndDisplay 都 可 以 正 确 地 处 理。

---- 下 面 就 是 该 函 数 的 具 体 实 现:

void  CMainFrame::PopupAMenuAndDisplay
      (int x,int y,CString sDocType)
{
	CMenu menu;		//说明一个菜单实例
	menu.CreatePopupMenu();	//建立一个动态弹出式菜单
	CPtrArray ptrDoc;		//文档指针数组
	int nMenuID=1;			//菜单ID
	POSITION curTemplatePos = AfxGetApp()->GetFirstDocTemplatePosition();	//取得文档模板
	while(curTemplatePos != NULL){CDocTemplate* curTemplate = AfxGetApp()- >GetNextDocTemplate(curTemplatePos); CString str;
	    curTemplate- >GetDocString(str, CDocTemplate::docName);
	    if(str == sDocType){	//寻找指定模板
		  CDocument* pDoc;
		  POSITION docPos=curTemplate->GetFirstDocPosition();  	//取该模板类的所有文档实例
		  while(docPos!=NULL&&(pDoc=curTemplate->GetNextDoc(docPos))! =NULL){    //在动态菜单中加入一个菜单项
		 	 menu.AppendMenu(MF_STRING|MF_ENABLED, nMenuID++,pDoc->GetTitle());
                            ptrDoc.Add(pDoc);	//文档指针加入数组
		  }
	   }
	}
	
//请注意TrackPopupMenu的这种用法
	int nSelection=menu.TrackPopupMenu
    (TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL|
      TPM_NONOTIFY|TPM_RETURNCMD, x,y, this);
	menu.DestroyMenu(); //销毁菜单
	if(nSelection >0){	//如果选择了菜单中的某一项
		//从Doc找到对应的视
		POSITION viewPos=((CDocument*)
      ptrDoc[nSelection-1])- >GetFirstViewPosition();
		CView* pView=((CDocument*)ptrDoc[nSelection-1])-
      >GetNextView(viewPos);
		//从视找到活动窗口并激活它
		MDIActivate(pView- >GetParentFrame());	
	}
}

通 过 这 个 例 子 可 以 看 到 这 种 方 法 的 特 点 是 不 使 用 消 息 映 射, 而 是 直 接 从TrackPopupMenu 的 返 回 值 中 取 得 菜 单 消 息 进 行 处 理。 它 的 优 点 是 允 许 动 态 地 指 定 菜 单 项 与 菜 单ID, 因 此 具 有 最 大 的 灵 活 性。 当 然, 这 需 要 使 用TrackPopuoMenu 函 数 未 写 入 文 档 的 两 个 特 性。 不 过, 我 们 不 必 担 心 微 软 会 在 将 来 的API 中 去 掉 这 些 特 性。 因 为 这 样 恐 怕 也 将 使 它 的 一 些 老 程 序 的 弹 出 式 菜 单 发 生 问 题。

你可能感兴趣的:(windows,String,api,null,文档,mfc)