VC 2010 + MFC : 在对话框里面加入工具条CMFCToolBar
By:章永辉
VC 2010 + MFC 新库的资料很少,以下给出本人的实现方法。
(1)古典的工具条
(a)对话框中加入CMFCToolBar的成员变量。
CMFCToolBar m_wndToolBar;
(b)创建工具条并显示之
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS |CBRS_FLYBY | CBRS_BORDER_BOTTOM) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("未能创建工具栏\n");
return -1; // 未能创建
}
m_wndToolBar.AdjustLayout();
这句" m_wndToolBar.AdjustLayout();"非常重要,否则,工具条将不显示。
至此,我们看到了XP风格的工具条,本人称之为古典的工具条,——相对Feature Pack界面库而言,这个XP风格的工具条确实是很古老的了。
(2)Feature Pack风格的工具条
通过前面的努力,我们看到了老土的工具条,虽然如此,我们还得继续努力,否则,这样的工具条和Ribbon界面不搭调,有还不如没有。
所以,我继续跟踪,结果显示,是m_bIsDlgControl成员变量在作祟。但是这是保护类型的变量,没法直接修改的,所以,我继承之。
代码:
加上头文件包含。
#include "SegMFCToolBar.h"
将
CMFCToolBar m_wndToolBar;
改为:
CSegMFCToolBar m_wndToolBar;
在
m_wndToolBar.AdjustLayout();
前面加上:
m_wndToolBar.SetIsDlgControl(FALSE);
//SegMFCToolBar.h头文件
#pragma once
// CSegMFCToolBar
class CSegMFCToolBar : public CMFCToolBar
{
DECLARE_DYNAMIC(CSegMFCToolBar)
public:
CSegMFCToolBar();
virtual ~CSegMFCToolBar();
protected:
DECLARE_MESSAGE_MAP()
public:
void SetIsDlgControl(BOOL b)
{
m_bIsDlgControl=b;
}
};
// SegMFCToolBar.cpp : 实现文件
//
#include "stdafx.h"
#include "AdServer.h"
#include "SegMFCToolBar.h"
// CSegMFCToolBar
IMPLEMENT_DYNAMIC(CSegMFCToolBar, CMFCToolBar)
CSegMFCToolBar::CSegMFCToolBar()
{
}
CSegMFCToolBar::~CSegMFCToolBar()
{
}
BEGIN_MESSAGE_MAP(CSegMFCToolBar, CMFCToolBar)
END_MESSAGE_MAP()
至此,完美解决。
20110902补充:
将
m_wndToolBar.AdjustLayout();
改为
m_wndToolBar.AdjustSizeImmediate();
否则,对话框的工具条大小会不正确的。
20110903补充:
虽然如此,但是我们无法响应工具条的事件,也无法处理ON_UPDATE_COMMAND_UI。所以,我们需要加入以下两句代码:
m_wndToolBar.SetOwner(this);
m_wndToolBar.SetRouteCommandsViaFrame(FALSE);
这样,还是不够的!我们还得处理一下WM_IDLEUPDATECMDUI消息。如下:
//消息处理函数的声明
afx_msg LRESULT OnIdleUpdateCmdUI(WPARAM wParam, LPARAM);
//消息映射
ON_MESSAGE(WM_IDLEUPDATECMDUI, &CSegMFCToolBar::OnIdleUpdateCmdUI)
//消息处理函数的实现
LRESULT CSegMFCToolBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
{
// the style must be visible and if it is docked
// the dockbar style must also be visible
if ((GetStyle() & WS_VISIBLE) &&
(m_pParentDockBar == NULL || (m_pParentDockBar->GetStyle() & WS_VISIBLE)))
{
CFrameWnd* pTarget = (CFrameWnd*)GetOwner();
if (pTarget == NULL)
pTarget = AFXGetParentFrame(this);
if (pTarget != NULL)
OnUpdateCmdUI(pTarget, (BOOL)wParam);
}
return 0L;
}
效果图(背景透明的对话框专用工具条):
20110903补充(2):
再次发现问题,工具条的提示信息没有更新到状态栏! 为此,我们再次对MFC标准库进行手术。这次,我们需要重载ShowCommandMessageString虚函数。
函数声明:
void ShowCommandMessageString(UINT uiCmdId);
void SendMessageString(UINT uiTrackId);
函数实现:
static inline BOOL __stdcall IsSystemCommand(UINT uiCmd)
{
return(uiCmd >= 0xF000 && uiCmd < 0xF1F0);
}
void CSegMFCToolBar::ShowCommandMessageString(UINT uiCmdId)
{
if (m_hookMouseHelp != NULL)
{
return;
}
if (uiCmdId == (UINT) -1 || uiCmdId == AFX_CUSTOMIZE_INTERNAL_ID)
{
SendMessageString(AFX_IDS_IDLEMESSAGE);
return;
}
UINT uiTrackId = uiCmdId;
if (IsSystemCommand(uiCmdId))
{
uiTrackId = ID_COMMAND_FROM_SC(uiCmdId);
ASSERT(uiTrackId >= AFX_IDS_SCFIRST && uiTrackId < AFX_IDS_SCFIRST + 31);
}
else if (uiCmdId >= AFX_IDM_FIRST_MDICHILD)
{
// all MDI Child windows map to the same help id
uiTrackId = AFX_IDS_MDICHILD;
}
SendMessageString(uiTrackId);
}
void CSegMFCToolBar::SendMessageString(UINT uiTrackId)
{
CFrameWnd* pTarget=(CFrameWnd*)GetOwner();
if (pTarget!=NULL && pTarget->IsFrameWnd())
{
pTarget->SendMessage(WM_SETMESSAGESTRING, (WPARAM) uiTrackId);
return;
}
pTarget=(CFrameWnd*)AfxGetMainWnd();
if (pTarget!=NULL && pTarget->IsFrameWnd())
{
pTarget->SendMessage(WM_SETMESSAGESTRING, (WPARAM) uiTrackId);
return;
}
}
20110924补充:
经测试发现,工具条上面并没有响应ON_UPDATE_COMMAND_UI,所以,我们还得加入代码。以下代码仅对模式对话框有效,非模式对话框,请自行作修改。
方法一:我们重载一下ContinueModal函数。
virtual BOOL ContinueModal();
BOOL CSegDialogEx::ContinueModal()
{
if (m_Toolbar!=NULL)
{
if( m_Toolbar->IsWindowVisible() )
{
CFrameWnd* pParent = ( CFrameWnd* ) m_Toolbar->GetParent();
if( pParent )
{
m_Toolbar->OnUpdateCmdUI( pParent, ( WPARAM ) TRUE );
}
}
CMenu* pMainMenu = GetMenu();
if (pMainMenu!=NULL)
{
CCmdUI cmdUI;
for (INT n = 0; n < pMainMenu->GetMenuItemCount(); ++n)
{
CMenu* pSubMenu = pMainMenu->GetSubMenu(n);
if (pSubMenu!=NULL)
{
cmdUI.m_nIndexMax = pSubMenu->GetMenuItemCount();
for (UINT i = 0; i < cmdUI.m_nIndexMax;++i)
{
cmdUI.m_nIndex = i;
cmdUI.m_nID = pSubMenu->GetMenuItemID(i);
cmdUI.m_pMenu = pSubMenu;
cmdUI.DoUpdate(this, FALSE);
}
}
}
}
}
return CDialogEx::ContinueModal();
}
即可。
工具条按钮无效时 ,灰色蒙板效果图:
注: 上面的修改会导致对话框刷新出现问题。所以,部分时候,我们需要自行调用RedrawWindow来强制重绘其他控件。其实,也可以不加这个函数,而是,在需要的时候,调用一下工具条的OnUpdateCmdUI即可。
然而,对于动态调整大小的对话框,这个方法会导致严重的界面刷新问题。此方法不是很好。
方法二:
SendMessageToDescendants(WM_IDLEUPDATECMDUI,
(WPARAM)TRUE, 0, TRUE, TRUE);
为了更省事儿,我这样:
BOOL CSegDialogEx::ContinueModal()
{
SendMessageToDescendants(WM_IDLEUPDATECMDUI,
(WPARAM)TRUE, 0, TRUE, TRUE);
return CDialogEx::ContinueModal();
}
这样,即可不必使用m_Toolbar指针!但效果和方法(1)一样。然而,这里我们至少可以得出结论:
(a)我们可以手动调用:m_Toolbar->OnUpdateCmdUI( pParent, ( WPARAM ) TRUE );
(b)我们也可以手动调用:SendMessageToDescendants(WM_IDLEUPDATECMDUI,
(WPARAM)TRUE, 0, TRUE, TRUE);
显然,(b)是最好的方法,因为通用性更好。
由于ContinueModal的调用太过频繁了,所以,在ContinueModal里面修改,就会导致CPU耗用较高、界面刷新不正确等问题,
于是,我目前的解决方法是:手动调用(a) 。
最后,附上源代码。
By:章永辉