Click here to Skip to main content
15,886,069 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
i've spent a whole day on this and got no where, i hope someone can help

i have a vs2010 MFC application and i have a number of menu item items. each menu item has a keyboard key associated with it so i want to use something like the string that is used to make the cut, copy and paste menu items

"&Copy\tCtrl+C" this is displayed in the menu with "Copy" left aligned and "Ctrl+C" right aligned

so i thought simple, i make my strings "Snare drum\tS", "Bass drum\tB", etc
with the expection of getting something like this
Snare drum  S
Bass drum    B
with the S and the B right aligned
but it doesn't work, the S and the B don't show up at all

i've tired adding them as accelerators, i've tried simply using spaces but the menu is displayed with non fixed width fonts so they don't right align nicely. its very confusing. maybe if i can find a way to chenge the menu for to something fixed width that will help.

by the way, if i remove \tCtrl+C from the description of Copy is still displays it. i suspect the \tCtrl+C is saved in the system registry somewhere but don't think that helps me

does anyone have an ideas please?
Posted
Updated 30-May-11 0:29am
v5

I have just tried this in Win32 and it works correctly using your samples. Perhaps you could show the actual code you have so that others can check it for you. Are you sure that you are recompiling the correct project before testing?
 
Share this answer
 
I realize I am waaay late to this party, but I had a similar problem to the OP and managed to fix it in a painless way, although finding the fix was not at all painless :). I thought I'd share my solution in case somebody else is having this problem. Here's some background first.

My app is a Visual Studio-style MDI app originally created by AppWizard. The tree controls on the left side are views derived from CTreeView. These tree views have context menus and accelerator key handling. The latter is accomplished in the usual way, by having an HACCEL member in the window class, a separate accelerator table for this view in the resource script, and by calling LoadAccelerators() in the constructor of the class. Also, the usual technique of overriding PreTranslateMessage() and calling ::TranslateAccelerator() within it is used. That all works fine.

Now, my problem was that when displaying menus having entries like this in the resource script

IDR_POPUP_CONFIG_THIS MENU
BEGIN
    POPUP "Popup"
    BEGIN
        MENUITEM "Delete Configuration\tDel",   ID_DELETE_ITEM
    END
END


the "\tDel" at the end was stripped off when the menu was displayed.

After much pain and suffering :), I tracked it down to calls within the framework of the following functions:
CKeyboardManager::FindDefaultAccelerator()

which is called by:
CMFCToolBarMenuButton::OnCalculateSize()

A popup menu item is apparently a kind of CMFCToolBarMenuButton. At any rate, the latter function grabs a pointer to a CFrameWnd, which in my case ended up being the main window of the application. Then it calls CKeyboardManager::FindDefaultAccelerator(), passing it the CFrameWnd pointer. This function searches the accelerator table of the CFrameWnd class, and tries to find a matching accelerator key for the command ID. If it does, it uses an instance of CMFCAcceleratorKey to get the appropriate string corresponding to the keystroke of the matching accelerator and passes that string through the argument list of CKeyboardManager::FindDefaultAccelerator() to another function that concatenates the text already in the menu item with the one found by the CMFCAccelerator instance. All of this is in support of the keyboard customization, which, if you think about it, won't work properly in general if you put shortcut key names in the menu item in the resource script. My app does not have such customization.

The problem was that the main window's accelerator table did not have the accelerator keys for the CTreeView-derived class. So all that needs to be done is to add these accelerators to the accelerator table of the main window, right? Well, almost, but there's another wrinkle. When I made the fix to the main window's accelerator table, it still didn't work. Digging a little deeper, I found that the main window's newly-modified accelerator table had 15 entries, but the call to get the count of accelerators in the main window in CKeyboardManager::FindDefaultAccelerator() only yielded 14. What's going on here? It turns out that these accelerators are stored in the registry, in a section that looks like ThisApp\\Workspace\\Keyboard-0. I shut down the app and deleted this key folder from the registry, and boom, the accelerator key count now went from the incorrect value of 14 to the correct value of 15. It looks like these registry entries track changes made by the user, but not the developer! I tried removing the added accelerator from the main window's accelerator table in the resource script, and in CKeyboardManager::FindDefaultAccelerator(), it still told me there were 15 items, and still showed the accelerator key text on my menu, even when there was no such accelerator in the main window's accelerator table! Again, deleting the aforementioned registry key folder brought everything back in sync.

To sum up, if you have a child window that handles its own accelerators by overriding PreTranslateMessage() to call ::TranslateAccelerator(), and you want the text of the accelerators to show up in the context menus of the child window, you must do two things:

1) Add the accelerators to the main window's accelerator table as well
2) Delete the registry key for e.g. ThisApp that looks like ThisApp\\Workspace\\Keyboard-0.

This registry key will be regenerated by the framework on application exit. The solution requires no code, only editing the .rc file and the registry. That's why the only code listed above is the portion of the MFC feature pack in which the menu text assignment takes place.

Hope this helps someone.
 
Share this answer
 
v2
thanks, there isn't any code, its just a resource string

i get the menu to pop up with a ON_WM_CONTEXTMENU() in the message map and this code
C#
void CTabTraxView::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
    ClientToScreen(&point);
    OnContextMenu(this, point);
}
void CTabTraxView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
    theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}

launches the right mouse popup but it was generated by the wizard so i expect its correct

the string loo like this
SQL
BEGIN
            MENUITEM "Snare\tS",        ID_EDIT_SNARE
            MENUITEM "Kick (Bass drum)\tK",        ID_EDIT_BASS
            MENUITEM "Closed High Hat\tH",        ID_EDIT_HIGHHAT
            MENUITEM "Open High Hat\t O",        ID_EDIT_OPENHIGHHAT
        END


etc

i'm on windows 7 and i've spent all day compiling and testing it
 
Share this answer
 
well it looks like i found a way to fix it my adding my own draw routing to the CMFCPopupMenu

here's the code, i don't have time to explain how it works but it does

in MainFrame.h i add an override
virtual BOOL OnShowPopupMenu (CMFCPopupMenu* pMenuPopup);


then in MainFrame.cpp
BOOL CMainFrame::OnShowPopupMenu (CMFCPopupMenu* pMenuPopup)
{
    CMDIFrameWndEx::OnShowPopupMenu(pMenuPopup);

	if (pMenuPopup == NULL || pMenuPopup->GetMenuBar () == NULL)
	{
		return TRUE;
	}

	// replace menu items with CTabStyleMenuItem to my my tab character
	ReplaceRangeWithTabStyleMenuItem(pMenuPopup, ID_EDIT_TRIPLETIZETKEY, ID_EDIT_GHOSTGKEY);
	ReplaceRangeWithTabStyleMenuItem(pMenuPopup, ID_EDIT_SNARE, ID_EDIT_RIDECYMBAL);
	ReplaceRangeWithTabStyleMenuItem(pMenuPopup, ID_EDIT_JOGLEFT, ID_EDIT_MOVENOTEDOWN);
	ReplaceRangeWithTabStyleMenuItem(pMenuPopup, ID_EDIT_TAILUP, ID_EDIT_TAILDOWN);
	return TRUE;
}

void CMainFrame::ReplaceRangeWithTabStyleMenuItem(CMFCPopupMenu* pMenuPopup, UINT startId, UINT endId)
{
	//-------------------------------------------------------------------
	// Replace all "line style" menu items by CTabStyleMenuItem objects:
	//-------------------------------------------------------------------
	for (UINT uiLineStyleCmd = startId; uiLineStyleCmd <= endId; uiLineStyleCmd++)
	{
		int iIndex = pMenuPopup->GetMenuBar ()->CommandToIndex (uiLineStyleCmd);
		if (iIndex >= 0)
		{
			CMFCPopupMenuBar* pMebuBar = pMenuPopup->GetMenuBar();
			pMebuBar->m_bDisableSideBarInXPMode = TRUE;
			// Obtain item text and style:
			CString strText	= pMebuBar->GetButtonText (iIndex);
			UINT uiStyle	= pMebuBar->GetButtonStyle (iIndex);

			// Replace menu item:
			pMebuBar->ReplaceButton(uiLineStyleCmd, CTabStyleMenuItem (uiLineStyleCmd, strText, uiStyle & TBBS_CHECKED));
		}
	}
}


the import part here is for every menu item in the range, i replace its class with CTabStyleMenuItem here
ReplaceButton(uiLineStyleCmd, CTabStyleMenuItem, ...

my menu strings look like this now

CSS
BEGIN
    POPUP "Edit"
    BEGIN
        MENUITEM "Cu&t\tCtrl+X",                ID_EDIT_CUT
        MENUITEM "&Copy\tCtrl+C",               ID_EDIT_COPY
        MENUITEM "&Paste\tCtrl+V",              ID_EDIT_PASTE
        MENUITEM SEPARATOR
        MENUITEM "Tripletize:T", ID_EDIT_TRIPLETIZETKEY
        MENUITEM "Flam:F", ID_EDIT_FLAMFKEY
        MENUITEM "Accent:A", ID_EDIT_ACCENTAKEY
        MENUITEM "Ghost:G", ID_EDIT_GHOSTGKEY
        POPUP "Insert note"
        BEGIN
            MENUITEM "Snare:S",        ID_EDIT_SNARE
            MENUITEM "Kick (Bass drum):K",        ID_EDIT_BASS
            MENUITEM "Closed High Hat:H",        ID_EDIT_HIGHHAT
            MENUITEM "Open High Hat:O",        ID_EDIT_OPENHIGHHAT
            MENUITEM "Crash Cymbal 1:C",        ID_EDIT_CRASHCYMBAL1
            MENUITEM "Crash Cymbal 2:V",        ID_EDIT_CRASHCYMBAL2
            MENUITEM "Ride Cymbal:R",        ID_EDIT_RIDECYMBAL
        END
        POPUP "Move note"
        BEGIN
            MENUITEM "Move notes left:Left Arrow", ID_EDIT_JOGLEFT
            MENUITEM "Move notes right:Right Arrow", ID_EDIT_JOGRIGHT
            MENUITEM "Move note up:Up Arrow",  ID_EDIT_MOVENOTEUP
            MENUITEM "Move note down:Down Arrow", ID_EDIT_MOVENOTEDOWN
        END
        POPUP "Tail direction"
        BEGIN
            MENUITEM "Tail up:Shift+Up Arrow", ID_EDIT_TAILUP
            MENUITEM "Tail down:Shift+Down Arrow", ID_EDIT_TAILDOWN
        END
    END
END


the ':' character in the menu test string will be replaced by right aligning the following text in my special draw routine.

so create the class
class CTabStyleMenuItem : public CMFCToolBarMenuButton  
{
	DECLARE_DYNCREATE(CTabStyleMenuItem)

public:
	CTabStyleMenuItem(UINT uiCmdID = 0, LPCTSTR lpszText = NULL,
						BOOL bIsChecked = FALSE);
	virtual ~CTabStyleMenuItem();

protected:
	virtual void OnDraw (CDC* pDC, const CRect& rect, CMFCToolBarImages* pImages,
						BOOL bHorz = TRUE, BOOL bCustomizeMode = FALSE,
						BOOL bHighlight = FALSE,
						BOOL bDrawBorder = TRUE,
						BOOL bGrayDisabledButtons = TRUE);
};


and

IMPLEMENT_DYNCREATE(CTabStyleMenuItem, CMFCToolBarMenuButton)


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CTabStyleMenuItem::CTabStyleMenuItem(UINT uiCmdID, LPCTSTR lpszText,
									   BOOL bIsChecked) :
	CMFCToolBarMenuButton (uiCmdID, NULL /* HMENU */, -1 /*iImage*/, lpszText)
{
	if (bIsChecked)
	{
		m_nStyle |= TBBS_CHECKED;
	}
}
//*****************************************************************************************
CTabStyleMenuItem::~CTabStyleMenuItem()
{
}
//*****************************************************************************************
void CTabStyleMenuItem::OnDraw (CDC* pDC, const CRect& rect, CMFCToolBarImages* /*pImages*/,
			BOOL /*bHorz*/, BOOL /*bCustomizeMode*/, BOOL bHighlight,
			BOOL /*bDrawBorder*/, BOOL /*bGrayDisabledButtons*/)
{
	ASSERT_VALID (pDC);

	CRect rectItem = rect;
	rectItem.DeflateRect (1, 1);

	//--------------------
	// Fill item interior:
	//--------------------
	FillInterior (pDC, rectItem, bHighlight);

	// split test string at the ':'
	int colonPos = m_strText.Find(':');

	if(colonPos==-1) {
		// no colon, just normal print

		// Draw text label left:
		CRect rectText = rectItem;
		rectText.left += afxGlobalData.GetTextWidth () +5;
		rectText.right = rectText.left + afxGlobalData.GetTextWidth () * 4;

		pDC->DrawText (m_strText, rectText, DT_SINGLELINE | DT_VCENTER);
	} else {
		CString left = m_strText.Left(colonPos);
		CString right = m_strText.Right(m_strText.GetLength()-colonPos-1);

		// Draw text label left:
		CRect rectText = rectItem;
		rectText.left += afxGlobalData.GetTextWidth () +5;
		rectText.right -= 10; //rectText.left + afxGlobalData.GetTextWidth () * 4;

		pDC->DrawText (left, rectText, DT_SINGLELINE | DT_VCENTER);
		pDC->DrawText (right, rectText, DT_SINGLELINE | DT_VCENTER | DT_RIGHT);
		//TRACE("%s l=%d r=%d l=%d r=%d\n", left.GetBuffer(), rectItem.left, rectItem.right, rectText.left, rectText.right);
	}
	// Draw border:
	if (m_nStyle & TBBS_CHECKED)
	{
		pDC->Draw3dRect (rectItem, GetSysColor (COLOR_3DSHADOW), GetSysColor (COLOR_3DHILIGHT));
	}
	else if (bHighlight)
	{
		pDC->Draw3dRect (rectItem, GetSysColor (COLOR_3DHILIGHT), GetSysColor (COLOR_3DSHADOW));
	}
}


you can probably work it out from here
 
Share this answer
 
well it looks like i found a way to fix it by adding my own draw routine to the CMFCPopupMenu

here's the code, i don't have time to explain how it works but it does

in MainFrame.h i add an override
virtual BOOL OnShowPopupMenu (CMFCPopupMenu* pMenuPopup);


then in MainFrame.cpp
BOOL CMainFrame::OnShowPopupMenu (CMFCPopupMenu* pMenuPopup)
{
    CMDIFrameWndEx::OnShowPopupMenu(pMenuPopup);

	if (pMenuPopup == NULL || pMenuPopup->GetMenuBar () == NULL)
	{
		return TRUE;
	}

	// replace menu items with CTabStyleMenuItem to my my tab character
	ReplaceRangeWithTabStyleMenuItem(pMenuPopup, ID_EDIT_TRIPLETIZETKEY, ID_EDIT_GHOSTGKEY);
	ReplaceRangeWithTabStyleMenuItem(pMenuPopup, ID_EDIT_SNARE, ID_EDIT_RIDECYMBAL);
	ReplaceRangeWithTabStyleMenuItem(pMenuPopup, ID_EDIT_JOGLEFT, ID_EDIT_MOVENOTEDOWN);
	ReplaceRangeWithTabStyleMenuItem(pMenuPopup, ID_EDIT_TAILUP, ID_EDIT_TAILDOWN);
	return TRUE;
}

void CMainFrame::ReplaceRangeWithTabStyleMenuItem(CMFCPopupMenu* pMenuPopup, UINT startId, UINT endId)
{
	//-------------------------------------------------------------------
	// Replace all menu items by CTabStyleMenuItem objects:
	//-------------------------------------------------------------------
	for (UINT uiLineStyleCmd = startId; uiLineStyleCmd <= endId; uiLineStyleCmd++)
	{
		int iIndex = pMenuPopup->GetMenuBar ()->CommandToIndex (uiLineStyleCmd);
		if (iIndex >= 0)
		{
			CMFCPopupMenuBar* pMebuBar = pMenuPopup->GetMenuBar();
			pMebuBar->m_bDisableSideBarInXPMode = TRUE;
			// Obtain item text and style:
			CString strText	= pMebuBar->GetButtonText (iIndex);
			UINT uiStyle	= pMebuBar->GetButtonStyle (iIndex);

			// Replace menu item:
			pMebuBar->ReplaceButton(uiLineStyleCmd, CTabStyleMenuItem (uiLineStyleCmd, strText, uiStyle & TBBS_CHECKED));
		}
	}
}


the important part here is for every menu item in the range, i replace its class with CTabStyleMenuItem with ReplaceButton

my menu strings look like this now

CSS
BEGIN
    POPUP "Edit"
    BEGIN
        MENUITEM "Cu&t\tCtrl+X",                ID_EDIT_CUT
        MENUITEM "&Copy\tCtrl+C",               ID_EDIT_COPY
        MENUITEM "&Paste\tCtrl+V",              ID_EDIT_PASTE
        MENUITEM SEPARATOR
        MENUITEM "Tripletize:T", ID_EDIT_TRIPLETIZETKEY
        MENUITEM "Flam:F", ID_EDIT_FLAMFKEY
        MENUITEM "Accent:A", ID_EDIT_ACCENTAKEY
        MENUITEM "Ghost:G", ID_EDIT_GHOSTGKEY
        POPUP "Insert note"
        BEGIN
            MENUITEM "Snare:S",        ID_EDIT_SNARE
            MENUITEM "Kick (Bass drum):K",        ID_EDIT_BASS
            MENUITEM "Closed High Hat:H",        ID_EDIT_HIGHHAT
            MENUITEM "Open High Hat:O",        ID_EDIT_OPENHIGHHAT
            MENUITEM "Crash Cymbal 1:C",        ID_EDIT_CRASHCYMBAL1
            MENUITEM "Crash Cymbal 2:V",        ID_EDIT_CRASHCYMBAL2
            MENUITEM "Ride Cymbal:R",        ID_EDIT_RIDECYMBAL
        END
        POPUP "Move note"
        BEGIN
            MENUITEM "Move notes left:Left Arrow", ID_EDIT_JOGLEFT
            MENUITEM "Move notes right:Right Arrow", ID_EDIT_JOGRIGHT
            MENUITEM "Move note up:Up Arrow",  ID_EDIT_MOVENOTEUP
            MENUITEM "Move note down:Down Arrow", ID_EDIT_MOVENOTEDOWN
        END
        POPUP "Tail direction"
        BEGIN
            MENUITEM "Tail up:Shift+Up Arrow", ID_EDIT_TAILUP
            MENUITEM "Tail down:Shift+Down Arrow", ID_EDIT_TAILDOWN
        END
    END
END


the ':' character in the menu test string will be replaced by right aligning the following text in my special draw routine.

so create the class
class CTabStyleMenuItem : public CMFCToolBarMenuButton  
{
	DECLARE_DYNCREATE(CTabStyleMenuItem)

public:
	CTabStyleMenuItem(UINT uiCmdID = 0, LPCTSTR lpszText = NULL,
						BOOL bIsChecked = FALSE);
	virtual ~CTabStyleMenuItem();

protected:
	virtual void OnDraw (CDC* pDC, const CRect& rect, CMFCToolBarImages* pImages,
						BOOL bHorz = TRUE, BOOL bCustomizeMode = FALSE,
						BOOL bHighlight = FALSE,
						BOOL bDrawBorder = TRUE,
						BOOL bGrayDisabledButtons = TRUE);
};


and

IMPLEMENT_DYNCREATE(CTabStyleMenuItem, CMFCToolBarMenuButton)


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CTabStyleMenuItem::CTabStyleMenuItem(UINT uiCmdID, LPCTSTR lpszText,
									   BOOL bIsChecked) :
	CMFCToolBarMenuButton (uiCmdID, NULL /* HMENU */, -1 /*iImage*/, lpszText)
{
	if (bIsChecked)
	{
		m_nStyle |= TBBS_CHECKED;
	}
}
//*****************************************************************************************
CTabStyleMenuItem::~CTabStyleMenuItem()
{
}
//*****************************************************************************************
void CTabStyleMenuItem::OnDraw (CDC* pDC, const CRect& rect, CMFCToolBarImages* /*pImages*/,
			BOOL /*bHorz*/, BOOL /*bCustomizeMode*/, BOOL bHighlight,
			BOOL /*bDrawBorder*/, BOOL /*bGrayDisabledButtons*/)
{
	ASSERT_VALID (pDC);

	CRect rectItem = rect;
	rectItem.DeflateRect (1, 1);

	//--------------------
	// Fill item interior:
	//--------------------
	FillInterior (pDC, rectItem, bHighlight);

	// split test string at the ':'
	int colonPos = m_strText.Find(':');

	if(colonPos==-1) {
		// no colon, just normal print

		// Draw text label left:
		CRect rectText = rectItem;
		rectText.left += afxGlobalData.GetTextWidth () +5;
		rectText.right = rectText.left + afxGlobalData.GetTextWidth () * 4;

		pDC->DrawText (m_strText, rectText, DT_SINGLELINE | DT_VCENTER);
	} else {
		CString left = m_strText.Left(colonPos);
		CString right = m_strText.Right(m_strText.GetLength()-colonPos-1);

		// Draw text label left:
		CRect rectText = rectItem;
		rectText.left += afxGlobalData.GetTextWidth () +5;
		rectText.right -= 10; //rectText.left + afxGlobalData.GetTextWidth () * 4;

		pDC->DrawText (left, rectText, DT_SINGLELINE | DT_VCENTER);
		pDC->DrawText (right, rectText, DT_SINGLELINE | DT_VCENTER | DT_RIGHT);
		//TRACE("%s l=%d r=%d l=%d r=%d\n", left.GetBuffer(), rectItem.left, rectItem.right, rectText.left, rectText.right);
	}
	// Draw border:
	if (m_nStyle & TBBS_CHECKED)
	{
		pDC->Draw3dRect (rectItem, GetSysColor (COLOR_3DSHADOW), GetSysColor (COLOR_3DHILIGHT));
	}
	else if (bHighlight)
	{
		pDC->Draw3dRect (rectItem, GetSysColor (COLOR_3DHILIGHT), GetSysColor (COLOR_3DSHADOW));
	}
}


you can probably work it out from here
 
Share this answer
 
You this is the "BCG" example, I also do, they still did not get out. I click the button event event, can you help me. I Emai is 757624760@qq.com
 
Share this answer
 
This is your BCG example, I also do. There is no solution, can you help me find my source it.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900