使用 WTL 的 Toolband (IE 工具栏) 示例






4.92/5 (25投票s)
2001年8月27日
6分钟阅读

1899574

10829
使用 WTL 为 Internet Explorer 创建您自己的 Toolband
鸣谢
本模块基于 ATL DeskBand Wizard 文章,该文章由 Erik Thompson 创建。感谢他提供了一个出色的向导,让您无需处理 DeskBand 的繁琐细节。
请注意,本项目不使用 MFC。对于所有希望在工具栏中使用 MFC 的 MFC 硬核用户,我建议您从微软的 MSDN 站点下载 KKBar 示例。
创建 ToolBand 模块
您需要安装 ATL DeskBand Wizard 才能创建 ToolBands。请遵循本文章中的说明。
启动 Tool band 项目的最佳方法是使用 ATL COM 应用程序向导。COM 对象必须是进程内服务器,因此请选择“DLL”作为服务器类型。其余选项可以保留其默认设置。(注意:本项目不使用 MFC,因为它基于 ATL 和 WTL)
拥有 ATL COM 项目后,您可以使用 ATL DeskBand Wizard 来创建初始 Tool Band。
添加 WTL 支持
您需要 WTL 库,可以从微软站点下载
请参阅 WTL 安装
以下头文件需要添加到 stdafx.h 文件中
atlapp.h atlwin.h atlctrls.h atlmisc.h
创建工具栏
您可以通过资源编辑器以通常的方式创建工具栏。创建工具栏后,您需要动态创建它。CBandToolBarCtrl
继承自 CToolBarCtrl,用于动态创建工具栏。
DWORD dStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT; HWND hWnd = m_wndToolBar.CreateSimpleToolBarCtrl(hWndChild, IDR_TOOLBAR_TEST, FALSE, dStyle);
这是在 SetSite 方法调用的 RegisterAndCreateWindow 函数中完成的。
消息反射
处理工具栏消息的最佳方法是通过“消息反射”让控件自行处理消息。反射消息的最佳方法是创建一个充当工具栏父级的不可见控件。不可见控件会将工具栏消息反射回自身。请参阅 CBandToolBarReflectorCtrl
类,了解将使用的不可见控件。
以下代码显示了用于实现消息反射的父子关系。
BOOL CToolBandObj::RegisterAndCreateWindow() { RECT rectClientParent; ::GetClientRect(m_hWndParent, &rectClientParent); // We need to create an Invisible Child Window using the Parent Window, // this will also be used to reflect Command // messages from the rebar HWND hWndChild = m_wndInvisibleChildWnd.Create(m_hWndParent,
rectClientParent, NULL, WS_CHILD); // Now we can create the Tool Bar, using the Invisible Child DWORD dStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT; HWND hWnd = m_wndToolBar.CreateSimpleToolBarCtrl(hWndChild,
IDR_TOOLBAR_TEST, FALSE, dStyle); m_wndToolBar.SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); m_wndToolBar.m_ctlBandEdit.m_pBand = this; return ::IsWindow(m_wndToolBar.m_hWnd); }
以下宏用于区分反射消息和普通消息。例如,WM_COMMAND 反射的消息会返回为 OCM_COMMAND。
#define OCM_COMMAND_CODE_HANDLER(code, func) \ if(uMsg == OCM_COMMAND && code == HIWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define OCM_COMMAND_ID_HANDLER(id, func) \ if(uMsg == OCM_COMMAND && id == LOWORD(wParam)) \ { \ bHandled = TRUE; \ lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \ if(bHandled) \ return TRUE; \ } #define OCM_NOTIFY_CODE_HANDLER(cd, func) \ if(uMsg == OCM_NOTIFY && cd == ((LPNMHDR)lParam)->code) \ { \ bHandled = TRUE; \ lResult = func((int)wParam, (LPNMHDR)lParam, bHandled); \ if(bHandled) \ return TRUE; \ }
浏览器导航
为了在浏览器中导航,您需要实例化 IWebBrowser2 COM 对象。这通常在 SetSite 方法中完成,例如:
IServiceProviderPtr pServiceProvider = pUnkSite; if (_Module.m_pWebBrowser) _Module.m_pWebBrowser = NULL; if(FAILED(pServiceProvider->QueryService(SID_SWebBrowserApp, IID_IWebBrowser, (void**)&_Module.m_pWebBrowser))) return E_FAIL;
实例化 COM 对象后,您可以使用 navigate 方法转到您的 URL。
_variant_t varURL = _bstr_t("www.codeproject.com");
_variant_t varEmpty;
_Module.m_pWebBrowser->Navigate2(&varURL, &varEmpty, &varEmpty,
&varEmpty, &varEmpty);
拖放编辑和组合框控件
CBandEditCtrl 类继承自具有拖放功能的 WTL CEdit 控件。即,您可以将文本从浏览器直接拖放到 CEdit 控件。
CBandComboBoxCtrl 类继承自具有拖放功能的 WTL CComboBox 控件。即,您可以将文本从浏览器直接拖放到 CComboBox 控件。
可配置的工具栏按钮样式
CBandToolBarCtrl 类允许您在工具栏上拥有以下按钮样式:
- 图像和文本在右侧
- 图像和文本在下方
- 仅图像
弹出菜单跟踪
弹出菜单用于访问配置选项。
工具提示
这取自 MSDN,用于解释为何需要自行处理工具栏的工具提示。
对于派生自 CFrameWnd 的父窗口中包含的按钮和其他控件,工具提示会自动显示。这是因为 CFrameWnd 为 TTN_GETDISPINFO 通知提供了一个默认处理程序,该处理程序会处理与控件关联的工具提示控件的 TTN_NEEDTEXT 通知。
但是,当 TTN_NEEDTEXT 通知由与不在 CFrameWnd 中的控件关联的工具提示控件发送时,不会调用此默认处理程序,例如对话框或窗体视图中的控件。因此,您必须为 TTN_NEEDTEXT 通知消息提供一个处理函数,以便为子控件显示工具提示。
在 WTL 中,可以通过在重写的工具栏控件中使用以下消息处理程序轻松完成此操作:
NOTIFY_CODE_HANDLER(TTN_NEEDTEXT, OnToolbarNeedText)
以下代码是从资源加载工具提示并设置工具提示文本。
LRESULT CBandToolBarCtrl::OnToolbarNeedText(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled) { CString sToolTip; //-- make sure this 1is not a seperator if (idCtrl != 0) { if (!sToolTip.LoadString(idCtrl)) { bHandled = FALSE; return 0 } } LPNMTTDISPINFO pttdi = reinterpret_cast<LPNMTTDISPINFO> (pnmh);pttdi->lpszText = MAKEINTRESOURCE(idCtrl); pttdi->hinst = _Module.GetResourceInstance(); pttdi->uFlags = TTF_DI_SETITEM; //-- message processed return 0; }
更新状态栏
您可以使用浏览器方法 put_StatusText 更新状态栏,此方法通常可在 WM_MENUSELECT 事件中使用。
以下代码从资源加载菜单文本并在浏览器状态栏上显示它。
LRESULT CBandToolBarCtrl::OnMenuSelect(UINT /*uMsg*/,
WPARAM wParam, LPARAM lParam, BOOL& bHandled) { WORD nID = LOWORD(wParam); WORD wFlags = HIWORD(wParam); //-- make sure this is not a seperator CString sStatusBarDesc; if ( !(wFlags & MF_POPUP) ) { if (nID != 0) { if (!sStatusBarDesc.LoadString(nID)) { bHandled = FALSE; return 0; } int nPos = sStatusBarDesc.Find(_T("\n")); if (nPos != -1) { sStatusBarDesc = sStatusBarDesc.Left(nPos+1); _Module.m_pWebBrowser-> put_StatusText(_bstr_t(sStatusBarDesc)); return 0; } } } return 0; }
如何在您的工具栏添加一个箭头
要在您的工具栏添加一个箭头,您需要在 DBIM_MODEFLAGS 掩码下的 GetBandInfo 方法中添加 DBIMF_USECHEVRON 标志。
... if(pdbi->dwMask == DBIM_MODEFLAGS) { //AddChevron pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT | DBIMF_USECHEVRON | DBIMF_BREAK; } ...
这基本上添加了显示箭头的能力。如果您想在工具栏上看到它出现,那么您必须确保 pdbi->ptMinSize.x 值小于 pdbi->ptActual.x 值(同样,这些值可以在 GetBandInfo 方法中设置)。
要处理箭头菜单中按钮的事件,您必须子类化托管您的工具栏的 rebar 控件。已使用以下步骤子类化 rebar 控件:
1. 查找 rebar 控件 - 这可以通过获取浏览器的窗口句柄并搜索其所有子窗口来完成,直到找到 rebar 控件。
2. 找到 rebar 控件后,您可以使用 ATL CContainedWindow 简单地对其进行子类化。
BOOL CBandToolBarCtrl::SetBandRebar() { HWND hWnd(NULL); _Module.m_pWebBrowser-> get_HWND((long*)&hWnd); if (hWnd == NULL) return FALSE; m_ctlRebar.m_hWnd = FindRebar(hWnd); if (m_ctlRebar.m_hWnd == NULL) return FALSE; m_RebarContainer.SubclassWindow(m_ctlRebar); return TRUE; }
子类化窗口后,事件应该会传递到工具栏类。
附加到浏览器上下文菜单
我曾试图复制 Google 和 CodeProject 的工具栏右键浏览器上下文菜单搜索,但没有成功,直到我查看了二进制资源,我才发现您可以通过在注册表中添加选项来扩展 Internet Explorer 菜单。通过在您的 .rgs 文件中添加一个条目,可以轻松实现这一点。
HKCU { NoRemove Software { NoRemove Microsoft { NoRemove 'Internet Explorer' { NoRemove MenuExt { ForceRemove '&Sample Toolband Serach' = s
'res://%MODULE%/MENUSEARCH.HTM' { val Contexts = b '10' } } } } } }
请注意注册表项的值(这是一个存在于模块资源中的 HTML 脚本)。
请参阅模块资源下的 HTML 中的 MENUSERACH.HTM,了解脚本的功能。
历史
-
版本 1 [2001年8月23日] - 创建
-
版本 2 [2001年9月4日] - 添加了工具提示代码
-
版本 2.1 [2001年9月23日] - 父子 WebBrowser 修复
-
版本 2.2 [2002年1月13日] - 小幅重写,修复内部编译器错误问题,Unicode 兼容,状态栏示例,箭头示例,工具提示更改
-
版本 2.3 [2002年1月15日] - 错误修复,链接错误修复,展示如何禁用、启用按钮,右键上下文菜单
-
版本 2.4 [2002年1月29日] - 实际父子浏览器修复,v2.1 修复无效
-
版本 2.5 [2002年10月29日] - 错误修复,添加了组合框示例,箭头宽度计算