您的位置:首页 > 其它

在对话框里面加入工具条CMFCToolBar

2012-09-05 10:06 232 查看
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:章永辉
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息