Detect click on a blank item in a list control - c++

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.

Related

Handling Menu Selection from View Class with MFC(Visual Studio)

I'm using Visual Studio and MFC to write a program that will take mouse input from the user to draw a polygon and clip it against a rectangle when the respective menu button is pressed. My problem is, the user input and drawing is handled in the view class(CCssample1View, to be exact), while the menus are in CMainFram class. Even when I define OnMenuSelect function in CCssample1View, it doesn't get notified when a menu button is selected.
Is there a way to associate the menu buttons with the view class so that when they are pressed, the view class' OnMenuSelect function is notified, instead of, or in addition to, that of Main Frame? Or is there another way of handling this problem?
P.S. I literally started using Visual Studio, MFC and OpenGL about 6 hours ago, so I might have understood something monumentally wrong.
Update:
Here's the message maps for the view:
BEGIN_MESSAGE_MAP(CCssample1View, CView)
//{{AFX_MSG_MAP(CCssample1View)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_SIZE()
ON_COMMAND(ID_DBL_BUF, OnDblBuf)
ON_COMMAND(ID_NO_DBL_BUF, OnNoDblBuf)
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
ON_WM_MENUSELECT()
END_MESSAGE_MAP()
and the main frame:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code !
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
creation of the menu(in main frame's OnCreate function):
CMenu *menu = new CMenu;
menu->CreateMenu();
menu->AppendMenuA(MF_STRING, 2002, "Clip");
SetMenu(menu);
lastly, view's event handler:
void CCssample1View::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu)
{
CView::OnMenuSelect(nItemID, nFlags, hSysMenu);
while(1);
// TODO: Add your message handler code here
}
Still, nothing happens when I press the menu button(by that I mean the program runs on without the infinite loop taking effect). What am I doing wrong?
Why do you want to use WM_MENUSELECT?
WM_MENUSELECT is sent only to the main Frame that holds the menu. This Windows message is never routed.
Instead use a WM_COMMAND (ON_COMMAND) handler. WM_COMMAND handlers are routed through the view, Frame, doc and application object according to this documentation http://msdn.microsoft.com/en-us/library/shfzay75.aspx and this http://msdn.microsoft.com/en-us/library/xt2c310k.aspx
If you want to add a handler for more command items you can use ON_COMMAND_RANGE and others.
PS: You have already ON_COMMAND handler in your code!
Menu commands can be handled in the main frame, or the view, or the document. Just add the appropriate entry in the message map for the class where you want to handle the message. Each command can be handled in one place only, so if you already have a message map entry in the main frame and don't want to handle it there remove the line from the main frame message map.

Preventing Mouse-wheel scrolling of controls

I am developing an application using the MFC library and I am currently trying to prevent the user accidentally changing one of the combo box controls when they are scrolling the mouse wheel.
I am looking for a solution without deriving a new class from the CComboBox class and preventing the mouse scrolling there.
My understanding of the system is that Windows passes the WM_MOUSEWHEEL message to the Combo box control which handles it (scrolling the combo box) and then this is propagated up the chain of parent controls (so them to my CFormView etc.), which means I cannot prevent the scrolling by capturing the event in my form view.
Does anyone have a solution to this problem? Thanks in advance!
You can always derive a control from CComboBox and trap the WM_MOUSEWHEEL message in the control itself. Then simply use your new derived combo box in your form view.
If you don't want to create a derived class (perhaps it's too big a change for your project), you can subclass the combo box and trap the WM_MOUSEWHEEL there.
Override the PreTranslateMessage handler in the main window class and look for WM_MOUSEWHEEL messages. Compare the pMsg->hwnd handle in PreTranslateMessage handler with the combobox handle, if found, filter the messages away.

Unhandled LButtonUp windows message in COleControl

I have a COleControl object in my project.
Inside this COleControl, I have a CRectTracker object.
I want to handle the windows message (event) of WM_LButtonUp. (when the user stops to press the left mouse button).
In my .h file I wrote:
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
In my .cpp file, in
BEGIN_MESSAGE_MAP(CMyControl, COleControl)
I added
ON_WM_LBUTTONUP()
Then I developed a method of
void CMyControl::OnLButtonUp(UINT nFlags, CPoint point)
I did the same thing for the windows message LButtonDown.
When I leave the left mouse button inside my control, method get handles and everything works fine.
The problem occurs when I leave the left mouse button when the mouse tracker is on the CRectTracker object. In this case, the mouse LButtonUp message does not get handled.
I tried to use Spy++ and I can see that the windows message WM_LButtonUp exists and occurs as expected. But it my code, it does not get handled (just in the case of mouse on the CRectTracker).
The LButtonDown event get handled successfully all the time, even when I press the mouse button while the mouse is on the CRectTracker.
What can be the problem with the LButtonUp message?
Thanks
That is just an inevitable side-effect of the way the tracker works. Once you click on the tracker or one of the sizing handles then MFC captures the mouse and starts a modal message loop. Which is going to pick off mouse-move and button-up messages to implement the tracking operation. That loop doesn't exit until the left button-up message is seen, the Escape key is seen or the capture is lost. Accordingly, your OnLButtonUp() function cannot run while that loop is active, the message was intercepted before it could be dispatched.
You'll need to do this differently, not relying on OnLButtonUp(). Hard to give specific advice since you didn't describe why you need it. The source code for the modal loop is in atlmfc/src/mfc/trckrect.cpp, CRectTracker::TrackHandle() if you need more insight.

OnLButtonDown() is not fired if I click an item in my dialog (ListBox, CheckBox etc) but it fires ok if I click background or Static Text

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.

"?" help button triggers WM_HELP _and_ WM_LBUTTONUP

I have a Windows application that registers one CALLBACK procedure that handles WM_HELP messages for the dialog, and one CALLBACK procedure that handles WM_LBUTTONUP messages for a custom button.
Now, when the user clicks the "?" button, then clicks the custom button, the help opens up as expected (on mouse down), BUT if the help window is not occluding the button, a WM_LBUTTONUP message is triggered as well for the custom button (on mouse up). This causes the button to trigger when the user was only asking for help.
Is there any way to stop the WM_LBUTTONUP message from being sent if the button press is for help?
EDIT: The custom button is implemented using a STATIC control. I believe this is because it needs to have an image and no border. So, it does not send BN_CLICKED notifications. In fact, it does not seem to trigger WM_COMMAND in the parent at all.
Thanks
This is normal. Be sure to use the button's BN_CLICKED notification to see the difference. Generated when the user clicks the button, not generated when the user uses the help cursor. The button still sees the normal button up/down messages so that's not a good trigger to detect a click. Extra bonus is that the button can now also be clicked with the keyboard (space bar).
A good class library takes care of these nasty little details.
A better way would be to create ? as a custom control with BS_CHECKBOX | BS_PUSHLIKE style and capture the mouse. After that you will get all the WM_LBUTTONDOWN message to this custom control and then you can use WindowFromPoint to get the window where the WM_LBUTTONDOWN happened and can send a custom notification to the parent window. The parent window can then show a the tooltip or open the help doc or discard it.
The advantage is, you create the control only once and you can use it in multiple places.
Okay, I fixed it by creating the custom button (static control) with the SS_NOTIFY style, then handling the STN_CLICKED notification in the WM_COMMAND message. (SS_NOTIFY causes WM_COMMAND to trigger in the parent when it is clicked.) This does not trigger when using the "?" button. Thanks!