How to make child control handle accelerator command of parent CView - c++

I have a CFormView, and a child CListCtrl control. I can handle
accelerator events, like Ctrl+C, Ctrl+V ... in CFormView without
problem, by defining below message handler:
ON_COMMAND(ID_EDIT_COPY, &CMyFormView::OnEditCopy)
Now I want my CListCtrl handle these commands differently. I want to
implement OnEditCopy in CListCtrl class, rather than implement logic
in the view class. How can I pass the accelerator events from CView to
child control, when CListCtrl is on focus? I tried like:
ON_CONTROL_REFLECT(ID_EDIT_COPY, &CMyListCtrl::OnEditCopy)
But it doesn't work.

Alternative you can override PreTranslateMessage() on CMyListCtrl and call TranslateAccelerator()
BOOL CMyListCtrl::PreTranslateMessage(MSG* pMsg)
{
if (m_hAccelTable)
{
if (::TranslateAccelerator(m_hWnd, m_hAccelTable, pMsg))
return(TRUE);
}
return CListCtrl::PreTranslateMessage(pMsg);
}
It requires acccess to the global accelerator-resource on the mainframe, or that you load the accelerator again. Then your CMyListCtrl will receive the WM_COMMAND messages specified in the accelerator table.
http://support.microsoft.com/kb/222829

Use same ON_COMMAND macro in CMyListCtrl.
ON_COMMAND(ID_EDIT_COPY, &CMyListCtrl::OnEditCopy)
All you have to do is overriding OnCmdMsg method.
BOOL CMyFormView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
if (GetFocus() == m_myListCtrl
&& m_myListCtrl->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return CMyFormView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
(m_myListCtrl is the CMyListCtrl instance pointer.)
This make all WM_COMMAND message first handled in m_myListCtrl if its the focus window.

Related

Wrong parent HWND in modal dialog

Why do i get desktop as a parent HWND for my modal dialog here?
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
HWND parent = GetParent(); // or GetAncestor(m_hWnd, GA_PARENT);
assert(parent != GetDesktopWindow()); // not ok
...
}
....
}
//somewhere in code
//m_hWnd is some valid HWND
assert(m_hWnd != GetDesktopWindow()); //ok
CSaveProfileAsDlg dlg;
dlg.DoModal(m_hWnd /*as a parent wnd*/);
I can "solve" it by passing corret HWND in CSaveProfileAsDlg ctor, but i'd like to have correct solution.
Thank you!
The documentation is very confusing but I think I found the problem. DoModal internally calls ::DialogBox(), one parameter of which takes a HWND named hWndParent. From the documentation:
hWndParent [in, optional]
Type: HWND
A handle to the window that owns the dialog box.
The keyword here is the word "owns". The section about owned windows confirms this:
Dialog boxes and message boxes are owned windows by default. An application specifies the owner window when calling a function that creates a dialog box or message box.
So we actually talk about the owner window instead of its parent. This makes sense as the dialog is a free floating window and not part of a window hierarchy as "parenthood" would imply.
You can get the owning window by using:
HWND parent = ::GetWindow(m_hWnd, GW_OWNER);
I had a similiar issue. I was wondering why GetParent() return always a different CWnd*.
The right solution is simple, just pass the desired pWnd to the dlg Constructor.
It will be stored in the CDialog member variable m_pParentWnd.
Then you can always get the passed pWnd with this member variable in your Dialog.
//somewhere in code
//pWnd some valid CWnd pointer
CSaveProfileAsDlg dlg (pWnd); // relevant!
dlg.DoModal();
.
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
CWnd* _pWnd = GetParent(); // not ok, returns probably CMainFrame or similiar
CWnd* pWnd = m_pParentWnd; // your parent Wnd
...
}
....
}
I've got two versions of the same application and one of them disables all parent popup windows as it should when DoModal is called. Second version disables only top-level CMainFrame and real parent is stay enabled so I can call modal dialog twice and more.
This happen in CWnd::GetSafeOwner_, line :
hWnd = ::GetLastActivePopup(hWnd);
Firsts version return real parent while second version return CMainFrame.
I've spent one day and could not find the cause of such behaviour. However I've found workaround:
When DoModal is called it Disable CMainFrame so I can disable it's children also:
afx_msg void OnEnable(BOOL bEnable);
ON_WM_ENABLE()
void CMainFrame::OnEnable(BOOL bEnable)
{
std::for_each(m_bars.begin(), m_bars.end(),
[=](const std::pair<EBar, BaseBar*>& bar)
{
bar.second->EnableWindow(bEnable);
});
}

MFC combobox killfocus doesn't work

I have a dialog box with several comboboxes in it as a member variables. The control wizard lets me create handlers for the comboboxes for the CBN_KILLFOCUS message. For example, one such handler is automatically called
void MyDlg::OnKillfocusMyCombo()
My expectation is that this handler would be called just as soon as I tab out of it. But it doesn't get called.
I ran into the same issue. This is a bug in MFC. (It's over 4 years later and it is still there.) Somehow ON_CBN_KILLFOCUS handler is never called although the Win32 CBN_KILLFOCUS notification itself is broadcast.
To fix this override the WindowProc for the dialog manually (Win32-way):
LRESULT CMyDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
//Fix for the bug in MFC
if(message == WM_COMMAND)
{
if(HIWORD(wParam) == CBN_KILLFOCUS &&
LOWORD(wParam) == Your_ComboBox_ID)
{
OnCbnKillfocusComboBox();
}
}
return CDialog::WindowProc(message, wParam, lParam);
}

How to capture MouseMove event in a MFC Dialog Based application for a checkbox?

My application is a VC6 MFC dialog based application with multiple property pages.
I have to capture a mousemove event over a control, for example Checkbox.
How can I capture the mousemove events over a checkbox in MFC?
A checkbox is a button control (eg. CWnd). Derive your own class from CCheckBox and handle the OnMouseMove event.
Per request...assuming a class derived from CButton...
BEGIN_MESSAGE_MAP(CMyCheckBox, CButton)
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
void CMyCheckBox::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CButton::OnMouseMove(nFlags, point);
}
Thanks for your replies.. I found a way to get the mousemove event for my app.
WM_SETCURSOR windows message gets the mouse move. It returns the Cwnd pointer for a control and the dialog.
Find my code below.
BOOL CMyDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
CWnd* pWndtooltip = GetDlgItem(IDC_STATIC_TOOLTIP);
if (pWnd != this)
{
if (IDC_SN_START_ON == pWnd->GetDlgCtrlID())
pWndtooltip->ShowWindow(SW_SHOW);
}
else
pWndtooltip->ShowWindow(SW_HIDE);
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
return true;
}
I found in #raj's OnSetCursor() code, that the associated Member variable for IDC_STATIC_TOOLTIP is that variable to which you assign the desired tool tip text. For example, if the associated variable is m_strToolTip, assign the desired text to display during the hovering event as follows:
m_strToolTip.Format("%s", "Tool tip text goes here");
I also found that UpdateData() was required upon entry into the event handler and UpdateData(FALSE) was required before the return. The SetCursor() call seems to have no effect when commented.
You can also override CDialog::PreTranslateMessage:
BOOL CSomeDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_MOUSEMOVE && pMsg->hwnd == m_checkBox->m_hWnd)
{
...
}
return CDialog::PreTranslateMessage(pMsg);
}

How to handle events for dynamically created objects in MFC?

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).

Intercepting messages from a child of a child with MFC

I have a CListCtrl class and at the moment when a user selects one of the sub items I am displaying a CComboBox over the subitem which the user can then make a selection from.
However I have a problem. When the user has made a selection i need the combo box to disappear (ie intercept CBN_SELCHANGE). The problem is that I need to make the CComboBox a child of the CListCtrl (Otherwise I get weird problems with the list drawing over the combo box even if i set the combo box to be topmost). So the CBN_SELCHANGE message gets sent to the list view which, understandably, ignores it. How can I get the list view to pass that message up to the parent window.
Do I really need to derive my own CListCtrl class that simply intercepts the CBN_SELCHANGE message and passes it up to the parent window? Is there a better way to do this than creating an OnWndMsg handler?
Thanks for any help!
Edit: This code works
class CPassThroughListCtrl : public CListCtrl
{
protected:
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
if ( message == WM_COMMAND )
{
GetParent()->SendMessage( message, wParam, lParam );
}
return CListCtrl::OnWndMsg( message, wParam, lParam, pResult );
}
public:
CPassThroughListCtrl()
{
};
};
But i'd really like to know if there is a nicer way to do this.
You can subclass CComboBox such that it will handle CBN_CLOSEUP message.
Your custom Combo will know about the manager i.e. the object that created it in the first place and will have to destroy it upon close up (top level window or whatever, should be provided as an argument to your custom combobox constructor)...
So when you create combobox on a top of the list item you will create instance of this customized combobox instead of the MFC default one.
Combobox event handler could look like that:
BEGIN_MESSAGE_MAP(CNotifyingComboBox, CComboBox)
ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseUp)
END_MESSAGE_MAP()
void CNotifyingComboBox::OnCloseUp()
{
// _manager is pointer to the object that created this combobox,
// and is responsible for its destruction,
// should be passed into CNotifyingComboBox cosntructor
if( NULL != _manager )
{
_manager->OnCloseUpComboBox(this);
}
}