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.
Related
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;
}
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.
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.
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.
I have derived a CButton class and created my own radion button control. Its all working nicely with the exception that I can't get the parent dialog to detect when the radio button it clicked.
The parent dialog will detect the radio button click if I call CButton::OnLButtonUp() but the problem in doing that is that the framework also draws the radio button as well. I don't want to do that as I am drawing the radio button myself.
Can somebody please tell me how to stop Windows/MFC framework from drawing the control in this case? If I don't call CButton::OnLButtonUp() then yeah, Windows/MFC won't draw the control but my parent dialog won't get a BN_CLICKED notification either.
I know I could send a custom message back to my dialog but I don't want that - I want compatability with the BN_CLICKED message.
As you will see below, I have also tried posting a message back to the owning dialog and this doesn't work either.
void CNCCheckBox::OnLButtonUp(UINT nFlags, CPoint point)
{
if( m_Owner )
m_Owner->PostMessage( BN_CLICKED, (WPARAM) IDC_RAD_1/*GetDlgCtrlID()*/, (LPARAM) this->m_hWnd );
//CButton::OnLButtonUp(nFlags,point); // Can't use this!!
}
I've solved it. I got rid of OnDrawItem() (AFX_MSG), and added DrawItem (AFX_VIRTUAL) instead. Also, in PreSubClassWindow() I modify the style to the button is treated as a BS_PUSHBUTTON and BN_CLICKED events are now being sent to my parent dialog.
So in short:
- Don't use OnPaint()
- Don't use OnDrawItem()
- Use:
//{{AFX_VIRTUAL(CNCCheckBox)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL