Handling accelerator in a dialog - mfc

What step am I missing?
I have an accelerator table:
I have added a member variable to my app class:
HACCEL m_hAccel;
I have added the following override:
BOOL CMeetingScheduleAssistantApp::ProcessMessageFilter(int code, LPMSG lpMsg)
{
if (m_hAccel)
{
if (::TranslateAccelerator(m_pMainWnd->m_hWnd, m_hAccel, lpMsg))
{
AfxMessageBox(_T("Found"));
return(TRUE);
}
}
return CWinAppEx::ProcessMessageFilter(code, lpMsg);
}
I am only using the popup message box for debugging and it confirms that the key press is being detected.
My menu resource is setup correctly:
So my menu is operation with menu handlers. And I have set up the program to load the accelerator table. If I press, for example Ctrl+Shift+X whilst it is detected by the accelerator table why doesn't my popup dialog actually process it?
I should point out that my main app dialog displays one of two editors. So when a editor is displayed I load the accelerator table for that editor.
What step am I missing? Why is the dialog not processing the accelerator?
Update
I found this answer How to make child control handle accelerator command of parent CView.
I found that if I add a HACCEL directly to my popup dialog and then just use PreTranslateMessage:
if (m_hAccelTable)
{
if (::TranslateAccelerator(GetSafeHwnd(), m_hAccelTable, pMsg))
return TRUE;
}
It works.

For keyboard accelerators in dialogs I do this:
In OnInitDialog
BOOL CMyDlg::OnInitDialog()
{
...
m_hAccel = LoadAccelerators ( AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_DLGACCEL));
...
}
PreTranslateMessage
BOOL CMyDlg::PreTranslateMessage(MSG* pMsg)
{
if (m_hAccel)
{
if (::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
return(TRUE);
else
return CDialog::PreTranslateMessage(pMsg);
}
else
return CDialog::PreTranslateMessage(pMsg);
}
In OnDestroy
void CMyDlg::OnDestroy()
{
...
VERIFY(DestroyAcceleratorTable(m_hAccel)) ;
CDialog::OnDestroy();
}
Message map:
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
...
ON_COMMAND(IDC_SOMECOMMANDID, OnDoSomething)
ON_UPDATE_COMMAND_UI(IDC_SOMECOMMANDID, OnUpdateDoSomething)
...
END_MESSAGE_MAP()
Command handlers
void CMyDlg::OnUpdateDoSomething(CCmdUI* pCmdUI)
{
...
pCmdUI->Enable(...) ;
}
void CMyDlg::OnDoSomething()
{
...
}
Accelerator table in .rc file
IDR_DLGACCEL ACCELERATORS
BEGIN
"A", IDC_SOMECOMMANDID, VIRTKEY, CONTROL, NOINVERT // Ctrl+A
...
END
That's all.

Related

MFC Win32 | LButtonUp not being received after clicking toolbar button (CMFCToolBarButton::OnClick)

What I'm trying to achieve
Well, the title might not have explained the problem very well, so here goes:
I am trying to create a Win32 app using MFC that lets you edit and inspect other windows.
I want the user to be able to select other windows.
I got inspired by the "Find Window Process" tool on the toolbar on sysinternals applications such as ProcessExplorer.
The way it works is you click, then the window disappears, and then you drag it over the window you want to select. A border pops up around it and when you let go, it selects the window the mouse is over.
My problem
The problem I was facing is that I don't know how to detect when the user lets go of the mouse on another window.
I detect mouse down using OnClick in CMFCToolBarButton
I tried using SetCapture() but that did nothing.
I tried using OnNcLButtonUp and OnLButtonUp but neither of them worked. (alongside SetCapture)
Here's my code so far (ChildView.cpp):
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_UPDATE_COMMAND_UI(ID_TB_LOCATEWINDOW, &CChildView::EnableToolbarButton)
ON_UPDATE_COMMAND_UI(ID_TOOLS_MESSAGELAUNCHER, &CChildView::EnableToolbarButton)
ON_WM_XBUTTONUP()
// ON_WM_LBUTTONUP()
ON_WM_NCLBUTTONUP()
END_MESSAGE_MAP()
....
void CChildView::LocateWindow()
{
GetParentFrame()->ShowWindow(SW_MINIMIZE);
SetCapture();
}
void CChildView::OnNcLButtonUp(UINT nHitTest, CPoint point)
{
ReleaseCapture();
GetParentFrame()->ShowWindow(SW_NORMAL);
MessageBox(L"Stuff", L"");
CWnd::OnNcLButtonUp(nHitTest, point);
}
I want to mention that the LocateWindow function gets called when the toolbar button is clicked (as in mouse down, not mouse down AND up)
It is called from the OnClick function.
Here's the code for that:
(I replace the button with OnToolbarReset)
// CLocateWindowButton.cpp : implementation file
//
#include "pch.h"
#include "WindowHacker.h"
#include "MainFrm.h"
#include "CLocateWindowButton.h"
// CLocateWindowButton
IMPLEMENT_SERIAL(CLocateWindowButton, CMFCToolBarButton, 1)
// CLocateWindowButton member functions
CLocateWindowButton::CLocateWindowButton()
{
}
CLocateWindowButton::CLocateWindowButton(CMainFrame* mainFrame, UINT uiCmdID, LPCTSTR lpszText) : CMFCToolBarButton(uiCmdID, NULL, lpszText)
{
this->mainFrame = mainFrame;
}
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
return FALSE;
}
void CLocateWindowButton::CopyFrom(const CMFCToolBarButton& src)
{
CMFCToolBarButton::CopyFrom(src);
mainFrame = ((CLocateWindowButton&)src).mainFrame;
}
//void CLocateWindowButton::AssertValid() const
//{
// CMFCToolBarButton::AssertValid();
//
// // TODO: Add your specialized code here and/or call the base class
//}
UPDATE:
It seems to work when I put it inside an LButtonDown event, it just seems to not work when it is being detected from OnClick in CMFCToolBarButton
I found that in CMFCToolBar::OnLButtonUp, after calling OnClick in the button, it recaptures the cursor, invalidating our SetCapture.
BUT if I return TRUE instead of FALSE in OnClick, the mouse is not recaptured.
So changing this:
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
//ReleaseCapture();
this->mainFrame->SetCapture();
return FALSE;
}
To this:
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
//ReleaseCapture();
this->mainFrame->SetCapture();
return TRUE; // The line is changed here
}
The message gets sent to CMainFrame instead.

MFC: How do you implement a context menu for CRichEditView in a CTabView tab?

I have a CTabView with one of the tabs a CRichEditView. Rich text is added to the control and shows fine. If I select text within the CRichEditView the toolbar edit items work fine (for example, copy highlights, and if I click on it, it copies to the clipboard). However, I found that if I selected text and right click there is no context menu with a CRichEditView like there was with CEditView. Searching the Internet, I found an implementation for CRichEditView::GetContextMenu() to try and use. It first had an assert failure because the CDocument is not a rich text type, so for testing, I removed it (commented out below) and ended up with the following:
HMENU CMyRichView::GetContextMenu(WORD seltyp, LPOLEOBJECT lpoleobj, CHARRANGE* lpchrg)
{
// TODO: Add your specialized code here and/or call the base class
/*
CRichEditCntrItem* pItem = GetSelectedItem();
if (pItem == NULL || !pItem->IsInPlaceActive())*/
{
CMenu menuText;
menuText.LoadMenu(IDR_CONTEXT_EDIT_MENU);
CMenu* pMenuPopup = menuText.GetSubMenu(0);
menuText.RemoveMenu(0, MF_BYPOSITION);
return pMenuPopup->Detach();
}
}
Where the IDR_CONTEXT_EDIT_MENU is:
IDR_CONTEXT_EDIT_MENU MENU
BEGIN
POPUP "edit"
BEGIN
MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY
END
END
Now when I right-click I see the context menu. However, when I choose "copy", nothing happens. So I mapped the ID_EDIT_COPY so I could set a break point to see if it was called.
void CMyRichView::OnEditCopy()
{
// TODO: Add your command handler code here
ASSERT_VALID(this);
GetRichEditCtrl().Copy();
}
It's not if the context item is used, but it is if the toolbar is used.
What am I missing and doing wrong?
TIA!!
If the message goes to CTabView then add CTabView::OnEditCopy handler.
Otherwise, you can override PreTranslateMessage as shown below, this will make sure the message is sent to CMyRichEditView::OnEditCopy.
BOOL CMyRichEditView::PreTranslateMessage(MSG *msg)
{
if(msg->message == WM_CONTEXTMENU || msg->message == WM_RBUTTONDOWN)
{
CMenu menu;
menu.LoadMenu(IDR_CONTEXT_EDIT_MENU);
int c = menu.GetMenuItemCount();
CMenu* popup = menu.GetSubMenu(0);
popup->TrackPopupMenu(0, msg->pt.x, msg->pt.y, this, NULL);
return TRUE;
}
return CRichEditView::PreTranslateMessage(msg);
}

initialize double click on edit control in MFC

I am trying to set an mouse click event on editbox and when I am double clicking on edit box it should bring up a message box.
ON_WM_LBUTTONDBLCLK(IDC_EDITItem, &MessageManage::OnItemDoubleClick)
void MessageManage::OnItemDoubleClick()
{
MessageBox( m_strItemMsg, "Sample code", MB_OK | MB_ICONINFORMATION );
}
At alternative is to just use PreTranslateMessage on your dialog:
BOOL CMFCApplication1Dlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_LBUTTONDBLCLK &&
pMsg->hwnd == ::GetDlgItem(m_hWnd, IDC_EDIT1))
{
AfxMessageBox(_T("Run Code"));
return TRUE; //Important!!! Message is handled
}
return CDialogEx::PreTranslateMessage(pMsg);
}
It's not taking double click event from edit box
One way to accomplish this is to derive your own class from CEdit and handle ON_WM_LBUTTONDBLCLK(). The following code responded to the double click on an edit control in a sample program.
BEGIN_MESSAGE_MAP(MyEdit, CEdit)
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()
// MyEdit message handlers
void MyEdit::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CEdit::OnLButtonDblClk(nFlags, point);
}

Example code for CMFCMenuButton?

Sorry for the newbie question, but can anyone point me at sample code that illustrates the use of the CMFCMenuButton? The Microsoft help refers to "New Controls samples", but these samples seem to be in the Visual Studio 2008 "Feature Pack", and this refuses to install on my system since I'm running VS 2013 and don't have VS 2008. I haven't been able to find the samples as stand-alone code.
To be specific, I have a dialog bar in which I want a button labelled Save with drop-down options of Save All and Save Visible (with Save All the default). But any working code would at least get me started.
Declare data members:
CMFCMenuButton m_button_menu;
CMenu m_menu;
Also add the button's id to message map and data exchange:
BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
ON_BN_CLICKED(IDC_MFCMENUBUTTON1, OnButtonMenu)
...
END_MESSAGE_MAP
void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_MFCMENUBUTTON1, m_button_menu);
}
Define:
BOOL CMyDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
//...
m_menu.LoadMenu(IDR_MENU1);
m_button_menu.m_hMenu = m_menu.GetSubMenu(0)->GetSafeHmenu();
return TRUE;
}
Where IDR_MENU1 is a regular menu bar and we get its first submenu. For example:
IDR_MENU1 MENU
BEGIN
POPUP "Dummy"
BEGIN
MENUITEM "&Item1", ID_FILE_ITEM1
MENUITEM "&Item2", ID_FILE_ITEM2
END
END
If button's drop-down arrow is clicked, a popup menu appears, menu result is passed to OnButtonMenu. If left side of button is clicked, then OnButtonMenu is called directly, without showing a popup menu.
void CMyDialog::OnButtonMenu()
{
CString str;
switch (m_button_menu.m_nMenuResult)
{
case ID_FILE_ITEM1:
str = L"first menu item clicked";
break;
case ID_FILE_ITEM2:
str = L"second menu item clicked";
break;
default:
str = L"Button click (popup menu did not appear, or menu ID is not handled)";
break;
}
MessageBox(str);
}
** When working with docking controls, dialog bars, etc. MFC may run its own subclass, I don't think DoDataExchange gets called. m_button_menu could be invalid. GetDlgItem can be used to find the correct pointer:
CMFCMenuButton* CMyDlgBar::GetButtonMenu()
{
CMFCMenuButton* pButton = &m_button_menu;
if (!IsWindow(pButton->m_hWnd))
pButton = (CMFCMenuButton*)GetDlgItem(IDC_MFCMENUBUTTON1);
return pButton;
}
Everywhere else we use GetButtonMenu() instead of m_button_menu. For example:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
//...
m_dlgbar.Create(...);
m_dlgbar.m_menu.LoadMenu(IDR_MENU1);
m_dlgbar.GetButtonMenu()->m_hMenu = m_dlgbar.m_menu.GetSubMenu(0)->GetSafeHmenu();
return 0;
}
void CMainFrame::OnButtonMenu()
{
CString str;
switch (GetButtonMenu()->m_nMenuResult)
...
}
What if the Drop-Down Arrow does not show?
Then read the answer here that explains the changes needed to your RC file.

how to correctly send CMFCMenuButton selected ID to the message map?

firstly let me describe what I have:
scenario: CMFCMenuButton, loaded with a CMenu, in a dialog test: click on an item of the menuresult: the message map will get the ID of the CMFCMenuButton and not the ID of the menu
how to get the actual menu ID clicked: use CMFCMenuButton::m_nMenuResultThe idea is that I want to have menu items and buttons in this dialog, and there would be buttons that share IDs with the menu items.So in the handler that I've created for the menu button I can get that m_nMenuResult and send it to the dialog or do whatever I want, but that doesn't seem to be how the CMFCMenuButton should work. What is the correct way of doing it?
CodeHere follows an example on how you can reproduce this.
I've used ON_COMMAND_RANGE also with IDC_MFCMENUBUTTON1 just to reuse the code for the OnMenu function
void CRepositionDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_MFCMENUBUTTON1, m_cmfcMenuButton);
}
BEGIN_MESSAGE_MAP(CRepositionDlg, CDialog)
ON_COMMAND_RANGE(IDC_MFCMENUBUTTON1,IDC_MFCMENUBUTTON1,OnMenu)
ON_COMMAND_RANGE(IDC_MENU1, IDC_MENU11, OnMenu)
END_MESSAGE_MAP()
// CRepositionDlg message handlers
afx_msg void CRepositionDlg::OnMenu(UINT nID)
{
CString csMessage;
csMessage.Format(L"OnMenu(%d)",nID);
AfxMessageBox(csMessage);
if(nID == IDC_MFCMENUBUTTON1)
{
OnMenu(m_cmfcMenuButton.m_nMenuResult);
}
}
BOOL CRepositionDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
CMenu* pMenu = new CMenu;
pMenu->CreatePopupMenu();
for(int i = IDC_MENU1; i <= IDC_MENU11; i++)
{
CString csMenu;
csMenu.Format(L"menu %d",i);
pMenu->AppendMenuW(MF_STRING,i,csMenu);
}
m_cmfcMenuButton.m_hMenu = pMenu->GetSafeHmenu();
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
If you code a handler for BN_CLICKED for the menu button, it will respond with 0 for m_nMenuResult if the click is on the button, or, m_nMenuResult will contain the ID of the menu item selected. If that's not what you wanted, I think you're fighting against the way the button works. Your only other option would be to create your own class to represent a menu button and add the behavior you want.