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.
Related
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;
}
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();
}
I'm writing a WTL Aero wizard and I'd like to gray out the window's Close button (its first step will require no user interaction and can not be canceled, so disabling the button is perfectly appropriate).
Putting the following code:
CMenuHandle pMenu = GetSystemMenu(FALSE);
pMenu.EnableMenuItem(SC_CLOSE, FALSE);
in OnInitDialog does not work, as the procedure is called before the window itself is displayed on the screen (the ATLASSERT(::IsMenu(m_hMenu)); assertion in EnableMenuItem is tripped at runtime).
Is there an elegant way to disable the Close button? (I'm a WTL beginner, and I'd like the solution to be as clean as possible).
This is a minimal version of the wizard's page code:
#include "stdafx.h"
class MainPage : public CAeroWizardPageImpl<MainPage> {
public:
BEGIN_MSG_MAP(MainPage)
MESSAGE_HANDLER_EX(WM_INITDIALOG, OnInitDialog)
CHAIN_MSG_MAP(__super)
END_MSG_MAP()
enum {
IDD = IDR_MAINFRAME
};
MainPage() : CAeroWizardPageImpl<MainPage>(IDR_MAINFRAME) {
/* Set the wizard's title */
m_headerTitle.LoadString(IDS_INSTALLHEADER);
SetHeaderTitle(m_headerTitle);
}
private:
CString m_headerTitle;
LRESULT OnInitDialog(UINT message, WPARAM wParam, LPARAM lParam) {
UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
/* Disable the wizard buttons and center the window */
ShowWizardButtons(0, 0);
EnableWizardButtons(PSWIZB_BACK, 0);
CenterWindow();
return TRUE;
}
};
The close [X] button is a part of Common Controls wizard property sheet class. You are not supposed to alter its presentation and behavior. What you can do is to handle PSN_QUERYCANCEL notification and prevent wizard from closing. With WTL it is easy, however you need to know that that there are two versions of notifications handlers available.
If _WTL_NEW_PAGE_NOTIFY_HANDLERS is defined, typically in stdafx.h, then you do it like this:
class MainPage :
public CAeroWizardPageImpl<MainPage>
{
// ...
INT OnQueryCancel()
{
return 1; // Zero to Allow Wizard Close
}
};
Otherwise, older syntax is in use:
class MainPage :
public CAeroWizardPageImpl<MainPage>
{
// ...
BOOL OnQueryCancel()
{
return FALSE; // Allow Wizard Close?
}
};
Along with preventing cancellation/close you are free to indicate this by showing a message box suggesting use to wait until the pending operation is completed, or otherwise display a notification (e.g. flash a static control etc.)
I am using a MFC link control in my dialog based application,and I add an event handler of BN_CLICKED for it, hoping that it could do something for me, however, when i click on it, it just does nothing at all(when i don't set the url of it, when i set the url, it will jump to that url), my event handler is not triggered. So, how to disable its default "jump" behaviour and trigger my handler?
Subclassing CMFCLinkCtrl and adding an ON_WM_LBUTTONDOWN event handler seems to work.
You can then choose whether or not to call CMFCLinkCtrl::OnLButtonDown.
class CMyLinkCtrl : public CMFCLinkCtrl {
DECLARE_DYNAMIC(CMyLinkCtrl)
public:
CMyLinkCtrl();
virtual ~CMyLinkCtrl();
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
protected:
DECLARE_MESSAGE_MAP()
};
IMPLEMENT_DYNAMIC(CMyLinkCtrl, CMFCLinkCtrl)
CMyLinkCtrl::CMyLinkCtrl() {
}
CMyLinkCtrl::~CMyLinkCtrl() {
}
BEGIN_MESSAGE_MAP(CMyLinkCtrl, CMFCLinkCtrl)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
void CMyLinkCtrl::OnLButtonDown(UINT nFlags, CPoint point) {
static bool bDisabled = false;
if(bDisabled) {
MessageBox(_T("Link is disabled"));
} else {
CMFCLinkCtrl::OnLButtonDown(nFlags, point);
}
bDisabled = !bDisabled;
}
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).