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

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.

Related

Notification of colour changed on CMFCColorButton?

I have an MFC Color Button on my dialog. I'd like to detect when a user selects a different colour but can't seem to pick up any notifications.
From reading the documentation I thought this would work;
// Within the CPP
BEGIN_MESSAGE_MAP(CSampleCode, CDialogEx)
ON_NOTIFY(BN_CLICKED, IDC_MFCCOLOR_BUTTON, OnColorSelectionChanged)
END_MESSAGE_MAP()
void CSampleCode::OnColorSelectionChanged(NMHDR* pNMHDR, LRESULT* pResult)
{
// Do Something
}
I have other controls in the dialog whose notifications I am picking up without problems. Can't seem to get the MFC Color Button to trigger anything though.
Any thoughts?
From the BCG help files...
The CBCGColorButton object notifies its parent about color changing by
WM_COMMAND | BN_CLICKED notification. The parent may retrieve the
current color by calling GetColor method.
Looks like you're looking for a notify message when you should be trapping WM_COMMAND.

Detect click on a blank item in a list control

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.

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.

Is there any function called after the OnInitDialog function in MFC?

I want to create a thread after the creation of a dialog box in MFC. Is there any function that Windows has provided and is automatically called after OnInitDialog so that I can create my thread inside it?
You can simply create your thread in the OnInitDialog function. There's no reason to overcomplicate things by going and searching for a different function, or splitting your initialization code up in two pieces. (There also isn't any such function, because there's no corresponding Windows message that is sent.)
If you want to get your dialog box on the screen before you create the thread, you can just show it manually using the ShowWindow function. For example:
ShowWindow(SW_SHOW);
RedrawWindow();
Also see this post by Raymond Chen: Waiting until the dialog box is displayed before doing something
OnInitDialog() is the main function called upon initialization (in reaction to WM_CREATE).
Why can't you create your thread in there?
I have changed the thread priority to below normal and when the thread executes for the first time I set the thread to normal priory. This works fine. Thanks for your response.
After many years of feeling unsatisifed with the OnTimer solution to draw first view graphics in an MFC dialog app (a favorite playground), this seemed like a nice simple solution:-
Add a WM_HSCROLL handler with class wizard.
At the end of OnInitDialog post a hscroll message with a NULL LPARAM
In handler detect the NULL, draw graphics.
a timer meant that the app was alive for some time before graphics happened, and apparently hscroll is prioritized to happen just after the WM_PAINT message which would erase a picture element to its blank state, deleting anything that was drawn during initdialog.
BOOL CSpecDlg::OnInitDialog()
{
...
PostMessage(WM_HSCROLL,0, (LPARAM)NULL);
return TRUE; // return TRUE unless you set the focus to a control
}
void CSpecDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
if (pScrollBar==NULL)
{
plot();
}
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}