The NewMenu is published on CodeProject. There you will find the discussion board.
STYLE_XP | STYLE_ICY | STYLE_ORIGINAL |
![]() |
![]() |
![]() |
This class, CNewMenu
, implements owner drawn menus
derived from the CMenu
class. The goal of this class is to have
menus with the same look as the new menu from Microsoft products with
Icons
and Title
support. I was inspired from the great
class BCMenu from Brent
Corkum. I took some ideas and almost every function but reimplemented almost
all of the code and expanded it with some other nice functions. It was a hard
work to change the look of the whole menu that belongs to an application -
changing the border and adding shading
. Furthermore, it was tricky
changing all menus under windows 2000 or system menu under Windows XP when you
are using themes. Over all, I hope it's now easy enough for you to use this
class in new applications.
This new menu works under Windows 95/98/Me and Windows NT4.0/2000/XP. When you would like to use Chinese or some other special characters, you have to compile your project with Unicode support. But keep in mind you must have installed the Unicode libraries with Visual Studio, otherwise you get a linking error.
Additionally, when you want your program to work on all platforms, you have
to compile it with Visual Studio 6.0. I did not find out why the drawing didn't
work correctly when it was compiled with Visual Studio 7.0 under Windows NT 4.0.
Furthermore, when you do not like the flat border you can use the
STYLE_XXX_NOBORDER
and then menus will have the standard menu
border like the system.
![]() |
![]() |
![]() |
![]() |
It's also possible to add a title to a menu. You can add a title on the top
or on the left side of a menu. You can have a gradient or a solid color as a
background. With the function SetMenuTitle
you can add or remove a
title from a menu. The gradient drawing also works on Windows 95.
// Set a title to the system menu
m_SystemNewMenu.SetMenuTitle(_T("New Menu Sample"),
MFT_GRADIENT|MFT_LINE|MFT_CENTER);
![]() ![]() |
Under menu style STYLE_XP you have the possibility to enable or disable
the additional drawing style to icons. Disabled icons have a
|
CDialog, CFrameWnd, CMDIFrameWnd,
CMDIChildWnd, CMiniFrameWnd
and CMultiDocTemplate
with the
new classes CNewDialog, CNewFrameWnd,
CNewMDIFrameWnd, CNewMDIChildWnd,
CNewMiniFrameWnd
and
CNewMultiDocTemplate
in your project.
CWinApp
's implementation, set
the menustyle just before you create the frame or a dialog window in the
function InitInstance
. // Set drawing style of all menus to XP-Mode or STYLE_ORIGINAL
CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);
CNewMultiDocTemplate* pDocTemplate;
pDocTemplate = new CNewMultiDocTemplate(IDR_MDINEWTYPE,
RUNTIME_CLASS(CMDINewMenuDoc),
// custom MDI child frame
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CMDINewMenuView));
AddDocTemplate(pDocTemplate);
// Loading toolbar icons into the menu
pDocTemplate->m_NewMenuShared.LoadToolBar(IDR_DRAWBAR);
m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);
InitInstance
. // Set drawing style of all menus to XP-Mode
CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);
CDialog
to CNewDialog
in your Project.
OnInitDialog
function after calling the base class function. // CDialogDlg could be your Dialog.
BOOL CDialogDlg::OnInitDialog()
{
// Call the baseclass
CNewDialog::OnInitDialog();
// Load icons to the menu
m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);
}
measureitem
,
menuchar
and initmenupopup
. For this purpose I use
the CNewFrame
template class. So you can replace
CYourSpecialBase
with
CNewFrame<CYourSpecialBase>
in your project.
IMPLEMENT_DYNAMIC
,
IMPLEMENT_SERIAL
or IMPLEMENT_DYNACREATE
to
sub-class the new one. IMPLEMENT_DYNAMIC(CYourNewSpecialFrame, CYourSpecialBase)
BEGIN_MESSAGE_MAP(CYourNewSpecialFrame, CNewFrame<CYourSpecialBase>)
//{{AFX_MSG_MAP(CYourNewSpecialFrame)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
OnCreate
function.
BOOL
InitInstance()
function from CMultiDocTemplate
to
CNewMultiDocTemplate
: CNewMultiDocTemplate* pDocTemplate; pDocTemplate = new CNewMultiDocTemplate(IDR_MDINEWTYPE, RUNTIME_CLASS(CMDINewMenuDoc), // custom MDI child frame RUNTIME_CLASS(CChildFrame), RUNTIME_CLASS(CMDINewMenuView)); AddDocTemplate(pDocTemplate);
// Loading toolbar icons into the menu pDocTemplate->m_NewMenuShared.LoadToolBar(IDR_DRAWBAR); // Set drawing style of all menus to XP-Mode CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);
CMainFrame
)
from CMDIFrameWnd
to CNewMDIFrame
in the definition
and implementation file. // Change the base class in the following makro IMPLEMENT_DYNAMIC(CMainFrame, CNewMDIFrameWnd)
// don't forget to change here the baseclass BEGIN_MESSAGE_MAP(CMainFrame, CNewMDIFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code ! ON_WM_CREATE() //}}AFX_MSG_MAP END_MESSAGE_MAP()
int CMainFrame::OnCreate(LPCREATESTRUCT
lpCreateStruct)
for loading bitmap in the default menu: m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);
CMDIChildWnd
to CNewMDIChildWnd
. Do the same for all
derived classes.
InitInstance
. // Set drawing style of all menus to XP-Mode CNewMenu::SetMenuDrawMode(CNewMenu::STYLE_XP);
CDialog
to CNewDialog
in your Project.
CMainFrame
)
from CFrameWnd
to CNewFrameWnd
in definition and
implementation file.
int CMainFrame::OnCreate(LPCREATESTRUCT
lpCreateStruct)
for loading bitmap in the default menu: m_DefaultNewMenu.LoadToolBar(IDR_MAINFRAME);
Sometimes it would be nice to change the menu in a message box or in a
CFileDialog
Unfortunately, a MessageBox has no base class for
overriding. Another problem is the file dialog. There are at least two dialogs
and the CFileDialog
only sub-classes the child dialog of the
FileDialog
displayed. For those and other dialogs, I added a
special sub-classing mechanism.
// Subclass dialog with system menu
CNewMenuHelper myHelper(NEW_MENU_DIALOG_SUBCLASS|NEW_MENU_DIALOG_SYSTEM_MENU);
AfxMessageBox(_T("You must restart the application"), MB_OK);
What can one do when he or she deso not want to have a menu border replacing
in the next dialog displayed? It is very simple! Just place the
CNewMenuHelper
before your call like in the following sample.
// Subclass dialog with normal border painting
CNewMenuHelper myHelper(NEW_MENU_DEFAULT_BORDER);
AfxMessageBox(_T("You must restart the application"), MB_OK);
The MFC library replaces some colors depending on the chosen system colors.
When you use a black line in the bitmap, the color black will be replaced with
the system color of COLOR_BTNTEXT
. Following the color mapping map.
By the way, it is the same color mapping of the toolbars.
The color RGB(0x00,0x00,0x00) will be changed to COLOR_BTNTEXT (black)
The color RGB(0x80,0x80,0x80) will be changed to COLOR_BTNSHADOW (dark gray )
The color RGB(0xC0,0xC0,0xC0) will be changed to COLOR_BTNFACE (bright gray )
The color RGB(0xFF,0xFF,0xFF) will be changed to COLOR_BTNHIGHLIGHT (white )
There are several possibilities for adding icons to a menu. An easy way is
with a toolbar resource. But developer studio only supports 16 colors for
toolbar-bitmaps. What you can do is: first define your toolbar then replace the
saved toolbar-bitmap with a hicolor bitmap by hand. Additionally, you can define
every icon in a different bitmap and assign it to a menu item. My way was to
define a bitmap like in an image list, make a helper table with the resource id
of the bitmap and the size of an icon followed by the ids of the icons. The end
of the table must be marked with the id number NULL
.
// Define the table
static WORD ToolId[] =
{ IDR_NEWMENU256, // Resource id of the bitmap-icons
16, 15, // Dimension of each icon
ID_FILE_NEW, // First icon's ID
ID_FILE_OPEN, // Second icon's ID
ID_FILE_SAVE, // and so on...
NULL}; // Marker for end of table
//Load the definition into menu with transparent color
m_DefaultNewMenu.LoadToolBar(ToolId,RGB(192,192,192));
It is very simple to add or change the title of a submenu entry when you know
the menu text. The helper function ModifyODMenu
can be used.
// set the icon IDB_MORE_C to the submenu "More A" m_NewMenuShared.ModifyODMenu(0,_T("More A"),IDB_MORE_C);
It was strange to find that under Windows 2000 you have a window menu with handle 0x10012 which is shared by all applications. (I think under Windows 98 the menu is 0x0090). I don't know why but this is also a reason of the specialty for some effects belonging to the sub-classed menu. First, when you have disabled menu effects, the menu windows are alternating created or shown. That means the first shown menu is mostly the special menu - never created nor destroyed - only shown and disabled, and the second is created normally and destroyed after closing. The third time, sometimes, the special window is shown again and then the next menu is created normally. For this reason, when you sub-class the special menu, you are responsible for restoring all values before unsub-classing it. Be careful when you change some flags or styles, because the other applications like having the standard menu. You can also receive bluescreens when you forget to unsub-class or when changing painting areas belonging to that menu. This is really ugly! Second, when you enable menu effects every displayed menu is created from scratch. For this reason it doesn't matter if you don't restore all flags or styles!
Let me explain how I got the goal to sub-class all menus. First, you can't
sub-class the system menu class #32768 of an application; I was also looking for
other possibilities. I decided to create a windows hook for all windows messages
and catch all messages for creating a window. After checking for the window
class, when I found the menu class, I subclassed it. But how I can sub-class the
special menu, which is never created? I found that one of the first messages
which were being sent to that menu, was 0x01E2 and one of the latest
WM_SHOWWINDOW
. So I caught these messages for sub- and
unsub-classing the special menu. Only between those two messages do you have
access to the special window! I don't know why, so be careful.
After I sub-class the menu window I can draw the border by by processing
WM_NCPAINT
message. But how is the shading effect created? Just
before the menu is shown on the screen I save the menu region in a bitmap. This
is the reason why it does not work when the background has been changed. Then I
combined it with a shadow drawing. Another way would be using layered windows,
but this will not work on all systems like windows 95/98.
I thought owner drawing a menu is easy all the time, but on Windows XP, with themes enabled, it is a problematic thing - especially the main frame when the system menus were displayed. Instead of owner drawing, the menu items were drawn by the frame. The frame had painted the windows button of the width of the menu at the place of the menu item instead of drawing it, so I found a work-around. I increased the menu item's identifier before showing the menu and restored it after closing it. So it works under Windows 2000 and XP.
You should never stop debugging when you are in middle of painting the menus because the special menu wouldn't be restored or unsub-classed. Furthermore, there were a lot of strange effects. It is not forbidden but be careful and have a lot time for rebooting the system when it doesn't work correctly after stopping the debugger.
23 January 2003 - Version 1.13
10 November 2002 - Version 1.11
30 July 2002 - Version 1.10