{A}
简介
这是一个特殊的工具栏辅助模板类,您可以使用WTL框架窗口的基于应用程序实现以下:将文本添加到工具栏按钮添加下拉菜单的工具栏按钮组合框添加到工具栏
这里给出的代码使得它很容易显示专业外观的工具栏的任何WTL CFrameWnd的派生窗口,我希望,解释它是如何的作品也证明有用的。入门
首先,你需要包含头文件,最好是在你为MainFrm.h:#include "ToolBarHelper.h"
下一步,推导出你的窗口CToolBarHelper类(可能的CMainFrame):{C}
接下来,如果你是想工具栏的下拉菜单/组合框,你需要添加一个CHAIN_MSG_MAP进入你的消息映射,以确保正确处理CBN_SELCHANGE和TBN_DROPDOWN消息:BEGIN_MSG_MAP(CMainFrame)
...
CHAIN_MSG_MAP(CToolBarHelper<CMainFrame>)
END_MSG_MAP()
这可能是一个好主意,反正这样做,如果你想添加drop-downs/comboboxes后。CString的,等等。
请注意,代码使用的CString,CRECT,CSIZE类。这意味着,如果你是在Visual Studio 6编译,您将需要#包括LT; atlmisc.hgt,而Visual Studio 2005的用户可能要使用新的atlstr.h和atltypes.h头。 atlctrls.h文件也是必不可少的,但WTL的类向导默认情况下添加到stdafx.h中。
接下来,添加相关的魔法你的CMainFrame::OnCreate函数来调剂您的工具栏。工具栏按钮添加文本
将文本添加到工具栏按钮,您首先需要使用ATL_SIMPLE_TOOLBAR_PANE_STYLE_EX而不是ATL_SIMPLE_TOOLBAR_PANE_STYLE,以确保您的工具栏创建。如果你忘了这一步,那么文本将不会出现在工具栏上按钮。只需更换这一行:HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME,
FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);
与此:HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE,
ATL_SIMPLE_TOOLBAR_PANE_STYLE_EX);
注意,ATL_SIMPLE_TOOLBAR_PANE_STYLE_EX是一个简单的组合ATL_SIMPLE_TOOLBAR_PANE_STYLE和神奇的工具栏TBSTYLE_LIST风格(确保按钮上的文字会显示)。
现在,所有你需要做的的是将文本添加到相关的按钮,使用下列方法之一:AddToolbarButtonText(HWND hWndToolBar,UINT的NID,LPCTSTR lpsz)
使用此方法来设置工具栏按钮的文本直接从一个字符串,例如:AddToolbarButtonText(hWndToolBar, ID_APP_ABOUT, _T("About"));
只需通过工具栏的窗口句柄(CreateSimpleToolBarCtrl返回一个),你想改变按钮的ID,和您要添加的文字。AddToolbarButtonText(hWndToolBar的HWND,UINT,UINT的NID nStringID)
使用这种方法,从指定的字符串资源添加文本。例如:
这将载入IDS_TOOLBAR_TEXT字符串和文本分配到指定的工具栏按钮(在这种情况下,ID_EDIT_PASTE)。
最后,您可以设置工具栏按钮的文本已经被分配到一个按钮的工具提示使用下列方法:AddToolbarButtonText(HWND hWndToolBar,UINT的NID)
此方法将试图加载字符串相同的ID按钮,寻找工具提示文本,并将其分配给按钮。例如:AddToolbarButtonText(hWndToolBar, ID_FILE_SAVE);
这将负载与编号ID_FILE_SAVE,看起来像这样的字符串:Save the active document\nSave (Ctrl+S)
工具提示文本,可以发现后,立即\ N。现在,你可以看到,快捷键已硬连线成工具提示文本,以便AddToolbarButtonText将寻找首个开放式支架下面的\ n只使用之间的文本。因此,在这个例子中,ID_FILE_SAVE编号的工具栏按钮将设置为文本保存。
够简单。引擎盖下
将文本添加到工具栏按钮,需要以下步骤:确保工具栏TBSTYLE_EX_MIXEDBUTTONS样式位。调用CToolBarCtrl::AddStrings方法添加按钮上的文字工具栏的内部列表。更改按钮的信息,以确保TBSTYLE_AUTOSIZE BTNS_SHOWTEXT位被设置。删除,然后重新插入工具栏按钮。添加下拉菜单的工具栏按钮
为了增加一个下拉菜单,工具栏按钮,你首先需要使用Visual Studio资源编辑器创建菜单。然后,只需添加一个调用下面的方法的CMainFrame::OnCreate中:AddToolBarDropDownMenu(hWndToolBar的HWND,nButtonID UINT,UINT nMenuID)
指定工具栏的窗口句柄(再次,一个由CreateSimpleToolBarCtrl返回),将被分配的下拉按钮,按钮的ID,你要显示的按钮被点击时的菜单的ID。很简单。菜单命令,将被路由到主框架窗口是正常的,如果您有分配按钮图像时,就会出现(见本演示示例程序)。
如果你想立即修改菜单显示之前(例如,添加新项目),然后重写以下虚函数:virtual void PrepareToolBarMenu(UINT nMenuID, HMENU hMenu);
此功能称为CToolBarHelper之前显示一个菜单,并通过大约是要显示的菜单的ID,和菜单句柄,这将允许你修改菜单内容。
例如,示例程序演示如何加入一个分隔符和两个新的菜单项是附加到文件的IDR_NEW菜单|新的工具栏按钮:void CMainFrame::PrepareToolBarMenu(UINT nMenuID, HMENU hMenu)
{
if (nMenuID == IDR_NEW)
{
CMenuHandle menu(hMenu);
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, ID_NEW_DOCUMENT, _T("Document..."));
menu.AppendMenu(MF_STRING, ID_NEW_TEMPLATE, _T("Template..."));
}
}
很简单的东西。引擎盖下
添加到工具栏按钮的下拉菜单中包括以下步骤:确保按钮本身分配的TBSTYLE_EX_DRAWDDARROWS风格。处理TBN_DROPDOWN通知消息。显示菜单使用的CMainFrame::m_CmdBar.TrackPopupMenu,以确保菜单显示凉爽每个项目旁边的小按钮,如果分配。
请注意,一个CSimpleMap是用来与菜单ID映射工具栏按钮的ID。已经很多年这个非常简单的地图类的ATL的一部分,不应该给你的任何问题。我的STL的大用户,所以通常会使用std::地图,但CSimpleMap这个非常简单的目的,是完全足够的。将组合框添加到工具栏
要到工具栏上添加一个ComboBox,首先你需要添加一个按钮到工具栏,作为一个占位符的行为。只需添加一个新的按钮,使用Visual Studio工具栏编辑器,并确保它有一个ID - 没有图像。接下来,创建组合框,调用下面的方法:从CMainFrame中:OnCreate中:CreateToolbarComboBox(HWND hWndToolBar,UINT NID,UINT nWidth,UINT nHeight,DWORD dwComboStyle)
您需要提供的最低参数的是工具栏的窗口句柄(再次,一个由CreateSimpleToolBarCtrl返回)和特殊的占位符"按钮添加到您的工具栏的ID。组合框的宽度,高度和样式将默认为16,16和CBS_DROPDOWNLIST,(注意宽度/高度指定的字符,而不是像素,并且只应该用来为指导,为不同口味的Windows /视觉样式,可能会导致组合框,确定自己的身高)。
CreateToolbarComboBox返回新创建的组合框,然后你就可以使用添加项目的HWND。例如,要添加一个ComboBox包含三个项目:CComboBox combo = CreateToolbarComboBox(hWndToolBar, ID_COMBO_PLACEHOLDER);
combo.AddString(_T("Item 1"));
combo.AddString(_T("Item 2"));
combo.AddString(_T("Item 3"));
CToolBarHelper,当一个ComboBox项目被选中,将调用下面的函数,你必须添加到您的CMainFrame类:void OnToolBarCombo(HWND hWndCombo, UINT nID, int nSel,
LPCTSTR lpszText, DWORD dwItemData);
这是通过组合框的窗口句柄,按钮(例如,ID_COMBO_PLACEHOLDER),新选定的ComboBox项,项目文本和项目数据的索引编号。示例程序显示了这一行动。ComboBox控件的UI更新
如果你想要一个工具栏组合框,可能需要在项目的选择要更新的基础上一些程序的状态,然后你应该做以下内容:您的CMainFrame类中添加一个CComboBox成员。分配一个CreateToolbarComboBox调用返回的HWND的CComboBox。在您的CMainFrame::ONIDLE方法,改变ComboBox中选择相应的。
例如,在您为MainFrm.h:
CComboBox m_wndCombo;
然后,在CMainFrame::OnCreate中:m_wndCombo = CreateToolbarComboBox(hWndToolBar, ID_COMBO_PLACEHOLDER);
m_wndCombo.AddString(...);
在CMainFrame::的OnIdle:if (GetFocus() != m_wndCombo)
{
// Check combo selection is accurate
}
注意ONIDLE代码更新ComboBox的第一检查ComboBox中没有重点,这就避免了一些潜在的闪烁。
示例程序演示了这一行动。引擎盖下
将控件添加到工具栏已被一些人想办以来的第一个共同的工具栏控件,WIN95发布,此标准的工程实践像这样:更改占位按钮是一个分离器,由于设计怪癖,分离器的宽度是可以改变的。更改分离器的宽度相匹配,您要建立的控制。创建控件的工具栏控件的父,使用分隔按钮RECT的大小。
这工作得很好各种风味多年的框架应用程序,方法是我在写这篇文章时选择。不过,我第一次尝试看起来像这样:
问题是,如果你是使用Windows XP视觉样式,工具栏分隔符不再是一个空的空间,因为它是在经典模式下运行时Win9x/Win2000 - 相反,绘制一条垂直线。正如你可以看到从上面的图片,这看起来有点奇怪。起初,我认为这是要解决一个讨厌的和复杂的NM_CUSTOMDRAW劈,一场噩梦。人们改变占位符"按钮是一个分隔符的原因是因为他们可以任意的宽度,而普通按钮的宽度是固定的... ...是什么呢?当然不是 - 文字的按钮,可以明显改变宽度相匹配的文本,我提供以下解决方案:更改的占位符按钮,BTNS_SHOWTEXT的样式。确保被禁用按钮的状态。如果不是,然后徘徊在鼠标下面的ComboBox中会导致要绘制一个幽灵"按钮。
另一个小问题是,ComboBox的高度可能比工具栏本身(特别是如果像我这样的,你运行启用大字体)小了不少,所以我也加入了一些代码中心的IT。
最后一点是为组合框使用的字体。起初,我只是用系统GUI股票字体,但略有不同,这是用来显示工具栏按钮上的文字字体。一些周围挖掘后,会出现菜单字体,所以我使用SystemParametersInfo和SPI_GETNONCLIENTMETRICS组合得到的LOGFONT,所以我可以创建一个使用与组合框的副本。ToolBarHelper.h
下面是在充分CToolBarHelper:
示例应用程序#pragma once
// Define various toolbar button styles in case they are missing
#ifndef TBSTYLE_EX_MIXEDBUTTONS
#define TBSTYLE_EX_MIXEDBUTTONS 0x00000008
#endif
#ifndef BTNS_SHOWTEXT
#define BTNS_SHOWTEXT 0x0040
#endif
#define ATL_SIMPLE_TOOLBAR_PANE_STYLE_EX
(ATL_SIMPLE_TOOLBAR_PANE_STYLE|TBSTYLE_LIST)
/// Class used to expost useful toolbar
/// functionality to a WTL CFrameWnd-derived class
template <class T>
class CToolBarHelper
{
private:
/// Wrapper class for the Win32 TBBUTTONINFO structure.
class CTBButtonInfo : public TBBUTTONINFO
{
public:
/// Constructor
CTBButtonInfo(DWORD dwInitialMask = 0)
{
memset(this, 0, sizeof(TBBUTTONINFO));
cbSize = sizeof(TBBUTTONINFO);
dwMask = dwInitialMask;
}
};
/// Wrapper class for the Win32 TBBUTTON structure.
class CTBButton : public TBBUTTON
{
public:
/// Constructor
CTBButton()
{
memset(this, 0, sizeof(TBBUTTON));
}
};
private:
CFont m_fontCombo; ///< Font to use for comboboxes
CSimpleMap<UINT, UINT> m_mapMenu; ///< Map of command IDs -> menu IDs
public:
/// Message map
BEGIN_MSG_MAP(CToolbarHelper<T>)
COMMAND_CODE_HANDLER(CBN_SELCHANGE, OnSelChangeToolBarCombo)
NOTIFY_CODE_HANDLER(TBN_DROPDOWN, OnToolbarDropDown)
END_MSG_MAP()
/// Modify a toolbar button to have a drop-down button
void AddToolBarDropDownMenu(HWND hWndToolBar, UINT nButtonID, UINT nMenuID)
{
ATLASSERT(hWndToolBar != NULL);
ATLASSERT(nButtonID > 0);
// Use built-in WTL toolbar wrapper class
CToolBarCtrl toolbar(hWndToolBar);
// Add the necessary style bit (TBSTYLE_EX_DRAWDDARROWS) if
// not already present
if ((toolbar.GetExtendedStyle() & TBSTYLE_EX_DRAWDDARROWS)
!= TBSTYLE_EX_DRAWDDARROWS)
toolbar.SetExtendedStyle(toolbar.GetExtendedStyle() |
TBSTYLE_EX_DRAWDDARROWS);
// Get existing button style
CTBButtonInfo tbi(TBIF_STYLE);
if (toolbar.GetButtonInfo(nButtonID, &tbi) != -1)
{
// Modify the button
tbi.fsStyle |= TBSTYLE_DROPDOWN;
toolbar.SetButtonInfo(nButtonID, &tbi);
// We need to remember that this menu
// ID is associated with the button ID
// so use a basic map for this.
m_mapMenu.Add(nButtonID, nMenuID);
}
}
LRESULT OnToolbarDropDown(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
{
// Get the toolbar data
NMTOOLBAR* ptb = reinterpret_cast<NMTOOLBAR*>(pnmh);
// See if the button ID has an asscociated menu ID
UINT nMenuID = m_mapMenu.Lookup(ptb->iItem);
if (nMenuID)
{
// Get the toolbar control
CToolBarCtrl toolbar(pnmh->hwndFrom);
// Get the button rect
CRect rect;
toolbar.GetItemRect(toolbar.CommandToIndex(ptb->iItem), &rect);
// Create a point
CPoint pt(rect.left, rect.bottom);
// Map the points
toolbar.MapWindowPoints(HWND_DESKTOP, &pt, 1);
// Load the menu
CMenu menu;
if (menu.LoadMenu(nMenuID))
{
CMenuHandle menuPopup = menu.GetSubMenu(0);
ATLASSERT(menuPopup != NULL);
T* pT = static_cast<T*>(this);
// Allow the menu items to be initialised (for example,
// new items could be added here for example)
pT->PrepareToolBarMenu(nMenuID, menuPopup);
// Display the menu
// Using command bar TrackPopupMenu method means menu icons are displayed
pT->m_CmdBar.TrackPopupMenu(menuPopup,
TPM_RIGHTBUTTON|TPM_VERTICAL, pt.x, pt.y);
}
}
return 0;
}
/// Override this Allow the menu items to be enabled/checked/etc.
virtual void PrepareToolBarMenu(UINT /*nMenuID*/, HMENU /*hMenu*/)
{
}
/// Add text to a toolbar button
void AddToolbarButtonText(HWND hWndToolBar, UINT nID, LPCTSTR lpsz)
{
// Use built-in WTL toolbar wrapper class
CToolBarCtrl toolbar(hWndToolBar);
// Set extended style
if ((toolbar.GetExtendedStyle() & TBSTYLE_EX_MIXEDBUTTONS)
!= TBSTYLE_EX_MIXEDBUTTONS)
toolbar.SetExtendedStyle(toolbar.GetExtendedStyle() |
TBSTYLE_EX_MIXEDBUTTONS);
// Get the button index
int nIndex = toolbar.CommandToIndex(nID);
CTBButton tb;
toolbar.GetButton(nIndex, &tb);
int nStringID = toolbar.AddStrings(lpsz);
// Alter the button style
tb.iString = nStringID;
tb.fsStyle |= TBSTYLE_AUTOSIZE|BTNS_SHOWTEXT;
// Delete and re-insert the button
toolbar.DeleteButton(nIndex);
toolbar.InsertButton(nIndex, &tb);
}
/// Add resource string to a toolbar button
void AddToolbarButtonText(HWND hWndToolBar, UINT nID, UINT nStringID)
{
CString str;
if (str.LoadString(nStringID))
AddToolbarButtonText(hWndToolBar, nID, str);
}
/// Add text to a toolbar button (using tooltip text)
void AddToolbarButtonText(HWND hWndToolBar, UINT nID)
{
TCHAR sz[256];
if (AtlLoadString(nID, sz, 256) > 0)
{
// Add the text following the '\n'
TCHAR* psz = _tcsrchr(sz, '\n');
if (psz != NULL)
{
// Skip to first character of the tooltip
psz++;
// The tooltip text may include the accelerator, i.e.
// Open (Ctrl+O)
// So look for an open brace
TCHAR* pBrace = _tcschr(psz, '(');
if (pBrace != NULL)
*(pBrace - 1) = '\0';
AddToolbarButtonText(hWndToolBar, nID, psz);
}
}
}
/// Create a combobox on a toolbar
HWND CreateToolbarComboBox(HWND hWndToolBar, UINT nID,
UINT nWidth = 16, UINT nHeight = 16,
DWORD dwComboStyle = CBS_DROPDOWNLIST)
{
T* pT = static_cast<T*>(this);
// Use built-in WTL toolbar wrapper class
CToolBarCtrl toolbar(hWndToolBar);
// Get the size of the combobox font
CreateComboFont();
CSize sizeFont = GetComboFontSize();
// Compute the width and height
UINT cx = (nWidth + 8) * sizeFont.cx;
UINT cy = nHeight * sizeFont.cy;
// Set the button width
CTBButtonInfo tbi(TBIF_SIZE|TBIF_STATE|TBIF_STYLE);
// Make sure the underlying button is disabled
tbi.fsState = 0;
// BTNS_SHOWTEXT will allow the button size to be altered
tbi.fsStyle = BTNS_SHOWTEXT;
tbi.cx = static_cast<WORD>(cx);
toolbar.SetButtonInfo(nID, &tbi);
// Get the index of the toolbar button
int nIndex = toolbar.CommandToIndex(nID);
// Get the button rect
CRect rc;
toolbar.GetItemRect(nIndex, rc);
rc.bottom = cy;
// Create the combobox
DWORD dwStyle = WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_TABSTOP|dwComboStyle;
CComboBox combo;
combo.Create(pT->m_hWnd, rc, NULL, dwStyle, 0, nID);
combo.SetFont(m_fontCombo);
combo.SetParent(toolbar);
// The combobox might not be centred vertically, and we won't know the
// height until it has been created. Get the size now and see if it
// needs to be moved.
CRect rectToolBar;
CRect rectCombo;
toolbar.GetClientRect(&rectToolBar);
combo.GetWindowRect(rectCombo);
// Get the different between the heights of the toolbar and
// the combobox
int nDiff = rectToolBar.Height() - rectCombo.Height();
// If there is a difference, then move the combobox
if (nDiff > 1)
{
toolbar.ScreenToClient(&rectCombo);
combo.MoveWindow(rectCombo.left, rc.top + (nDiff / 2),
rectCombo.Width(), rectCombo.Height());
}
return combo;
}
/// Create the font to use for comboboxes
void CreateComboFont()
{
if (m_fontCombo == NULL)
{
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICS);
::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
// Create menu font
m_fontCombo.CreateFontIndirect(&ncm.lfMenuFont);
ATLASSERT(m_fontCombo != NULL);
}
}
/// Get the size of the default GUI font
CSize GetComboFontSize()
{
ATLASSERT(m_fontCombo != NULL);
// We need a temporary DC
const T* pT = static_cast<const T*>(this);
CClientDC dc(pT->m_hWnd);
// Select in the menu font
CFontHandle fontOld = dc.SelectFont(m_fontCombo);
// Get the font size
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
// Done with the font
dc.SelectFont(fontOld);
// Return the width and height
return CSize(tm.tmAveCharWidth, tm.tmHeight + tm.tmExternalLeading);
}
LRESULT OnSelChangeToolBarCombo(WORD /*wNotifyCode*/, WORD wID,
HWND hWndCtl, BOOL& /*bHandled*/)
{
T* pT = static_cast<T*>(this);
// Get the newly selected item index
CComboBox combo(hWndCtl);
int nSel = combo.GetCurSel();
// Get the item text
CString strItemText;
combo.GetLBText(nSel, strItemText);
// Get the item data
DWORD dwItemData = combo.GetItemData(nSel);
// Call special function to handle the selection change
pT->OnToolBarCombo(combo, wID, nSel, strItemText, dwItemData);
// Set focus to the main window
pT->SetFocus();
return TRUE;
}
};
两个示例应用程序提供 - 为Visual Studio 6,和一个用于Visual Studio 2005。我使用WTL的7.5,但与早期版本,我认为它应该工作。请注意,代码是Unicode标准,并在Visual Studio 2005示例包含Unicode的发布和调试版本。
示例应用程序演示CToolBarHelper所有可用的方法:点击"新建"按钮,下拉显示一个动态修改菜单。选择视图使用的第一个组合框的颜色。通过颜色,单击颜色按钮周期。单击颜色按钮下拉菜单中显示的颜色,包括按钮图标。注意如何改变颜色,将导致的第一个组合框更新。第二个combobox是简单地加在OnCreate从来没有通过的OnIdle更新。保存,粘贴,关于按钮的文本已被添加使用的各种AddToolbarButtonText方法。改进
如果您有任何意见或建议,我可以就如何改善这种代码,那么请随时发布。我有一个想法是添加支持编辑控件添加到工具栏,这应该不会太困难。