How to handle events for dynamically created objects in MFC? - c++

I am wondering how I can handle an event for a dynamically created variable, e.g. a list control.
CListCtrl* pList = new CListCtrl();<br/>
pList->Create(...);
How can I handle the event LVN_ITEMCHANGED for pList?
OnLvnItemchangedList(NMHDR *pNMHDR, LRESULT *pResult)
{
//do stuff
}
Do I have to create an extended CListCtrl or is there some other way? I would prefer not creating a extended class.

LVN_ITEMCHANGED is sent through WM_NOTIFY message from control to its parent so you just need to add LVN_ITEMCHANGE handler function in parent's class (e.g. CMyDlg):
In header file:
class CMyDlg : public CDialog
{
...
protected:
afx_msg void OnLvnItemChanged(NMHDR *pNMHDR, LRESULT *pResult);
...
}
In source file:
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
...
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST, &CMyDlg::OnLvnItemChanged)
...
END_MESSAGE_MAP()
...
void CMyDlg::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
*pResult = 0;
... // examine *pNMLV members for item's information
}
It does not matter how CListCtrl control is created (through resource editor or dynamically), approach is the same. Just make sure you are using correct control ID in ON_NOTIFY message map entry. (ID passed to Create/CreateEx or defined in Properties in resource editor).

Related

BCN_HOTITEMCHANGE notification is not getting called with CMFCButton class

BCN_HOTITEMCHANGE notification code is not getting called with CMFCButton class.
I'm using Visual Studio 2010
Steps performed:
1) Add a CButton to my dialog from the resource editor.
2) Use my own derived class CMFCButtonEx derived from CMFCButton class (which in turn is derived from CButton class).
3) Declare CMFCButtonEx 'm_btn_ex' instance variable to my dialog.
4) Add DDX_Control(pDX, IDC_BUTTON_EX, m_btn_ex);
5) When I click this button all my message handlers in the derived class are called except BCN_HOTITEMCHANGE notification associated with OnButtonItemChange function. The breakpoint in that function never hits. All the other message handlers (OnMouseMove, OnMouseLeave, and OnButtonClicked) are called correctly, and the breakpoints in them get hit.
As a note if I derive by CMFCButtonEx class from CButton class, then I can hit the breakpoint in OnButtonItemChange function i.e. BCN_HOTITEMCHANGE notification code is handled correctly
For some reason my BCN_HOTITEMCHANGE notification code is not being handled correctly by CMFCButton class. I can't understand what am I doing wrong here. I have also tried to use TBN_HOTITEMCHANGE notification code instead of BCN_HOTITEMCHANGE but the breakpoint in the OnButtonItemChange function never hits.
Thanks!
CMFCButtonEx class code:
IMPLEMENT_DYNAMIC(CMFCButtonEx, CMFCButton)
CMFCButtonEx::CMFCButtonEx()
{
m_bMouseTracking = FALSE
}
CMFCButtonEx::~CMFCButtonEx()
{
}
BEGIN_MESSAGE_MAP(CMFCButtonEx, CMFCButton)
//{{AFX_MSG_MAP(CMFCButtonEx)
ON_WM_MOUSEMOVE()
ON_WM_MOUSELEAVE()
ON_NOTIFY_REFLECT_EX(BCN_HOTITEMCHANGE, OnButtonItemChange)
//ON_NOTIFY_REFLECT_EX(TBN_HOTITEMCHANGE, OnButtonItemChange)
ON_CONTROL_REFLECT_EX(BN_CLICKED, OnButtonClicked)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BOOL CMFCButtonEx::OnButtonClicked()
{
AfxMessageBox("MFC Button Clicked");
return FALSE;
}
BOOL CMFCButtonEx::OnButtonItemChange(NMHDR* pNMHDR, LRESULT* pResult)
{
NMBCHOTITEM* pnmbchotitem = (NMBCHOTITEM*)pNMHDR;
AfxMessageBox("MFC Button Item Change");
*pResult = 0;
return FALSE;
}
void CMFCButtonEx::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bMouseTracking)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = this->m_hWnd;
if (::_TrackMouseEvent(&tme))
{
m_bMouseTracking = TRUE;
AfxMessageBox("MFC Button Mouse Move");
}
}
CMFCButton::OnMouseMove(nFlags, point);
}
void CMFCButtonEx::OnMouseLeave()
{
m_bMouseTracking = FALSE;
AfxMessageBox("MFC Button Mouse Move");
CMFCButton::OnMouseLeave();
}
BCN_HOTITEMCHANGE is not triggered for CMFCButton, or any classes derived from CMFCButton
As stated in documentation, Visual Style must be enabled otherwise BCN_HOTITEMCHANGE is not triggered. CMFCButton relies on owner draw method, so visual style is disabled for this button.
You could remove BS_OWNERDRAW from your CMFCButtonEx class. But this is not recommended because CMFCButtonEx relies on BS_OWNERDRAW.
A better solution is derive your class from CButton (and make sure Visual Styles is enabled), use custom draw instead of owner draw. You may not need OnMouseMove and OnMouseLeave.
class CMyButton : public CButton {}
...
BOOL CMyButton::OnButtonItemChange(NMHDR* pNMHDR, LRESULT* pResult)
{
auto ptr = (NMBCHOTITEM*)pNMHDR;
bool enter = ptr->dwFlags & HICF_ENTERING;
if (enter)
TRACE(L"CMyButton... enter\n");
else
TRACE(L"CMyButton... exit\n");
*pResult = 0;
return FALSE;
}

How to catch a new item selection in a class that derives CComboBox

I have a class that derives CComboBox and I want it to handle internally when the user selects another item. I know that I can catch the selection in the parent control with ON_CBN_SELCHANGE, but I want to handle the selection change within the combobox itself, so that I can use the private implementation details of my own derived combobox class to interpret the new selection.
Is this possible and how I can I do this?
In header:
afx_msg LRESULT OnSelchange();
in cpp file:
BEGIN_MESSAGE_MAP(CComboBoxExt, CComboBox)
ON_CONTROL_REFLECT_EX(CBN_SELCHANGE, OnSelchange)
END_MESSAGE_MAP()
LRESULT CComboBoxExt::OnSelchange()
{
// TODO: Add your control notification handler code here
// do your job ...
return Default();
}

Unable to receive / capture Windows Messages

I'm fairly new to MFC and the Message handling context.
I've a DLL Consumer application, which has a CFrameWndEx derived class,CMainFrame. Now this calls a DLL, which puts a CDialog etc. into this MainFrame window.
I wish to receive certain messages into my application.
So what I have done is, declared the expected messages in the Message Map, of the DLL Consumer application, and defined the appropriate message handlers.
Now, even though I can see that the application is being sent those registered messages, I'm not able to receive / handle them in the Consumer Window, i.e., nothing happens when those messages are broadcast.
Mainfrm.h
class CMainFrame : public CFrameWndEx
{
public:
CMainFrame();
protected:
DECLARE_DYNAMIC(CMainFrame)
public:
void OnFileDialogdisplay(void);
afx_msg LRESULT OnLogonChanged(WPARAM,LPARAM);
afx_msg LRESULT OnLanguageChanged(WPARAM,LPARAM);
afx_msg LRESULT OnShutdownRequested(WPARAM,LPARAM);
afx_msg LRESULT OnReconnectServer(WPARAM,LPARAM);
afx_msg LRESULT OnChangeRole(WPARAM,LPARAM);
}
Mainfrm.cpp
<some header files>
static UINT UWM_LOGON_CHANGED = ::RegisterWindowMessage(UWM_LOGON_CHANGE);
static UINT UWM_LANGUAGE_CHANGED = ::RegisterWindowMessage(UWM_LANGUAGE_CHANGE);
static UINT UWM_RECONNECT = ::RegisterWindowMessage(UWM_RECONNECT_SERVER);
static UINT UWM_SHUTDOWN_REQUESTED = ::RegisterWindowMessage(UWM_REQUEST_SHUTDOWN);
static UINT UWM_ROLE = ::RegisterWindowMessage(UWM_ROLE_CHANGE);
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
ON_WM_CREATE()
ON_WM_SETFOCUS()
ON_COMMAND(ID_VIEW_CUSTOMIZE, &CMainFrame::OnViewCustomize)
ON_REGISTERED_MESSAGE(AFX_WM_CREATETOOLBAR, &CMainFrame::OnToolbarCreateNew)
ON_COMMAND(ID_FILE_DIALOGDISPLAY, &CMainFrame::OnFileDialogdisplay)
ON_REGISTERED_MESSAGE(UWM_LOGON_CHANGED, OnLogonChanged)
ON_REGISTERED_MESSAGE(UWM_LANGUAGE_CHANGED, OnLanguageChanged)
ON_REGISTERED_MESSAGE(UWM_SHUTDOWN_REQUESTED, OnShutdownRequested)
ON_REGISTERED_MESSAGE(UWM_RECONNECT, OnReconnectServer)
ON_REGISTERED_MESSAGE(UWM_ROLE, OnChangeRole)
//ON_WM_NCCALCSIZE()
END_MESSAGE_MAP()
//code to register to Main server application to be able to receive messages
void manageregistration(CMainFrame* pFrame, bool flag)
{
if (flag)
{ // registration
HWND MyHandle = (HWND)pFrame->GetActiveWindow();
RegisterApmsClientPgm(_T("AAUserInterface"), MyHandle);
}
}
//Handlers
LRESULT CMainFrame::OnShutdownRequested(WPARAM,LPARAM lp)
{
AfxMessageBox(_T("Error"),MB_ICONERROR | MB_OK);
testProgram();
return 0;
}
LRESULT CMainFrame::OnChangeRole(WPARAM,LPARAM lp)
{
AfxMessageBox(_T("Error"),MB_ICONERROR | MB_OK);
testProgram();
return 0;
}
// etc etc.etc.
So, after all this, I can see that the Consumer application, is registered to receive these messages from another application which broadcasts them.
However, upon creating the condition where the messages are being broadcast, and they are as I can verify from other applications which receive them, no such message is being caught in my application.
I am not sure where the problem could be. The window is always on top, albeit with another CDialog derived DLL inside it.
Try using pFrame->m_hWnd. You can't assume that the Mainframe window is always active.

How to disable the CListCtrl select option

I don't know how to disable the CListCtrl select option. I want to override the CListCtrl class method or handle any window command ? Thanks.
If you want to stop the user selecting an item in a CListCtrl, you need to derive your own class from CListCtrl and add a message handler for the LVN_ITEMCHANGING notification.
So, an example class CMyListCtrl would have a header file:
MyListCtrl.h
#pragma once
class CMyListCtrl : public CListCtrl
{
DECLARE_DYNAMIC(CMyListCtrl)
protected:
DECLARE_MESSAGE_MAP()
public:
// LVN_ITEMCHANGING notification handler
afx_msg void OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult);
};
And then MyListCtrl.cpp:
#include "MyListCtrl.h"
IMPLEMENT_DYNAMIC(CMyListCtrl, CListCtrl)
BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
ON_NOTIFY_REFLECT(LVN_ITEMCHANGING, &CMyListCtrl::OnLvnItemchanging)
END_MESSAGE_MAP()
void CMyListCtrl::OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
{
// LVN_ITEMCHANGING notification handler
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// is the user selecting an item?
if ((pNMLV->uChanged & LVIF_STATE) && (pNMLV->uNewState & LVNI_SELECTED))
{
// yes - never allow a selected item
*pResult = 1;
}
else
{
// no - allow any other change
*pResult = 0;
}
}
So you can, for example, add a normal CListCtrl to a dialog, then create a member variable for it (by default it will be CListCtrl) then edit your dialog's header file to #include "MyListCtrl.h and change the list control member variable from CListCtrl to CMyListCtrl.

Can't get the handle of a Cwnd Class in MFC Windowless Activex?

I have asked two questions earlier about this and for each post there was some solutions i tried them, but the problem still exist.
My first question was : why a windowless Activex does not return the Handle. the suggestion was "change the creation setting an make windowless activate off, i have tried it but still m_hWnd property has returned zero as GetSafeHwnd() method has did.
the second one was the same question this one focused on COleControl class and it's ancestor CWnd. the solution was as this "Create invisible window somewhere in your control initialization code. Handle the messages sent to this window, and call controls methods directly". so i did that but the created class still returns zero handle.
here is my new invisible class source:
// moWind.cpp : implementation file
//
#include "stdafx.h"
#include "PINActive.h"
#include "moWind.h"
#include "include\xfspin.h"
#include <math.h>
// moWind
IMPLEMENT_DYNAMIC(moWind, CWnd)
moWind::moWind(){}
moWind::~moWind(){}
//=============================================================
LRESULT moWind::OnExecuteEvent (WPARAM wParam, LPARAM lParam)
{
WFSRESULT *pResult = (WFSRESULT *)lParam;
CString EK=_T("");
CString str;
int reskey=0;
if (pResult->u.dwEventID=WFS_EXEE_PIN_KEY)
{
LPWFSPINKEY pressedkey;
pressedkey=(LPWFSPINKEY)pResult->lpBuffer;
reskey = log10((double)pressedkey->ulDigit) / log10((double)2);
EK.Format("%d",reskey);
xfsOnKeyEvent->OnKeyRecieved(reskey);
}
else
{
str.Format("ExecuteEvent: ID = %d\r\n", pResult->u.dwEventID);
}
MessageBox("a Execute message Recieved");
return 0;
}
BEGIN_MESSAGE_MAP(moWind, CWnd)
ON_MESSAGE(WFS_EXECUTE_EVENT,OnExecuteEvent)
END_MESSAGE_MAP()
and this is .h file of the class:
// moWind.h
class IXFSEvents
{
protected:
IXFSEvents(){};
virtual ~IXFSEvents(){};
public:
virtual void OnKeyRecieved(int key)=0;
};
class moWind : public CWnd
{
DECLARE_DYNAMIC(moWind)
public:
moWind();
virtual ~moWind();
void Register(IXFSEvents* obj)
{
xfsOnKeyEvent= obj;
}
protected:
IXFSEvents* xfsOnKeyEvent;
LRESULT OnExecuteEvent (WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
and at the end here this the way I've used this class in my Activex:
in the myActivex.h file:
include "moWind.h"
class CmyActivexCtrl : public COleControl, public IXFSEvents
{
...
Class definition
...
protected:
moWind tmpWind;
.
.
};
finally in the creation method of myActivex i have initialized the component callback method an wanted to get it's Handle as this:
CmyActivexCtrl::CmyActivexCtrl()
{
InitializeIIDs(&IID_DmyActivex, &IID_DmyActivexEvents);
tmpWind.Register(this);
myOtherComponent.WindowsHandle=tmpWind.GetSafeHwnd(); //here my Cwnd derived class returns zero
//my other component gets the handle and call an API with it to register
//the given handle and force the API to send the messages to that handle.
}
As you mentioned you need a window handle to be able to receive user messages through it, you always have an option of creating a helper window, such as message only window, see Using CreateWindowEx to Make a Message-Only Window.
For your windowless control it is okay to not have any window handle at all, so you cannot really rely on handle availability unless you own a window yourself.