WM_KILLFOCUS for a modal dialog does not work - mfc

In my application I have a standard MFC modal dialog. I'd like close that dialog when the user clicks outside the dialog window. For that purpose I put ON_MESSAGE(WM_KILLFOCUS, OnKillFocus) in the dialog's message map hy hand (the class wizard does not offer that option):
BEGIN_MESSAGE_MAP(CTestTreeCtrlDlg, CDialog)
//{{AFX_MSG_MAP(CTestTreeCtrlDlg)
ON_NOTIFY(TVN_SELCHANGED, IDC_TREE1, OnSelchangedTree)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_KILLFOCUS, OnKillFocus)
END_MESSAGE_MAP()
...
void CTestTreeCtrlDlg::OnKillFocus()
{
...
}
Now if I click outside the dialog, the latter of course looses focus, but the OnKillFocus method won't get called for some reason.

Thank you patriiice !
WM_ACTIVATE does the job:
BEGIN_MESSAGE_MAP(CTestTreeCtrlDlg, CDialog)
//{{AFX_MSG_MAP(CTestTreeCtrlDlg)
ON_NOTIFY(TVN_SELCHANGED, IDC_TREE1, OnSelchangedTree)
//}}AFX_MSG_MAP
ON_WM_ACTIVATE()
END_MESSAGE_MAP()
...
void CTestTreeCtrlDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
CDialog::OnActivate(nState, pWndOther, bMinimized);
if (nState == WA_INACTIVE)
OnOK() ;
}

Quick search -> http://www.itlisting.org/5-windows/964b01901673b4b0.aspx
I am pretty sure it is a better approach to do this.

Related

Distinguish modeless vs modal dialog

I want use the same class CTestDialog for a modal dialog
CTestDialog dlg;
dlg.DoModal();
and for a modeless Dialog
m_pDlg = new CTestDialog;
m_pDlg->Create(CTestDialog::IDD,this);
m_pDlg->ShowWindow(SW_SHOW);
The problem I have is at PostNcDestroy(..) it crashes if it is constructed as modal Dialog:
void CTestDialog::PostNcDestroy()
{
CDialog::PostNcDestroy();
delete this; // <= need for modeless, but Crash! if constructed as modal Dialog
}
How can I determine, in a straightforward way, if the class was constructed as modeless or modal dialog?
Check the dialog's m_nModalResult. If it is -1 - the dialog was modeless; otherwise it will be one of IDOK, IDCANCEL, etc. codes.
[Edited to answer the comment]
This is different from the original question.
In the OK/Cancel handler, you can test:
if (m_nFlags & WF_MODALLOOP)
I have abandoned searching a solution if the MFC Dialog itself can distingish between modeless vs modal dialog.
This workaround works for me.
I have extended the constructor to tell if it is modeless or modal.
CTestDialog::CTestDialog(CWnd* pParent /*=NULL*/, BOOL bModeless /*=false*/)
: CDialogEx(CTestDialog::IDD, pParent)
, m_bModeless(bModeless)
{
}
void CTestDialog::PostNcDestroy()
{
CDialogEx::PostNcDestroy();
if (m_bModeless)
delete this;
}
void CTestDialog::OnOK()
{
if (UpdateData(TRUE))
{
if (m_bModeless)
DestroyWindow();
else
CDialogEx::OnOK();
}
void CTestDialog::OnCancel()
{
if (m_bModeless)
DestroyWindow();
else
CDialogEx::OnOK();
}

MFC click and move/drag dialog window

I am currently working on finishing some code handed off to me. It was written in MFC in Visual Studio 2005 years ago, was put on hold, and now is brought to me.
While I know C++, I have spent the last ~2 months studying the code and learning MFC and it's starting to come together.
The GUI for the code is an SWF flash file embedded in an invisible dialog window. I do not have the source code for the SWF file so will probably, in the future, redo it in WPF or something. I have the WMMode set to "Window" because in Transparent/Opaque mode it doesn't display properly, where it flashes/blinks everytime a mouse event is captured.
Anyhow, in Win XP/Vista, clicking and dragging the flash control works. In windows 7/8.1, it won't move.
Happy to provide any and all info needed. I'm still a little overwhelmed by MFC dialogs so unsure what you'd all like to see.
I found this question: Moving window by click-drag on a control
Which looks like it solves a lot of the issue. However, I don't want the whole control to be clickable like this, only the top part. Unfortunately, in the MS Resource view, the ActiveX control is blank as the SWF is only loaded at runtime; I've tried to find resources for this kind of thing but it's very difficult as I am unsure of the technical terms to use.
EDIT
I have attempted this by creating a very simple MFC app that has a Static Text control and nothing else. I am trying to get it to work by clicking on the static text (though I may be painting myself into a corner as it does not have a built-in lButtonDown event).
Here is the relevant code:
class MyDialog : public CDialog
{
public:
MyDialog(CWnd* pParent = NULL) : CDialog(MyDialog::IDD, pParent)
{ }
// Dialog Data, name of dialog form
enum{ IDD = INTERFACE1 };
protected:
virtual void DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); }
//Called right after constructor. Initialize things here.
virtual BOOL OnInitDialog()
{
CDialog::OnInitDialog();
pText = (CStatic *)GetDlgItem(ID_TEXT);
pText->SetWindowTextW(_T("Hello World!"));
return true;
}
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
private:
CStatic * pText;
public:
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
Overridden Method:
afx_msg void MyDialog::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnNcLButtonDown(HTCAPTION, point);
}
I have also tried setting nFlags to 0x2, calling OnLButtonDown (as opposed to onNcLButtonDown), various other things. The message fires but the window does not move (it does move from the title bar, as normal). What am I missing?
Actually lets try this code instead with ON_WM_NCHITTEST(). This will drag the dialog if you click the mouse anywhere in client area (client area acts as caption). There is a line rc.bottom = rc.top + 100 if you uncomment it then it will only drag if you click the top section (I picked the number 100 at random).
//declare:
afx_msg LRESULT OnNcHitTest(CPoint point);
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_WM_NCHITTEST()
END_MESSAGE_MAP()
LRESULT MyDialog::OnNcHitTest(CPoint point)
{
ScreenToClient(&point);
CRect rc;
GetClientRect(&rc);
//rc.bottom = rc.top + 100;
if (rc.PtInRect(point))
return HTCAPTION;
return CDialog::OnNcHitTest(point);
}
Second option:
If we want to move the dialog by clicking on a child control, and if that control captures the mouse, then try this method instead. ***Note, test to make sure the control works properly after it is moved.
BOOL MyDialog::PreTranslateMessage(MSG *msg)
{
if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON))
{
CPoint p;
GetCursorPos(&p);
CRect r;
ActiveX->GetWindowRect(&r);
if (r.PtInRect(p))
{
ReleaseCapture();
SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, 0);
SendMessage(WM_NCLBUTTONUP, HTCAPTION, 0);
return 1;
}
}
return CDialogEx::PreTranslateMessage(msg);
}

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);
}

Child Dialog - SetWindowTextA or SendMessageA crashes program - MFC

ERROR: afxwin2.inl line 165
My app is a dialog box with a few edit boxes. Once I click the button to evaluate the information entered I want to open a child dialog to display the results. I tried overloading DoModal() like this:
//in the child dialog
//.h
CResultsDlg::CResultsDlg(CParentDlg *parent);
virtual INT_PTR DoModal(float bmi);
//.cpp
CResultsDlg::CResultsDlg(CParentDlg *parent) : CDialogEx(CResultsDlg::IDD), _parent(parent)
{ //initializations }
INT_PTR CResultsDlg::DoModal(float bmi)
{
m_sBMI.Format("%f", bmi);
m_hBMI.SetWindowTextA(m_sBMI); //crashes !!!!!!!!!!
m_hBMI.SendMessageA(WM_SETTEXT, 0, (LPARAM)"15.11"); //crashes !!!!!!!!
// OnInitDialog(); //because this wasn't getting called at all
return CDialogEx::DoModal();
}
BOOL CResultsDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// __super::OnInitDialog(); //no difference...
m_hBMI.SetWindowTextA("10.3"); //crashes !!!
return true; // return TRUE unless you set the focus to a control
}
//in the parent dialog
//.cpp
void CParentDlg::OnBnClickedCalculate()
{
CResultsDlg childResultsDlg = this;
childResultsDlg.DoModal(15.7);
}
m_hBMI is a handle to a static text control. I tested an edit box but it still crashed.
I understand that it probably has something to do with the controls not being created yet but I tried every way I know.
Using breakpoints, I confirmed that OnInitDialog does not get called at all unless I put it in the overloaded DoModal function. SetWindowText/SendMessage still crashes in OnInitDialog with the same ASSERT error.
If I remove all SetWindowText/SendMessage then the child window does come up modal like it should but the 'result' static text control is the same as the text I set it to in the dialog editor properties pane.
Thanks !!!!!
*MORE DETAILS*-----------
void CResultsDlg::DoDataExchange(CDataExchange* pDX) // DDX/DDV support
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_BMI, m_fBMI);
DDV_MinMaxFloat(pDX, m_fBMI, 0, 100);
DDX_Control(pDX, IDC_BMI, m_hBMI);
}
The usual sequence when you start a dialog is:
You call CDialog::DoModal.
The dialog window gets created.
The child controls of the dialog get created.
OnInitDialog gets called.
CDialog::OnInitDialog calls DoDataExchange.
You have a DDX_Control call in your DoDataExchange method to map the child control to a member variable.
Notice that the member variables only get initialized at the end of that sequence. You're trying to use them way before, so you get a crash.
Store the value you need for initialization in a member variable and take care of it in DoDataExchange.

MFC TVN_ITEMEXPANDING doesn't call the handler function

I have a CTreeCtrl and I like to use its TVN_ITEMEXPANDING message, but the handler function never calls.
CsetkliensDlg.h
afx_msg void OnItemExpanding(NMHDR* pNmhdr,LRESULT *lResult);
CsetkliensDlg.cpp
BEGIN_MESSAGE_MAP(CCsetkliensDlg, CDialogEx)
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, &CCsetkliensDlg::OnItemExpanding)
END_MESSAGE_MAP()
...
void CCsetkliensDlg::OnItemExpanding(NMHDR* pNmhdr,LRESULT *lResult)
{
AfxMessageBox("almafa");
}
The items have childs.
You are trying to catch a notification in the parent dialog so you should use ON_NOTIFY instead of ON_NOTIFY_REFLECT.
Of course then your message map will be something lik:
ON_NOTIFY( TVN_ITEMEXPANDING, CTREECTRL_RESOURCES_ID, ONHandlerFunction)
You can use the reflection mechanism but then the handler should be in a CTreeCtrl derived class.