WM_MENUSELECT not handled in CFrameWndEx windows - c++

I'm trying to handle the WM_MENUSELECT message in a VS2008 (and VS2010) SDI project based on a CFrameWndEx main frame window.
I create a simple project from the VS2008 wizard (single document, "MFC standard", "use classic menu" options) which results in something like (after added the WM_MENUSELECT message):
class CMainFrame : public CFrameWnd
{
///...
public:
afx_msg void OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu);
};
and
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_WM_MENUSELECT()
END_MESSAGE_MAP()
///...
void CMainFrame::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
{
CFrameWnd::OnMenuSelect(nItemID, nFlags, hSysMenu);
}
This works, when putting a breakpoint in CMainFrame::OnMenuSelect it is triggered when the menu is used (in that case everytime)
Replacing CFrameWnd by CFrameWndEx (or by creating a new project with the wizard with the options single document, "MFC standard", "use a menubar and toolbar" options)
class CMainFrame : public CFrameWndEx
{
///....
public:
afx_msg void OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu);
};
and
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWndEx)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
ON_WM_CREATE()
ON_WM_MENUSELECT()
END_MESSAGE_MAP()
///....
void CMainFrame::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
{
CFrameWndEx::OnMenuSelect(nItemID, nFlags, hSysMenu);
}
The message will never be triggered.
I've even stripped down the project using CFrameWndEx to the strict minimum and I still cannot get the message.
Any idea why ? tips, hints are always welcome.
Thanks.
Max.

The base class must be correct in the macro at the start of the message map (above the ON_WM_MENUSELECT). My guess is that you didn't change it to CFrameWndEx when you changed the base class.

Aaah, the good old times of single-stepping into the source code of MFC! It's always been the key ;-)
CMFCPopupMenu::SetSendMenuSelectMsg() is your new friend.
Just add this line in your CMainFrame::OnCreate() :
CMFCPopupMenu::SetSendMenuSelectMsg(TRUE);
The bottom line is that MFC menus and toolbars aren't the usual wrappers around standard Windows objects. They implement things differently, using a different model.
Now, for the sake of backward compatibility, you can ask those classes to act as their predecessors and send a WM_MENUSELECT, which they don't by default.
Note: There are probably new mechanisms or best practices to manipulate these objects. It's probably worth investigating them rather than forcing a compatibility setting.

Related

How to bring dialog to top when child control on it is clicked

Working with C++ app, using MFC.
I have an aux dialog that is created as a secondary dialog to the main application dialog. This aux dialog has several buttons etc. as child controls. If I click on the aux dialog background or in its nonclient area, it comes to the top as expected. But if I click on one of the child buttons, the button functions correctly but the dialog doesn't come to the top.
Is there some event I should handle in the dialog to bring it to the top when any child control is clicked? Or perhaps set some property in the dialog that will ensure that happens?
I'm suspicious I need to handle some activate or focus event that I'm not. Or maybe once the children handle the click, it needs to be propagated up? Or the other way around?
EDIT: Additional details answering question in comment:
Main dialog class CMyDlg and aux dialog class CAuxDlg are both derived from CDialog.
A blank dialog resource exists for each; all child controls etc are created programmatically.
class CMyDlg : public CDialog
{
public:
CAuxDlg *aux;
...
protected:
virtual BOOL OnInitDialog()
...
DECLARE_MESSAGE_MAP()
}
class CAuxDlg : public CDialog
{
...
protected:
...
DECLARE_MESSAGE_MAP()
}
Aux dialog is created in main dialog's OnInitDialog() method:
BOOL CMyDlg::OnInitDialog()
{
...
aux = new CAuxDlg(this);
aux->Create(IDD_AUX_DIALOG, GetDesktopWindow());
...
}
In the aux dialog resource, all properties are FALSE except for:
border: resizing
style: popup
title bar: true
tool window: true
use system font: true
All of my custom controls are derived from a custom base class CMyBase. I added a handler to this base class:
ON_WM_LBUTTONDOWN()
...
void CMyBase::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnLButtonDown(nFlags, point);
GetParent()->SetActiveWindow();
}
and ensured that classes derived from CMyBase that handle WM_LBUTTONDOWN also call the base class's handler:
class CMyCtrl : public CMyBase
...
void CMyCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// do stuff here
CMyBase::OnLButtonDown(nFlags, point);
}
and that seems to have done the trick.
Note 1: As mentioned in the comments on the question, if I add a standard CButton or CStatic or even CWnd to the aux dialog, clicking on that standard control does bring the aux dialog to the top. I don't know what the standard controls are doing that mine is not (other than elaborate mouse handling), but it must be something like this.
Note 2: It isn't enough to just call CWnd::OnLButtonDown(). Activating the containing aux dialog seems to be necessary.

Error with BEGIN_MESSAGE_MAP "a nonstatic member reference must be relative to a specific object" MFC

I am creating a breakout game using MFC, and now I am programming a level creator. For the user to choose which block is desired to create, I'm using a SplitButton. I have created the menu resource to pair with my SplitButton, but I want each one of the options in the menu to do something. Reading the documentation about how to assign a afx_msg void OnOptionSelected() to an event using BEGIN_MESSAGE_MAP I found this:
//Microsoft Documentation Code
BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
ON_COMMAND(ID_MYCMD, &CMyDoc::OnMyCommand)
END_MESSAGE_MAP()
And I did the same with my code, creating an afx_msg for each of my menu options in my menu resource
//My code in CCreateWindow.h (Inside DECLARE_MESSAGE_MAP())
afx_msg void OnToughBlockChosen();
afx_msg void OnSturdyBlockChosen();
afx_msg void OnWeakBlockChosen();
afx_msg void OnSpecialBlockChosen();
afx_msg void OnIndestructibleBlockChosen();
And thus their assignation
//My code in CCreateWindow.cpp (Inside BEGIN_MESSAGE_MAP)
ON_COMMAND(ID_CHOOSEBLOCKTYPE_TOUGHBLOCK, &CCreateWindow::OnToughBlockChosen())
ON_COMMAND(ID_CHOOSEBLOCKTYPE_STURDYBLOCK, &CCreateWindow::OnSturdyBlockChosen())
ON_COMMAND(ID_CHOOSEBLOCKTYPE_WEAKBLOCK, &CCreateWindow::OnWeakBlockChosen())
ON_COMMAND(ID_CHOOSEBLOCKTYPE_SPECIALBLOCK, &CCreateWindow::OnSpecialBlockChosen())
ON_COMMAND(ID_CHOOSEBLOCKTYPE_INDESTRUCTIBLEBLOCK, &CCreateWindow::OnIndestructibleBlockChosen())
But when I did this, the error E024 a nonstatic member reference must be relative to a specific object appears. So far I have an idea of how it can be solved (ON_COMMAND(ID_CHOOSEBLOCKTYPE_TOUGHBLOCK, &m_CreateWindow.OnToughBlockChosen())) but just I dunno if that's the way to fix it and if it is I dunno where to declare the object since I tried to do it in the .h and .cpp and none worked. I just don't know what to do now since I did the same as in the Microsoft Documentation and it explicitly stated:
The ON_COMMAND macro is used to handle command messages generated by menus, buttons, and accelerator keys.

How do I fix my OnRButtonDown() to detect right click for my dialog? (using mfc)

Right now my code isn't detecting a right click on it's dialog.
Am I missing anything? Also, is there anything important I should know about how
detection works in mfc?
Inside my .h file contains this method as a public:
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
In my .cpp file I have these guys:
void CGadgetAddedDialog::OnRButtonDown(UINT nFlags, CPoint point)
{
char debugStr[1000];
sprintf_s(debugStr, "pressed on: %d, %d", point.x, point.y);
OutputDebugStringA(debugStr);
// TODO: Add your message handler code here and/or call default
CDialogEx::OnRButtonDown(nFlags, point);
}
BEGIN_MESSAGE_MAP(CGadgetAddedDialog, CDialogEx)
ON_WM_ERASEBKGND()
ON_WM_RBUTTONDOWN()
ON_WM_CONTEXTMENU()
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
If you click on a child control with the right mouse key, the child control receives the message.
If you want to handle the right mouse click for a context menu than just use a handler for WM_CONTEXTMENU. This guaranties that also the context menu key is handled correct.
Also this message is transferred from a child to the parent if the child doesn't handle it.

Migrated MFC app from VC6 to VS2010, now OnInitDialog() not called for CPropertyPage subclass

I have been tasked with migrating our product's UI to VS2010. It is an MFC app, originally written in VC6. I have performed the following steps:
Converted the VC6 .dsp using VS2010
fixed up compile errors due to stricter VS2010 compiler
Removed all project references to VC6 mfc libs and directories
My problem is that for a dialog object (actually it's a CPropertyPage object), OnInitDialog() is not being called before other methods are. This causes an exception as OnInitDialog() needs to setup member variables.
The dialog class (CPAGEViewDefRecordFields) is subclassed from our own CValidatedPropertyPage, which in turn is derived from the MFC CPropertyPage class. The virtual method OnInitDialog() is present in all subclasses.
In the VS2010 version, when DoModal() is called on the containing property sheet, the OnInitDialog() method of the CPAGEViewDefRecordFields class is not being called. In the VC6 version, it is being called and all works ok.
In VC6, I can see that the message WM_INITDIALOG is sent, and handled in AfxDlgProc(), which in turn then calls OnInitDialog() of the dialog object.
In the VS2010 version, the first message that is processed is WM_NOTIFY, not WM_INITDIALOG.
Unfortunately I have no prior experience in MFC. What I am assuming that something has changed in the behaviour of MFC between the VC6 version and the VS2010 version. However I've not been able to find anything on the net which is similar to this.
Is there another migration step I have missed? Should I have to do something to the resources in the project when doing the migration?
I have checked that the resource is tied to the correct cpp file, as I can double click on the property page, and the IDE takes me to the correct file for the CPAGEViewDefRecordFields class.
If any of you have any ideas, I'd be very grateful.
Thanks!
Chris.
class CPAGEViewDefRecordFields : public CValidatedPropertyPage
{
public:
// Construction
CPAGEViewDefRecordFields(CWnd* pParent,
CXpViewProp* pViewProp,
CFont* pFont = NULL,
UINT nIDCaption = 0,
BOOL bSumOpRequired = TRUE,
BOOL bMinMaxRequired = TRUE,
BOOL bAllRecords = TRUE,
BOOL bShowInitSel = TRUE,
XLong lLimits = 0,
BOOL bSortSelTree = TRUE,
CXpThreshBaseLogProp* pThreshLogProp = NULL);
~CPAGEViewDefRecordFields();
// Dialog Data
//{{AFX_DATA(CPAGEViewDefRecordFields)
enum { IDD = IDD_VIEW_DEF_RECORD_FIELDS };
//}}AFX_DATA
// Overrides
// ClassWizard generate virtual function overrides
//{{AFX_VIRTUAL(CPAGEViewDefRecordFields)
virtual BOOL OnInitDialog();
//}}AFX_VIRTUAL
virtual BOOL OnSetActive();
virtual BOOL OnKillActive();
virtual void OnOK();
protected:
...
// Generated message map functions
//{{AFX_MSG(CPAGEViewDefRecordFields)
afx_msg void OnPbRemove();
afx_msg void OnPbAdd();
afx_msg void OnDblclkAvailableFields(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnDblclkSelectedFields(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
...
UPDATE:
After some debugging, I can see what I think is the problem. However, not being an MFC programmer, I don't understand it.
I can see that OnInitDialog() is being called for the property sheet, and that a WM_INITDIALOG is then sent from the property sheet to the property pages. however, at some point in the windows internals, a WM_NOTIFY message is being sent, so this is the first message that is received, not the expected WM_INITDIALOG
I've highlighted the points on the stack trace, attached - can anyone explain why this is occuring? Is this normal behaviour - should I be catering for this in the future?
I've actually found a workaround, and that's to have an initialised flag, so that no code is executed until OnInitDialog() has been called. This is not the best solution, and I fear is more of a hack, so i would still appreciated any understanding of these messages. (I'm not an MFC programmer by trade you see!)
thanks!
OnInitDialog is called after all the dialog controls are created and just before the dialog box is displayed.
Thought I'd better answer this.
The answer came from a SO user's comment:
Your workaround with an initialized flag is the same as I would do. It looks like a tree view sends a notification when the tree view is created but your dialog isn't ready yet. You might not know when other controls do the same thing, so you need an initialized flag
The "workaround" is the only way to guarantee that the dialog is ready.

How do I create and use a CFormView in an MFC regular DLL? (visual studio 2008)

I recently asked this question which got me started in the right direction - at least for loading the MFC DLL and trying to show a dlg box.
The problem is, the typical dialog box is horrible as a main window for an APP. It is quite simple for me to create a new exe project to do what I want, but the problem is that I have a DLL and the tools just don't seem to allow me to hook up the classes to the windows forms in the resource editor. Thus I can't seem to handle the events that I need.
My questions:
How do I create and display a CFormView (based on an IDD_FORMVIEW I created in a resource editor) in an MFC DLL project?
How do I get the form to show and to process input?
How do I add event/message handlers for that window? (The menu item to do that from the .rc editor is greyed out)
How do I set a menu to the formview? (the properties for the resource in the editor do not let me associate it with a menu resource. (I can't figure out why)
The links I have been looking at are pretty light and ambiguous about how to do it. Most of them assume I can create a mainframe as an MFC single document app via the "wizard" - which is not the case.
Right now I call Create() on the window class I made and pass in the CWnd of the desktop as the parent.
I am not sure I have subclassed the CFormView Correctly. In fact, I am pretty sure I have done little of what I need to do, though I tried to follow the instructions I have seen.
I then call ShowWindow(SW_SHOW), but I still see nothing.
I think this SHOULD be simple. All I want to do is show the form I created in the form editor.
How do I do that and what is the simplest way?
Here is some code - the cpp code that calls it
MainForm *mf = new MainForm();
mf->Create(CWnd::GetDesktopWindow());
mf->ShowWindow(SW_SHOW);
Here is the .h file for the MainForm class
#include "afxcmn.h"
// MainForm form view
class MainForm : public CFormView
{
DECLARE_DYNCREATE(MainForm)
public:
MainForm();
virtual ~MainForm();
public:
virtual BOOL Create(CWnd* pParent);
public:
enum { IDD = IDD_FORMVIEW_MAIN };
#ifdef _DEBUG
virtual void AssertValid() const;
#ifndef _WIN32_WCE
virtual void Dump(CDumpContext& dc) const;
#endif
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
public:
CListCtrl m_SymbolSetList;
};
and here is the cpp for MainForm
#include "stdafx.h"
#include "MainForm.h"
// MainForm
IMPLEMENT_DYNCREATE(MainForm, CFormView)
MainForm::MainForm()
: CFormView(MainForm::IDD)
{
}
MainForm::~MainForm()
{
}
void MainForm::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_SYMBOLSETS, m_SymbolSetList);
}
BEGIN_MESSAGE_MAP(MainForm, CFormView)
END_MESSAGE_MAP()
// MainForm diagnostics
#ifdef _DEBUG
void MainForm::AssertValid() const
{
CFormView::AssertValid();
}
#ifndef _WIN32_WCE
void MainForm::Dump(CDumpContext& dc) const
{
CFormView::Dump(dc);
}
#endif
#endif //_DEBUG
BOOL MainForm::Create(CWnd* pParent)
{
CRect rect;
//pParent->GetClientRect(rect);
return CFormView::Create(NULL, NULL, WS_CHILD | WS_VISIBLE, rect, pParent, 0, NULL);
}
I would investigate creating and showing your window in a separate MFC UI thread. MFC has got its own mechanism for delivering Windows messages to the CWnd dervived objects called message pump and it needs to initialize its internal structures for it to work. I think you need to use framework function call to do it. Try this version of AfxBeginThread:
CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority=HREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
and pass your form as a pThreadClass; there is RUNTIME_CLASS macro that will do it for you. You then end the thread from withing the frame code.
As for the message handling, try opening properties for your form in the resource editor and click the Messages button on the top. You can than add your handlers to the messages that you need to handle.
I was able to associate the Menu property with a resource ID of a menu. I am not sure why you are not able to do so. The resource editor might get confused sometimes if you select a different resource in the tree view but your main window displays a different resource.
Regards