MFC: Didn't get EN_CHANGE message within derived CEdit - c++

I see very strange behavior of CEdit. In my derived class from CEdit control I need to catch text change event. I do it via ON_CONTROL_REFLECT_EX
class CSomeDerivedEdit : public CEdit
{
DECLARE_DYNAMIC(CSomeDerivedEdit )
public:
CSearchEditCtrl();
protected:
DECLARE_MESSAGE_MAP()
afx_msg BOOL OnEnChange();
...
};
in cpp
IMPLEMENT_DYNAMIC(CSomeDerivedEdit , CEdit)
BEGIN_MESSAGE_MAP(CSomeDerivedEdit , CEdit)
ON_CONTROL_REFLECT_EX(EN_CHANGE, &CSomeDerivedEdit::OnEnChange)
END_MESSAGE_MAP()
This control I create within some list control as child window, when I create it and pass as parent window pointer to list control everything working fine and I get EN_CHANGE events but when I pass as parent window header control of list control events didn't resive.
m_someEdit.Create( WS_CHILDWINDOW|WS_VISIBLE, rcRect, this, IDC_EDIT); here everything fine
m_someEdit.Create( WS_CHILDWINDOW|WS_VISIBLE, rcRect, GetHeaderCtrl(), IDC_EDIT); here also control create fine and I see it , but on_control_reflect didn't called when I type in edit control.

ON_CONTROL_REFLECT_EX can only work, if the receiver of the WM_COMMAND message is also subclassed in the MFC. If it is a pure windows control and not subclassed with the MFC the WM_COMMAND message is never reflected to any child.
Remember: The reflection works, because the MFC first handles a WM_COMMAND message from a child in the parent first and offers it back to the child control. And if not handled the WM_COMMAND message is handled inside the parent. Standard window controls doesn't know reflection and always handle WM_COMMAND messages by itself....
So if the header control is not subclassed by the MFC the reflection will not work.

I think you should read the MSDN. When you derive the class CEdit, you should put the ON_EN_CHANGE message handler between BEGIN_MESSAGE_MAP() and END_MESSAGE_MAP() macros. On the other hand, the OnEnChange function, whose declaration is:
afx_msg void OnEnChange();
does not return a BOOL value. The ON_EN_CHANGE message handler should be like this (from MSDN):
BEGIN_MESSAGE_MAP()
ON_EN_CHANGE(ID_OF_THIS_CONTROL,OnEnChange)
END_MESSAGE_MAP()
Hope this will help you.

Related

MFC: Notification after a control is created

I'm new to MFC. I chose to create an office style MFC app through the wizard in VS2017. I now want to extend CMFCShellTreeCtrl so I created another class with that as the base class. The basics are fine. My issue is that I want to do something like:
whatever MyClass::FuncitonCalledAfterControlCreated(...)
{
SetFlags(GetFlags() | SHCONTF_NONFOLDERS);
ModifyStyle(0x0, TVS_CHECKBOXES);
}
But I'm having trouble figuring out what virtual function to override or am I supposed to do one of those message mapping things? I would guess that whatever it is, it would be common to all controls? Anyway, what would be the appropriate function?
TIA!!
If control is derived from CWnd a WM_CREATE is issued which can be directed to the control via a message map of:
ON_WM_CREATE()
And member function:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
If on a dialog resource the WM_CREATE won't occur. Some say you can use PreSubClassWindow but on a case of testing Create(), that call comes BEFORE the CreateWindowEx call so won't work for setting the TVS_CHECKBOX style. I didn't try a CDialog with a tree control and check the call stack.

Handling Menu Selection from View Class with MFC(Visual Studio)

I'm using Visual Studio and MFC to write a program that will take mouse input from the user to draw a polygon and clip it against a rectangle when the respective menu button is pressed. My problem is, the user input and drawing is handled in the view class(CCssample1View, to be exact), while the menus are in CMainFram class. Even when I define OnMenuSelect function in CCssample1View, it doesn't get notified when a menu button is selected.
Is there a way to associate the menu buttons with the view class so that when they are pressed, the view class' OnMenuSelect function is notified, instead of, or in addition to, that of Main Frame? Or is there another way of handling this problem?
P.S. I literally started using Visual Studio, MFC and OpenGL about 6 hours ago, so I might have understood something monumentally wrong.
Update:
Here's the message maps for the view:
BEGIN_MESSAGE_MAP(CCssample1View, CView)
//{{AFX_MSG_MAP(CCssample1View)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_SIZE()
ON_COMMAND(ID_DBL_BUF, OnDblBuf)
ON_COMMAND(ID_NO_DBL_BUF, OnNoDblBuf)
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
ON_WM_MENUSELECT()
END_MESSAGE_MAP()
and the main frame:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code !
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
creation of the menu(in main frame's OnCreate function):
CMenu *menu = new CMenu;
menu->CreateMenu();
menu->AppendMenuA(MF_STRING, 2002, "Clip");
SetMenu(menu);
lastly, view's event handler:
void CCssample1View::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
{
CView::OnMenuSelect(nItemID, nFlags, hSysMenu);
while(1);
// TODO: Add your message handler code here
}
Still, nothing happens when I press the menu button(by that I mean the program runs on without the infinite loop taking effect). What am I doing wrong?
Why do you want to use WM_MENUSELECT?
WM_MENUSELECT is sent only to the main Frame that holds the menu. This Windows message is never routed.
Instead use a WM_COMMAND (ON_COMMAND) handler. WM_COMMAND handlers are routed through the view, Frame, doc and application object according to this documentation http://msdn.microsoft.com/en-us/library/shfzay75.aspx and this http://msdn.microsoft.com/en-us/library/xt2c310k.aspx
If you want to add a handler for more command items you can use ON_COMMAND_RANGE and others.
PS: You have already ON_COMMAND handler in your code!
Menu commands can be handled in the main frame, or the view, or the document. Just add the appropriate entry in the message map for the class where you want to handle the message. Each command can be handled in one place only, so if you already have a message map entry in the main frame and don't want to handle it there remove the line from the main frame message map.

MFC catch Control's Message of child from parent without create class handler for child?

I have a CDialog created by wizard named CDialogParent, then create a child dialog template has IDD= IDD_CHILD_DLG1, in this child dialog I put one button IDC_BTN1 (I don't create class handler for this child).
BOOL CDialogParent::OnInitDialog()
{
....
CDialog *pChild = new CDialog();
pChild->Create(IDD_CHILD_DLG1, this);
pChild->ShowWindow(SW_NORMAL);
}
Normally, I need to create new class handler CDialogChild for child and add message map like:
BEGIN_MESSAGE_MAP(CDialogChild, CDialog)
ON_BN_CLICKED(IDC_BTN1, &CDialogChild::OnBnClickedBtn1)
END_MESSAGE_MAP()
Problem that I want to catch control's message IDC_BTN1 of child dialog BUT by declare message map in CDialogParent like:
BEGIN_MESSAGE_MAP(CDialogParent, CDialog)
ON_BN_CLICKED(IDC_BTN1, &CDialogParent::OnBnClickedBtn1)
END_MESSAGE_MAP()
How to do this without create new class handler?
Thanks for help!
In short: You cannot.
There are 2 reasons why this is not possible:
Control IDs are unique among siblings (i.e. controls sharing the same parent window) only. Different dialogs can use the same ID for different controls.
I'll assume you mean Owned dialog when you say Child dialog (those are different concepts, but the following rationale is the same). When setting up an owner-owned window relationship, that relationship is based on window handles (HWND). There is no additional C++ type information available. Both the type and object pointer are required to call the appropriate class member in a message map.
If you want to be informed about an event raised in an owned dialog, implement a message handler in the owned dialog's class and post a message to the owning dialog.

Help please: No BN_CLICKED for custom radio button (MFC, VC++6)

I have derived a CButton class and created my own radion button control. Its all working nicely with the exception that I can't get the parent dialog to detect when the radio button it clicked.
The parent dialog will detect the radio button click if I call CButton::OnLButtonUp() but the problem in doing that is that the framework also draws the radio button as well. I don't want to do that as I am drawing the radio button myself.
Can somebody please tell me how to stop Windows/MFC framework from drawing the control in this case? If I don't call CButton::OnLButtonUp() then yeah, Windows/MFC won't draw the control but my parent dialog won't get a BN_CLICKED notification either.
I know I could send a custom message back to my dialog but I don't want that - I want compatability with the BN_CLICKED message.
As you will see below, I have also tried posting a message back to the owning dialog and this doesn't work either.
void CNCCheckBox::OnLButtonUp(UINT nFlags, CPoint point)
{
if( m_Owner )
m_Owner->PostMessage( BN_CLICKED, (WPARAM) IDC_RAD_1/*GetDlgCtrlID()*/, (LPARAM) this->m_hWnd );
//CButton::OnLButtonUp(nFlags,point); // Can't use this!!
}
I've solved it. I got rid of OnDrawItem() (AFX_MSG), and added DrawItem (AFX_VIRTUAL) instead. Also, in PreSubClassWindow() I modify the style to the button is treated as a BS_PUSHBUTTON and BN_CLICKED events are now being sent to my parent dialog.
So in short:
- Don't use OnPaint()
- Don't use OnDrawItem()
- Use:
//{{AFX_VIRTUAL(CNCCheckBox)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL

Equivalent of OnFinalMessage for MFC windows?

ATL CWindow class has a useful virtual method OnFinalMessage which is called after the last window message for the window is processed - at this point it is safe to destroy or deleted any objects associated with the window. Is there any equivalent for windows derived from the MFC CWnd class?
PostNcDestroy() is what you're looking for.
By the way, if you're implementing a modeless dialog and are looking for where to "delete this;", PostNcDestroy() is the place.
This answer describes how I eventually solved my problem. I'll note that while the answer by John Dibling was helpful, this was not the final solution to my problem. This is because the WM_NC_DESTROY message is sent as the final message to the window, but this can be handled before the last message to the window has finished being handled. See for example http://support.microsoft.com/?kbid=202110 for an explanation of the problem.
DialogProc() is called with WM_CLOSE.
ProcessWindowMessage() calls your WM_CLOSE handler.
In your WM_CLOSE handler, you call DestroyWindow().
This ends up calling DialogProc again with WM_NCDESTROY.
ProcessWindowMessage() calls your WM_NCDESTROY handler.
You call "delete this" in your WM_NCDESTROY handler.
After having called delete this, the object is no longer valid, but you are still technically in the WM_CLOSE handler so you will probably crash when you eventually get back there. This means it is not really safe to assume that you can do delete this in PostNcDestroy, since the object may still be live in some other stack frame.
///
/// A window designed to allow any window to use the "OnFinalMessage" method from the ATL CWindow class
/// You must call SubclassWindow for this instance so that the window procedure runs
template<class T>
class FinalMessageWindow : public CWindowImpl<FinalMessageWindow<T> >
{
T *_t; /// The object wanting to receive the final message notification
public:
BEGIN_MSG_MAP(FinalMessageWindow<T>)
END_MSG_MAP()
///
/// The constructor
/// \param t The object that wants to get the OnFinalMessage notification
FinalMessageWindow(T *t)
: _t(t)
{
}
///
/// Called when the final window message for the window has been processed - this is often a good time to delete the object
/// \param hWnd The window handle
virtual void OnFinalMessage(HWND hWnd)
{
_t->OnFinalMessage(hWnd);
}
};
I created the above class, note that it is derived from the ATL CWindow class - this allows me to use the OnFinalMessage handler for this class. The OnFinalMessage handler is different from the PostNcDestroy in MFC windows in that it is guaranteed to be called only after the final message handler on the stack has completed.
We then use window subclassing to insert this window as the window procedure for my own window:
// roughly speaking
FinalMessageWindow<MyWindow> _finalMessageWindow(this);
finalMessageWindow.SubclassWindow(m_hWnd);
Then we implement the OnFinalMessage handler for our window:
void MyWindow::OnFinalMessage(HWND hWnd)
{
delete this;
}