Wednesday, May 21, 2008

Create Menu Bar Programmatically - WM5

Hi,
today i show you how create a menubar from code without use any resource.

//add these lines in your wm_initdialog or wm_create function
#define ID_CHANGE_TEXT WM_APP+1
#define ID_ADD_NEW_ITEM WM_APP+2
#define ID_NEW_ITEM WM_APP+3

//Create the right popupmenu
HMENU hPopupRightMenu = ::CreatePopupMenu();
::InsertMenu(hPopupRightMenu, -1, MF_BYPOSITION, ID_CHANGE_TEXT, L"Change Left Text");
::InsertMenu(hPopupRightMenu, -1, MF_BYPOSITION, ID_ADD_NEW_ITEM, L"Add New Item here");
//create the toolbar menu
HMENU hMenu = ::CreateMenu();
::InsertMenu(hMenu, 0, MF_BYPOSITION, IDOK, L"Left");
::InsertMenu(hMenu, 1, MF_BYPOSITIONMF_POPUP, (UINT)hPopupRightMenu, L"Right");
//and now create the menu bar
HWND hMB = AtlCreateMenuBar(m_hWnd, (UINT)hMenu, SHCMBF_HMENU);

now add handlers for our menus id:
LRESULT OnChangeText(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
//try to modify text on the left button
TBBUTTONINFO tbbit = {0};
tbbit.cbSize = sizeof(tbbit);
tbbit.dwMask = TBIF_TEXT;
tbbit.pszText = L"Exit";
::SendMessage (SHFindMenuBar(m_hWnd), TB_SETBUTTONINFO, (UINT)IDOK, (LPARAM)&tbbit);
return 0;
}

LRESULT OnAddNewItem(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
//now try to modify right popup
TBBUTTONINFO tbbi = {0};
tbbi.cbSize = sizeof(tbbi);
tbbi.dwMask = TBIF_LPARAM;
::SendMessage (SHFindMenuBar(m_hWnd), TB_GETBUTTONINFO, (WPARAM)0, (LPARAM)&tbbi);
HMENU hPopupMenu = (HMENU)tbbi.lParam;
InsertMenu(hPopupMenu, -1, MF_BYPOSITION, ID_NEW_ITEM, L"New Item!");
return 0;
}

LRESULT OnNewItem(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
MessageBox(L"New Item Clicked!!!");
return 0;
}


this is ... :)

10 comments:

Nam said...

Where i get AtlCreateMenuBar ?

crino said...

AtlCreateMenuBar is a WTL function. It just calls the standard 'SHCreateMenuBar' function :)

Swaroop said...

@Crino,

I see that you were able to create a Menu bar dynamically. But you aren't passing/associating it with the current Window.

Can you tell me how the window would know that the created HMENU be shown?

I have seen an implementation of AtlCreateMenuBar() which just passes the nToolBarId = 'ATL_IDW_TOOLBAR'

http://flylinkdc.googlecode.com/svn/trunk/wtl/atlwince.h

But i couldn't figure out how the hWnd knows about the HMENU.

crino said...

sorry, i fixed the code
you need to call:

HWND hMB = AtlCreateMenuBar(m_hWnd, hMenu, SHCMBF_HMENU);

hMenu must be passed to AtlCreateMenuBar function with SHCMBF_HMENU flag.

Hope this help you.

Swaroop said...

@Crino,

The definition for AtlCreateMenuBar() is as follows

inline HWND AtlCreateMenuBar(HWND hWnd, UINT nToolBarId = ATL_IDW_TOOLBAR, DWORD dwFlags = 0, int nBmpId = 0, int cBmpImages = 0, COLORREF clrBk = 0)

So you're passing hMenu in place of nToolBarId ? It'd give a compilation error right?

Do you have a working sample that one can download and quickly check this?

Also this might be kind of stupid, but is there a header file that comes with Windows mobile 6 SDK where I can find this method? (I can't find AtlWince.h in the WinMob 6 SDK). I am currently trying to use SHCreateMenuBar directly without involving this method implementation.

crino said...

yes pass hMenu instead of ATL_IDW_TOOLBAR, the flags will tell to the 'system' that the parameter is an HANDLE.
if you take a look at atlwince.h (is into WTL package) you can 'decode' the steps necessary...anyway:
SHMENUBARINFO mbi = { sizeof(SHMENUBARINFO), hWnd, SHCMBF_HMENU , hMenu, ModuleHelper::GetResourceInstance(), 0, 0, 0, 0};
::SHCreateMenuBar(&mbi);

this is

Swaroop said...

@Crino,

I've modified the code to

SHMENUBARINFO mbi = { sizeof(mbi), hWnd, SHCMBF_HMENU, hMenu, GetModuleHandle(NULL), 0, 0, 0, 0 };

But I am still getting a compilation error as

XXXXX.c(372) : error C2440: 'initializing' : cannot convert from 'HMENU' to 'UINT'

I see that the nToolBarId is an unsigned int and we can't send a HMENU instance to it.

The 'decoded' AtlCreateMenuBar is defaulting nToolBarId = 'ATL_IDW_TOOLBAR'.

inline HWND AtlCreateMenuBar(HWND hWnd, UINT nToolBarId = ATL_IDW_TOOLBAR, DWORD dwFlags = 0, int nBmpId = 0, int cBmpImages = 0, COLORREF clrBk = 0)
{ ... }

Any clue where I am going wrong?

crino said...

just:
(UINT)hMenu

ATL_IDW_TOOLBAR is the default ATL toolbar.

Swaroop said...

Thanks Crino for the quick help. That helped me.

Although something's really wrong with left and right buttons. They're interchanged. Guess it must be my code.

Thanks a ton :)

crino said...

good ;)