Introduction
Welcome Gentle reader. The purpose of this article is to demonstrate the method
with which to display a popup menu from a CRicheditCtrl
after the user has right
clicked the mouse, but first a little history
Yea verily...in the beginning there was the magic of Windows 3.1 and
lo it was good(ish). And so it came to pass that years went by seemingly without
incident but lo behind the scenes hoards of Microsoft(tm)
minions worked feverishly for an on time release of new magic...Windows 95.
As part of the common controls to extend the much revered CEdit
class,
CRicheditCtrl
was introduced. And CEdit
class had some good magic for when you right clicked on it,
a wee context menu popped up so one could cut, paste and so on. Programmers looked on
this and saw it was good (ish). But alas CRicheditCtrl
had no direct method for displaying a context menu...and so it is not good, it is
in fact a darn pain!
So gentle reader I thought it was about time to provide a quick article on
how to display a context menu from a CRicheditCtrl after a right click of the mouse.
Alas though there seemed to be a hitch dear reader as, for reasons best known to
themselves, MS decided it wasn't going to make it totally easy (i.e. a simple override) to
catch the WM_RBUTTONDOWN
event from a CRichEditCtrl
. They chose instead to make it a a bit more
complicated (and I'm sure there's a very good reason which I just haven't researched.)
To begin with create a simple dialog based application, we'll call ours PopupDemo. Add a
menu resource ( let's call ours IDR_MENU1) also, in the main dialog add a CRichEditCtrl
member,
m_RichEdit
with IDC_RICHEDIT
. Don't Forget to add
AfxInitRichEdit();
in the application's Initinstance
or the application will not work...
Next, use the class wizard to generate an OnInitDialog
member function for
PopupDemoDlg
.
Inside this put the following
BOOL CPopupDemoDlg::OnInitDialog()
{
m_RichEdit.SetEventMask(ENM_MOUSEEVENTS);
...
This tells the RichEditCtrl that we want all mouse events reflected to the main window.
We do this so we can catch them in a WM_NOTIFY handler.
To catch these mouse events which are being reflected to CPopupDemoDlg,
we need to trap the event on OnNotify to do this...
The RichEditCtrl
sends the message "EN_MSGFILTER
" to the parent, now to get at which
message has been reflected (because it could be a left click or a mouse move) we need to
examine the message filter, so we cast lParam
to MSGFILTER
*
MSGFILTER * lpMsgFilter = (MSGFILTER *)lParam;
instead of the usual;
NMHDR * pnmh = (LPNMHDR)lParam;
Then we examine the result and test for which
actual windows message and which window we're dealing with.
if ((wParam == IDC_RICHEDIT) && (lpMsgFilter->nmhdr.code == EN_MSGFILTER)
& (lpMsgFilter->msg == WM_RBUTTONDOWN))
So the final
OnNotify
handler looks something like...
BOOL CPopupDemoDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
MSGFILTER * lpMsgFilter = (MSGFILTER *)lParam;
if ((wParam == IDC_RICHEDIT) && (lpMsgFilter->nmhdr.code == EN_MSGFILTER)
&& (lpMsgFilter->msg == WM_RBUTTONDOWN))
{
CPoint point;
::GetCursorPos(&point);
CMenu menu;
DWORD dwSelectionMade;
VERIFY(menu.LoadMenu(IDR_MENU1) );
CMenu *pmenuPopup = menu.GetSubMenu(0);
ASSERT(pmenuPopup != NULL);
dwSelectionMade = pmenuPopup->TrackPopupMenu( (TPM_LEFTALIGN|TPM_LEFTBUTTON|
TPM_NONOTIFY|TPM_RETURNCMD),
point.x, point.y, this
);
pmenuPopup->DestroyMenu();
}
return CDialog::OnNotify(wParam, lParam, pResult);
}
However... dear reader - there is a sneaker, faster way to code a method for dealing with the
same event. Just override PreTranslateMessage(MSG* pMsg)
(again use the class wizard). Do
something similar to the following (this is not in the download files)
BOOL CPopupDemoDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message ==WM_RBUTTONDOWN)
{
CWnd * pWnd = (CWnd*) GetDlgItem(IDC_RICHEDIT);
if (pWnd ==GetFocus())
{
CMenu menu;
DWORD dwSelectionMade; VERIFY(menu.LoadMenu(IDR_MENU1));
CMenu *pmenuPopup = menu.GetSubMenu(0);
ASSERT(pmenuPopup != NULL);
dwSelectionMade = pmenuPopup->TrackPopupMenu( (TPM_LEFTALIGN|
TPM_LEFTBUTTON|
TPM_NONOTIFY|
TPM_RETURNCMD),
pMsg->pt.x,
pMsg->pt.y, this
);
pmenuPopup->DestroyMenu();
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Yea - though there are probably other ways of doing this these, gentle reader, are what I have found to be two of the easiest ways of achieving the aim. And lo, it is good.
Here-in ends the lesson.