Simple IE-like Menu and Toolbar

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

Environment: VS6/.NET, Windows 98/Me/2000/XP

1 Overview

This article was inspired by the Alpha WTL Sample application that I saw recently. I decided to do something similar, using the MFC library. Standard MFC applications are very out of date with their 16 colored toolbars and menus without images. There are some MFC extensions that implement Office- or Visual Studio-like control bars. They are pretty and very powerful, but just too complicated for most simple applications. Besides, they don't obey the standard Windows UI style. My idea was to create a simple interface based on Internet Explorer that implements features introduced in Windows XP and remains compatible with all OS versions since Windows 98.

The project consists of three classes:

CMenuBar: A toolbar that looks and acts exactly like a menu bar. It can be placed in a rebar control just like in Internet Explorer. It also draws icons next to menu items.

CAlphaImageList: A replacement for CImageList that supports images with an alpha channel introduced in Windows XP. It automatically generates hot and disabled images looking like those in Internet Explorer 6.

CAlphaToolBar: An extension of CToolBar that allows using alpha channel images.

Under Windows XP, this interface automatically uses either the 3D style or the new flat style (compared in the picture above). Under older OS versions it uses the traditional 3D style. Another feature introduced in Windows XP is images with alpha channel. The icons can have smoothed edges so that they look good on every background, dark or bright. This interface works correctly with images from 16 colors to 32-bit alpha channel bitmaps under all versions of Windows.

2 Using It in Your Applications

Note: The menu bar only works in SDI frame windows. It can't be used in dialog windows or in MDI frame windows.

Step 1: Add AlphaImageList.cpp, AlphaToolBar.cpp, and MenuBar.cpp with their corresponding headers to your project.

Step 2: Make sure that your application uses the Windows XP visual style. Copy the manifest.xml file to the res directory of your project and add the following line to YourApp.rc2:

CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "res/manifest.xml"

Step 3: Ensure that Windows XP symbols will be included in your project. Add the following directive at the beginning of StdAfx.h or define it in your project settings:

#define _WIN32_WINNT 0x0501

You will need the new Platform SDK that includes Windows XP symbols.

Step 4: Put the following in your frame window header:

protected:  // control bar embedded members
    CStatusBar    m_wndStatusBar;
    CMenuBar      m_wndMenuBar;
    CAlphaToolBar m_wndToolBar;
    CReBar        m_wndReBar;

Of course you may add any number of toolbars and dialog bars to the rebar control.

Step 5: Modify your frame window's OnCreate handler like in the example:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    GetMenu()->DestroyMenu();
    SetMenu(NULL);

    if (!m_wndToolBar.Create(this, AFX_IDW_TOOLBAR) ||
        !m_wndToolBar.LoadToolBar(IDR_MAINFRAME, AILS_NEW))
    {
        TRACE0("Failed to create toolbar\n");
        return -1;      // fail to create
    }

    if (!m_wndMenuBar.Create(this) ||
        !m_wndMenuBar.LoadMenuBar(IDR_MAINFRAME, AILS_NEW))
    {
        TRACE0("Failed to create menubar\n");
        return -1;      // fail to create
    }

    m_wndMenuBar.LoadToolBar(IDR_MAINFRAME);

    if (!m_wndReBar.Create(this, RBS_BANDBORDERS,
        WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS
                 | CBRS_ALIGN_TOP)
        || !m_wndReBar.AddBar(&m_wndMenuBar)
        || !m_wndReBar.AddBar(&m_wndToolBar, NULL,
                             (CBitmap*)NULL, RBBS_BREAK))
    {
        TRACE0("Failed to create rebar\n");
        return -1;      // fail to create
    }

    if (!m_wndStatusBar.Create(this) ||
        !m_wndStatusBar.SetIndicators(indicators,
          sizeof(indicators)/sizeof(UINT)))
    {
        TRACE0("Failed to create status bar\n");
        return -1;      // fail to create
    }

    return 0;
}

Step 6: Add the following message handlers to your frame window: PreTranslateMessage, WM_MENUCHAR, WM_SYSCOMMAND, WM_ACTIVATE, and WM_SETTINGCHANGE.

BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->message == WM_SYSKEYDOWN && pMsg->wParam
                      == VK_MENU)
        m_wndMenuBar.SetPrefix(TRUE);
    else if (pMsg->message == WM_KEYUP && pMsg->wParam
                           == VK_MENU)
        m_wndMenuBar.SetPrefix(FALSE);

    return CFrameWnd::PreTranslateMessage(pMsg);
}

LRESULT CMainFrame::OnMenuChar(UINT nChar, UINT nFlags,
                               CMenu* pMenu)
{
    if (m_wndMenuBar.OpenMenu(nChar))
        return -1;

    return CFrameWnd::OnMenuChar(nChar, nFlags, pMenu);
}

void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
    if (nID == SC_KEYMENU && m_wndMenuBar.OnKeyMenu(lParam))
        return;

    CFrameWnd::OnSysCommand(nID, lParam);
}

void CMainFrame::OnActivate(UINT nState, CWnd* pWndOther,
                            BOOL bMinimized)
{
    m_wndMenuBar.Activate(nState != WA_INACTIVE);

    CFrameWnd::OnActivate(nState, pWndOther, bMinimized);
}

void CMainFrame::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
    m_wndMenuBar.UpdateSettings();

    CFrameWnd::OnSettingChange(uFlags, lpszSection);
}

2.1 Toolbar images

The CAlphaImageList class supports two styles of images:

AILS_OLD: The image list uses a 16-color bitmap and doesn't create the hot and disabled images. The toolbar looks like in old style applications. The default background color (RGB 192,192,192) is used as transparent. You can change it by modifying the following line in AlphaImageList.cpp:

#define AIL_TRANSPARENT   RGB(192,192,192)

To avoid some gray parts of buttons (like the disk label or the printer) being treated as transparent, I modified the original bitmap so that the magenta color is replaced by a slightly lighter gray (RGB 208,208,208), which is non-transparent. You can replace the Toolbar.bmp file with Toolbar4.bmp from the resources directory of the demo project. Before that, change the toolbar button size to 16x16 pixels.

AILD_NEW: The image list uses a 32-bit bitmap if comctl32.dll version 6 is detected or a 24-bit bitmap otherwise. You can use any number of colors in the toolbar bitmap. If the bitmap contains an alpha channel and the image list is 32-bit, the alpha channel is used as transparency information. Otherwise, the gray color (RGB 192,192,192) is transparent and all other colors are opaque.

You can create 32-bit bitmaps using Adobe Photoshop or a similar application. Even if the image has an alpha channel, you should use the gray color as the background so that the transparency is correct in previous OS versions—the only difference is that the icon edges won't be smooth in that case.

The resources directory of the demo project contains three versions of the new style toolbar bitmap: 8-bit, 24-bit, and 32-bit (with alpha channel). You can use whichever you want in your application. The bitmap was taken from the WTL sample application, with the background color changed to gray. The toolbar button size should be 16x16.

Note that Visual Studio will not let you edit the toolbar if the bitmap contains more than 256 colors. In that case, you will have to modify the resource file (YourApp.rc) by hand. The toolbar definition looks like this (16, 16 is the button size and IDR_MAINFRAME is the resource ID):

IDR_MAINFRAME TOOLBAR DISCARDABLE  16, 16
BEGIN
    BUTTON      ID_FILE_NEW
    BUTTON      ID_FILE_OPEN
    BUTTON      ID_FILE_SAVE
    SEPARATOR
    BUTTON      ID_EDIT_CUT
    ...
END

2.2 Menu resource

CMenuBar contains the following functions to load data from resources:

BOOL LoadMenuBar(UINT nID, int nStyle=AILS_OLD)

Load a menu resource. The nStyle parameter specifies the image list style (see above). You may call this function multiple times to replace the previous menu. The menu bar will automatically resize inside a rebar if needed. All images will be discarded.

BOOL LoadToolBar(UINT nID)

Load images from the bitmap resource and assign them to menu items with the same command IDs as in the toolbar resource. You may add multiple toolbars to a menu. You can create a separate toolbar resource that contains images for menu items, or use the default toolbar.

The menu bar assumes that the size of all images is 16x16 pixels. You can change this by editing the following lines in MenuBar.cpp:

#define MB_CX_ICON  16
#define MB_CY_ICON  16

2.3 Popup menus

CMenuBar also provides support for displaying context menus with the correct visual style and images. Put all popup menus in one resource and call the following function to load it:

BOOL LoadPopupMenu(UINT nID)

This menu will automatically use the same images as the window menu. There are two functions to display popup menus:

void TrackPopup(int nIndex, CPoint ptPos)
void TrackPopup(LPCSTR lpszName, CPoint ptPos)

The first one uses the menu index; the second one uses the name of the menu, which may be more comfortable if your application uses many context menus. The ptPos is the menu position in screen coordinates.

Note: If you don't load a separate popup menu, the window menu will be used by default.

3 Final Notes

I found a bug in MFC 6 that caused the edit view to be incorrectly placed in the window, so that it covers a part of the rebar. The CEditView::CalcWindowRect should be replaced with CView::CalcWindowRect to fix this. Also, the edit view doesn't load and save the text correctly if comctl32.dll version 6 is used.

Implementing support for MDI applications is possible, but quite difficult. Anyway, the MDI architecture is not comfortable and out of date; check out my last article (Multithreaded SDI Applications) for a different solution. There are a lot of things that could be done, such as support for chevrons, popup buttons, and so forth. However, I tried to keep this code small, simple, and yet useful for most purposes.

Visit the author's home page at http://www.mimec.w.pl.

Downloads

Download demo project - 49 Kb
Download source - 15 Kb


Comments

  • AddStrings crash solved

    Posted by AlexLouis on 06/17/2005 09:09am

    In the LoadMenuBar and AttachMenu methods, you have this code:

    // add button for each menu
    for(int i=0; i<m_nItemCount; i++)
    {
    	CString strText;
    	m_pMenu->GetMenuString(i, strText, MF_BYPOSITION);
    
    	tbb.iString = AddStrings(strText);
    	tbb.idCommand = i;
    
    	if (!AddButtons(1, &tbb))
    		return FALSE;
    }
    
    But You can't pass a CString to the AddStrings method, because the string must be ended by two NULL characters. Under somes circumstances, this may lead to a crash. The correct code would be like:
    // add button for each menu
    for(int i=0; i<m_nItemCount; i++)
    {
    	TCHAR strText[100];
    	ZeroMemory(strText, sizeof(TCHAR) *100);
    	
    	m_pMenu->GetMenuString(i, strText, 98, MF_BYPOSITION);
    
    	tbb.iString = AddStrings(strText);
    	tbb.idCommand = i;
    
    	if (!AddButtons(1, &tbb))
    		return FALSE;
    
    }

    Reply
  • very nice code and my suggestion for the "manifest" stuff

    Posted by Legacy on 11/06/2003 12:00am

    Originally posted by: seazi

    Dear Michal:
    
    

    I tried your code and all I can say about your code is one word: wonderful.

    There is one thing about the "manifest" stuff:
    1) usually we have a "MyApp.manifest" file in the res subfolder, not the .xml file you used;
    2) I would think that we could define IDR_MANIFEST in "Resource.h" as follows and do step 3):

    #define IDD_ABOUTBOX 100
    #define IDP_OLE_INIT_FAILED 100
    #define IDR_MANIFEST CREATEPROCESS_MANIFEST_RESOURCE_ID
    ...

    3) Add a line in "MyApp.rc" (not the .rc2 you used) as follows:

    ...
    IDR_MANIFEST RT_MANIFEST "res\\MyApp.manifest"


    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //

    ...

    Keep the wise mind going, my younger brother.

    Reply
  • popup menu

    Posted by Legacy on 09/23/2003 12:00am

    Originally posted by: Mitesh Pandey

    hi!

    I used the features provided by you in my MFC application and it worked fine.

    In my MFC application I have several popup menus. The items in these popup menus are not associated with the windows menu items. The IDs of the popup items are unique and are contained in their respective menu resource.

    How can I use the techniques provided by you to these popup menus so that these popup menus have icons.


    Please Help.

    Reply
  • IE bars

    Posted by Legacy on 09/12/2003 12:00am

    Originally posted by: Mitesh Raj Pandey

    I tried to compile the project in VC++ 6 but some errors appeared.
    
    

    I want to implement the features of you work in my MFC game application.

    Following are the errors what should I do.

    How can I access the identifiers that are not declared.

    Please Help.


    D:\My Documents\Downloads\iebars_demo\IEBars\AlphaImageList.cpp(335) : error C2065: 'SPI_GETFLATMENU' : undeclared identifier
    MenuBar.cpp
    D:\My Documents\Downloads\iebars_demo\IEBars\MenuBar.cpp(95) : error C2065: 'BTNS_BUTTON' : undeclared identifier
    D:\My Documents\Downloads\iebars_demo\IEBars\MenuBar.cpp(95) : error C2065: 'BTNS_AUTOSIZE' : undeclared identifier
    D:\My Documents\Downloads\iebars_demo\IEBars\MenuBar.cpp(408) : error C2065: 'SPI_GETKEYBOARDCUES' : undeclared identifier
    D:\My Documents\Downloads\iebars_demo\IEBars\MenuBar.cpp(409) : error C2065: 'SPI_GETFLATMENU' : undeclared identifier
    D:\My Documents\Downloads\iebars_demo\IEBars\MenuBar.cpp(558) : error C2065: 'TPM_VERPOSANIMATION' : undeclared identifier
    D:\My Documents\Downloads\iebars_demo\IEBars\MenuBar.cpp(608) : error C2065: 'MIIM_FTYPE' : undeclared identifier
    D:\My Documents\Downloads\iebars_demo\IEBars\MenuBar.cpp(740) : error C2065: 'COLOR_MENUHILIGHT' : undeclared identifier
    D:\My Documents\Downloads\iebars_demo\IEBars\MenuBar.cpp(757) : error C2065: 'DT_HIDEPREFIX' : undeclared identifier
    Generating Code...
    Error executing cl.exe.

    IEBars.exe - 9 error(s), 0 warning(s)

    • IE bars

      Posted by AiType on 09/23/2004 07:14am

      Hi! I had the same problems by compiling the demo code. That's because you use an old user32.lib, and so an old . You have to donwload the new platform sdk from Microsoft to get the new libraries. See on: http://www.microsoft.com/msdownload/platformsdk/sdkupdate/downlevel.htm bye

      Reply
    Reply
  • Memory leaks? Corrupted Stack?

    Posted by Legacy on 09/05/2003 12:00am

    Originally posted by: Kurta

    I can compile and run the iebars_demo.zip project (Windows XP, Visual Studio .NET) without modifications. But I get some strange results. Here's an excerpt of the debug output:

    'IEBars.exe': Loaded 'I:\My Projects\IEBars\Debug\IEBars.exe', Symbols loaded.
    .
    .
    .
    Loaded 'K:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.10.0_x-ww_f7fb5805\comctl32.dll', No symbols loaded.
    'IEBars.exe': Loaded 'K:\WINDOWS\system32\uxtheme.dll', No symbols loaded.

    // This occurs when the
    // BOOL CAlphaImageList::AddBitmap(UINT nID)
    // function returns (Ln:260).
    Run-Time Check Failure #2 - Stack around the variable 'bmpi' was corrupted.
    Run-Time Check Failure #2 - Stack around the variable 'bmpi' was corrupted.

    First-chance exception at 0x77e73887 in IEBars.exe: Microsoft C++ exception: CL_EXCEPCION_FINAL_FUNCION @ 0x0012f100.
    First-chance exception at 0x77e73887 in IEBars.exe: Microsoft C++ exception: CL_EXCEPCION_FINAL_FUNCION @ 0x0012f23b.

    // Closing the app.
    Detected memory leaks!
    Dumping objects ->
    i:\my projects\iebars\alphaimagelist.cpp(142) : {106} normal block at 0x003882D0, 8192 bytes long.
    Data: < > C0 C0 C0 00 C0 C0 C0 00 DA B0 A4 F9 E6 BA B0 EF
    i:\my projects\iebars\alphaimagelist.cpp(142) : {91} normal block at 0x003860A0, 8192 bytes long.
    Data: < > C0 C0 C0 00 C0 C0 C0 00 DA B0 A4 F9 E6 BA B0 EF
    Object dump complete.
    The program '[2796] IEBars.exe: Native' has exited with code 0 (0x0).

    What's the problem? What should I do?

    Thanks,

    K.

    Reply
  • Platform SDK not neccesary...

    Posted by Legacy on 09/04/2003 12:00am

    Originally posted by: Fagan Nicola

    If do not have a Platform SDK,
    create a file .h with this defines:

    //PlatForm.h

    #define BTNS_BUTTON TBSTYLE_BUTTON // 0x0000
    #define BTNS_SEP TBSTYLE_SEP // 0x0001
    #define BTNS_CHECK TBSTYLE_CHECK // 0x0002
    #define BTNS_GROUP TBSTYLE_GROUP // 0x0004
    #define BTNS_CHECKGROUP TBSTYLE_CHECKGROUP // (TBSTYLE_GROUP | TBSTYLE_CHECK)
    #define BTNS_DROPDOWN TBSTYLE_DROPDOWN // 0x0008
    #define BTNS_AUTOSIZE TBSTYLE_AUTOSIZE // 0x0010; automatically calculate the cx of the button
    #define BTNS_NOPREFIX TBSTYLE_NOPREFIX // 0x0020; this button should not have accel prefix
    #define BTNS_SHOWTEXT 0x0040 // ignored unless TBSTYLE_EX_MIXEDBUTTONS is set
    #define BTNS_WHOLEDROPDOWN 0x0080 // draw drop-down arrow, but without split arrow section
    #define SPI_GETFLATMENU 0x1022
    #define SPI_GETKEYBOARDCUES 0x100A
    #define TPM_VERPOSANIMATION 0x1000L
    #define MIIM_FTYPE 0x00000100
    #define COLOR_MENUHILIGHT 29
    #define DT_HIDEPREFIX 0x00100000

    ..and in your stdafx.h insert:

    #include "platform.h"

    Recompile all.
    Nice Work

    Bye

    Reply
  • how to display certain icons in the menu without creating a button in the toolbar?

    Posted by Legacy on 08/18/2003 12:00am

    Originally posted by: Appstmd

    Any idea on how to display certain icons in the menu without creating a button in the toolbar?

    I think I could do so by just hiding the button in the toolbar, but is it the best solution?

    Thnaks in advance!

    Reply
  • CFileDialog Problem!!!

    Posted by Legacy on 08/12/2003 12:00am

    Originally posted by: Brian Farkas

    The problem exists as "krkim" wrote.

    The CFileDialog works if you select "Open/Save" from the File menu, but if you place a new menu entry and open up a separate CFileDialog, it will "crash" after exiting DoModal. Pretty annoying.

    I've discarded all the modifications in CMainFrame (basically deleted your code from my project) but the CFileDialog still failed to work perfectly, so i suspect there is a bug in the new "Platform SDK" (thanks Microsoft ;).

    Sadly the new Platform SDK is required to run IEBARS (also the new XP Style buttons/graphics), so i did a little hack. Deleted the new platform SDK from Visual C++ (Tools->Options->Directories). This way IEBARS would not compile because of some missing symbols. I've looked up the values of these required symbols in the new platform SDK (in "CommCtrl.h" & "WinUser.h") and manually typed/defined them into my project's "stdafx.h". This way IEBARS compiled and CFileDialog worked fine again using my old/default plaform SDK which came with Visual C++. Sadly the new XP style graphics/buttons disappeared :(.

    I decided to copy the new CommCtrl.* (2 files) and WinUser.* (3 files) from the new platform SDK to my default (old) Visual C++ include directory. It turned out that the compiler wants a "TvOut.h" file aswell, so i copied it from the new platfrom SDK too. The XP style graphics came back and CFileDialog works, too.

    By the way, the compiler still says that some symbols left undefined (they are declared in the "WinUser.h"), so i kept these in my "stdafx.h".

    Hope this helps!

    greets,
    Brian/Audio Simulation


    How to fix CFileDialog with IEBARS.

    1. Do NOT use the new Platform SDK (until it is fixed;), so delete it from Visual C's Tools->Options->Directories.
    2. Copy new WinUser.* (3 files), CommCtrl.* (2 files) and TvOut.h from the new platform SDK Include folder to Visual C's default include folder (usually C:\Program Files\Microsoft Visual Studio\VC98\Include). Overwrite the old files.
    3. Add these defines into your "StdAfx.h" file

    #define SPI_GETKEYBOARDCUES 0x100a
    #define SPI_GETFLATMENU 0x1022
    #define TPM_VERPOSANIMATION 0x1000L
    #define MIIM_FTYPE 0x100
    #define COLOR_MENUHILIGHT 29
    #define DT_HIDEPREFIX 0x00100000

    That's All

    Reply
  • Some mistakes, help!

    Posted by Legacy on 07/31/2003 12:00am

    Originally posted by: Dan

    e:\program files\microsoft sdk\include\shtypes.h(102) : error C2011: '_SHITEMID' : 'struct' type redefinition
    e:\program files\microsoft sdk\include\shtypes.h(120) : error C2011: '_ITEMIDLIST' : 'struct' type redefinition
    e:\program files\microsoft sdk\include\shtypes.h(157) : error C2059: syntax error : 'constant'
    e:\program files\microsoft sdk\include\shtypes.h(160) : error C2143: syntax error : missing ';' before '}'
    e:\program files\microsoft sdk\include\shtypes.h(160) : error C2501: 'STRRET_TYPE' : missing storage-class or type specifiers
    e:\program files\microsoft sdk\include\shtypes.h(164) : error C2011: '_STRRET' : 'struct' type redefinition
    e:\program files\microsoft sdk\include\shtypes.h(210) : error C2143: syntax error : missing ';' before '}'
    e:\program files\microsoft sdk\include\shtypes.h(210) : error C2143: syntax error : missing ';' before '}'
    e:\program files\microsoft sdk\include\shtypes.h(210) : error C2143: syntax error : missing ';' before '}'
    e:\program files\microsoft sdk\include\shlwapi.h(61) : error C2143: syntax error : missing ';' before '{'
    e:\program files\microsoft sdk\include\shlwapi.h(61) : error C2447: missing function header (old-style formal list?)

    sdk was installed properly. But I find that there are some same definitons in file "ShlObj.h" and "ShTypes.h". Can you tell me how to solve this problem!
    Best Regards!

    Reply
  • A little bug

    Posted by Legacy on 06/11/2003 12:00am

    Originally posted by: Appstmd

    Hi!

    There is a little bug with the menu bar: if you click on a menu without releasing the left button, select an item in the menu and then release the left button, nothing happens!
    The program should execute the command associated with the selected item!

    Does anyone have an idea on how to fix this problem?

    Thks in advance!
    Appstmd.

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date