We use in several place a CTreeCtrl (TreeView) and accept a double click to open dialogs related to the double clicked node.
The opened dialog looses the focus after being opened, since the tree view seems to force to be focussed at the end of the double click handling.
Our scenario:
the user double clicks onto a node
the tree view gets focused and selects an item in its tree
the tree view containing window receives the NM_DBLCLK notification for the tree view and reacts on the double click by opening a dialog or a MDI child window in our MDI environment
the opened dialog/MDI child window gets focused after being opened
the tree view gets focused again
Even if we use in (3) (the notification handler) the result field returning a non zero value to prevent the rest of the default handling, (5) happens and the tree view gets focused again, the item selected again.
I'd really appreciate any hint about a way to resolve this issue, since it is really annoying, that a just opened dialog or window looses its focus right after opening.
Thanks in advance!
This behavior won't occur if you create a modal dialog box, because parent window is immediately disabled and dialog gains focus. But with mode-less dialog, flicker may occur, and dialog loses focus.
For mode-less dialog, use PostMessage or SetTimer so that the mode-less dialog is opened after TreeView message is processed. Example:
#define WM_USER_MSG1 WM_USER + 1
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
ON_MESSAGE(WM_USER_MSG1, create_dialog)
...
END_MESSAGE_MAP()
void CMyWnd::OnDblClick(NMHDR*, LRESULT* pResult)
{
PostMessage(WM_USER_MSG1, 0, 0);
*pResult = 0;
}
LRESULT CMyWnd::create_dialog(WPARAM, LPARAM)
{
if(!m_dlg.GetSafeHwnd())
m_dlg.Create(IDD_DIALOG_X, this);
m_dlg.ShowWindow(SW_SHOW);
return 0;
}
Related
I'm a newcomer to MFC world. I need to have some dialog based operaion for an old application. There I'm having a trouble for an appraently obvious thing and that is I can't seem to find a way to close the child dialog (Modal) that I'm invoking from my parent (also a Modal). In all the ways I tried always both child and parent is getting closed at once.
Here is what I tried:
I've created a default MFC application in VS 2012 Professional
ParentDlg.cpp
void ParentDlg::OnBnClickedOk()
{
ChildDlg childDlg;
dialogOutput.DoModal();
CDialogEx::OnOK();
}
In my Child.cpp for a Close button (ID: IDCLOSE)
ChildDlg.cpp
void ChildDlg::OnBnClickedClose()
{
// TODO: Add your control notification handler code here
EndDialog(IDCLOSE);
}
But this is closing both the parent and child, but I need only the child dialog to be closed (Parent Dialog should remain open) as I'm clicking the 'Close' buton on the child dialog.
In short, I like to have the same behaviour of the default IDOK button of 'IDD_ABOUTBOX' dialog, which is also a Modal dialog and closes only the About Box when I click on the 'OK' button in it.
You should only place IDOK in the ID property of your close button. And only the child dialog will close. You should no longer create an event handler, and if you did, delete the event handler.
I want to handle the clicks on child windows the same as clicks on parent window - how can I do that in winapi?
Currently, I can move the parent window by pressing anywhere on it but not on the child windows, because then it won't move. How can I change that?
The answer that works:
case WM_NCHITTEST:
if (hWnd==parent)
return HTCAPTION;
else return HTTRANSPARENT;
Capture the mouse click on the desired child controls(s), and then send a WM_SYSCOMMAND message with SC_DRAGMOVE (0xF012, aka SC_MOVE OR'ed with 2) as the wParam parameter to the parent window. That will invoke a drag operation on the parent window.
This is a widely known trick, and well documented online (just not by Microsoft) if you do a search for SC_DRAGMOVE.
In my mfc dialog based application, there is a CListCtrl. I need to disable a button when the user clicks on an empty item in the list control.I used NM_CLICK message and achieved this. But if the user drags the mouse out of list control area and releases the mouse this doesn't work. I found the reason that NM_CLICK will be called only on receiving button up message.Is there any other solution for this.
But if the user drags the mouse out of list control area and releases the mouse this doesn't work.
That is entirely by design, and you shouldn't want those clicks to "count". This is the only way that a user has of changing her mind in the middle of a click. It works like this:
Start to click on an object (or the empty space) in the list box control
Change your mind
While still holding down the mouse button (i.e., before committing your click), drag the mouse pointer outside of the bounds of the control
Think: Whew! That was a close call!
You'll notice that, in Windows, an action never happens until the mouse button is released (often called "MouseUp"). If this wasn't supported, there would be no way for the user to bail out early of an action, which is a critical feature of any user interface.
There are some feasible options.
In parent dialog, MouseUp Handler function can use for this.
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
By using point, you can check whether mouse releasing is occured CListCtrl.
If it is, you just disable the button.
Also, WindowFromPoint can be another option.
static CWnd* PASCAL WindowFromPoint(POINT point);
In such as a OnMouseMove, if you use this function, you can check the window is pointed by mouse.
Implement a LVN_ITEMCHANGED handler, and in that handler disable the button as soon as the selected item count is zero.
I found the solution for the issue. There is a message which can be handled if the user drags the mouse using left button namely LVN_BEGINDRAG.By handling this message in the OnBeginDrag function I update the button status.So the button gets updated as soon as the user tries to drag the mouse.The code is as below:
In the header add
afx_msg void OnBeginDrag( NMHDR* pNMHDR, LRESULT *pResult );
In the implementation add message map and corresponding function:
BEGIN_MESSAGE_MAP( .. )
ON_NOTIFY( LVN_BEGINDRAG, IDC_LIST1, OnBeginDrag )
END_MESSAGE_MAP
OnBeginDrag( .... )
{
Updatebutton();
}
It solves the issue. thank you all for the support.
I have a CDialog window (CDrafter) which contains a CRichEditCtrl control.
I overrided CDrafter::PreTranslateMessage and CDrafter::OnNotify to allow me to click on special words in the RichTextEdit with the mouse, which in turn opens another dialog (MyDialog).
*NOTE: I did this as I didn't like the limitations of the EN_LINK styling.*
So within the CDrafter::PreTranslateMessage i have:
It just determines where and which word was clicked (nothing more) (waits for OnNotify to do something with it).
So within the CDrafter::OnNotify I have:
BOOL CSTANAGMessageDrafterDlg::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
{
BOOL r = CDialog::OnNotify(wParam, lParam, pResult);
//if (::PreTranslateMessage found a word clicked on ) {
MyDialog dialog;
dialog.DoModal();
//}
//Awesome my dialog opened and I can start editing the form (via the keyboard) no problems.
//BUG: There is a problem as the mouse curser is still showing the
// move carpet position icon as if it is still focused on the RichTextEdit control.
// If I click the mouse anywhere (in the MyDialog, or within the parent dialog)
// the mouse icon, and focus correctly changes to MyDialog, then I can click OK or CANCEL.
return r;
}
I have tried the calling "CDialog::OnNotify(wParam, lParam, pResult)" after the MyDialog::DoModal - still see same issue.
The MyDialog::DoModal is called within the same thread as the parent dialog.
I would expect to be able to do the following:
click on word, and MyDialog opens, click on MyDialog::Cancel button and the dialog closes.
But there is a problem as this is my sequence:
click on word, and MyDialog opens, click on MyDialog::Cancel button (it doesn't work - only the mouse icon changes), click on MyDialog::Cancel button and the dialog closes
I need to (initially click) the mouse in order to get any mouse control within the newly opened dialog. i.e. the mouseover event on buttons, etc. does nothing until I (click).
If I click anywhere on the dialog window, like in the "background", or on a Static Text, the function OnLButtonDown() is fired. But if I click a ListBox/EditBox/Combo/Calendar/Checkbox etc is not fired.
I thought that because these have control variables atached to it, and the Static Text don't. But adding a new Listbox to my Dialog and testing, I see that it doesn't fire either, so now I am confused...
I added OnLButtonDown() with the Class Wizard, it appears in:
BEGIN_MESSAGE_MAP(CMFCTesting2Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
// other handlers, etc
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
My function:
void CMFCTesting2Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
AfxMessageBox(CString("BUTTON DOWN"));
CDialogEx::OnLButtonDown(nFlags, point);
}
I tried calling CDialogEx:: ... before AfxMessageBox... but same result.
The CListBox item, has the Notify option set to True, as I seen some advices in other posts.
I suspect that the WM notification message is captured somehow by the ListBox, and not sent "forward" to my CMFCTesting2Dlg, but I understood that this should happen with the Notify option set to True, on the ListBox, no? (well, obviously, not...)
Sorry, I am new to dealing with WM in MFC.
BTW, I use Visual Studio 2010 Ultimate, and this is a Visual C++ - MFC- MFC Application - Dialog Based project.
How can I capture this mouse down event if clicked on a listbox / combo / etc?
On the LONG STORY, I am actually trying to accomplish this issue:
I have two listboxes (lets say), and I want to scroll them synchronously, when user scrolls one of them, others must scroll the same (or update in the next moment). And I thought of using on mouse down to track the position of every Listbox (with the GetTopIndex), and on mouse up, to GetTopIndex again and compare with the previous ones. If a change was made, then a listbox was scrolled and then update all listboxes with SetTopIndex. The unfriendly solution for the user, but simpler for me, would be clicking a button that does this verification / update, but its not elegant at all, and it can only set them at the same level, but can't determine which one was scrolled last time. This automatically scrolling of the listboxes should be made just for displaying, it is not important the selections in the listboxes. I tried using the Message Type in the Add Event Handler for the Listbox, but none that are displayed work for my problem, KillFocus and SetFocus, are not fired if the scroll-bar is dragged, only if an item in the listbox is clicked... and I didn't succeed on the others message type either, from the Add Event Handler.
Once a window handles a message, it stops being sent to the other windows beneath it. A static window doesn't handle a mouse down, so it goes to the dialog. The other controls all handle it in some fashion (setting focus at least) so it never gets to the dialog.
You can override the OnLButtonDown handler in each of the controls to do something else once they've finished their default handling in the base class. You might also get to see the message in the PreTranslateMessage method of the dialog.
As far as getting the List Controls to sync scrolling goes, your best bet would be to intercept WM_VSCROLL by subclassing CListCtrl or perhaps by PreTranslateMessage, and then calling SetScrollInfo on the other list. If the number of items in the lists are the same, it should be fairly simple.