Add event handler to button in c++ - c++

I created a button in c++ as follows:
HWND btn = CreateWindow(
"BUTTON",
"OK",
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
10,
10,
100,
100,
hWnd,
NULL,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
The button is displayed in the main window (hWnd) but I don't know how or where to give it an event handler. Any help please?

There are three ways to detect the button being clicked.
The preferred approach is to add a WM_COMMAND handler to the window procedure of the button's parent window. When the button is clicked, it sends a BN_CLICKED notification to its parent window. This is described in the MSDN documentation for buttons:
Handling Messages from a Button
Notification Messages from Buttons
If you are adding the button to a parent window that you do not own, you can subclass the parent window using SetWindowsLongPtr(GWL_WNDPROC) or SetWindowSubClass(), and then you can handle messages that are sent to it, such as BN_CLICKED. This only works if the subclassing code runs in the same thread that owns the parent window.
Alternatively, you can subclass the button itself and handle keyboard and mouse messages instead.
Another option is to set an event hook using SetWinEventHook() asking to receive EVENT_OBJECT_INVOKED events. In the event callback procedure, the provided hwnd, ID, and idChild parameters will identify the control that is being invoked, such as a clicked button.

When the button is clicked, it sends a BN_CLICKED notification message (carried by the WM_COMMAND message) to its parent window. The BN_CLICKED notification code is in the HIWORD of the wParam of the message. The LOWORD of the wParam of the message has the ID of the button. The lParam of the message has the HWND of the button. This is all in the online Windows docs. Google for BN_CLICKED.
Consider this pseudo code... it's from memory. Basically, add the stuff inside the WM_COMMAND case to the window procedure that you already have:
LRESULT WINAPI YourWindowProc(HWND hWnd, UINT nMsg, WPARAM wp, LPARAM lp)
{
switch (nMsg)
{
case WM_COMMAND:
{
switch (HIWORD(wp))
{
case BN_CLICKED:
{
switch (LOWORD(wp))
{
case IDC_BUTTON1: // or whatever the ID of your button is...
{
// do stuff for button...
break;
}
}
break;
}
}
break;
}
default:
return DefWindowProc(hWnd, nMsg, wp, lp);
}
return 0;
}

In the second-to-last parameter, type (HMENU)1
in the WNDPROC:
switch (uMsg)
{
case WM_COMMAND:
switch (wParam)
{
case 1:
// Code
}
}

Related

Button Control disappears on click - Click Handler does not get fired on Clicking Button - Win32

I am working on Zoom SDK which is based on win32 gui.
I have created 3 buttons using CreateWindow method on the window handle, which is provided by the ZoomSDK.
Code + Screenshot - 1
Now there are two problems with this.
As soon as I click the buttons, they disappear.
See the Screen Shots BEFORE
See the Screen Shots AFTER
I want to know the reason why this is happening and how can I fix this?
HWND hFirstView, hSecondView;
cntrl->GetMeetingUIWnd(hFirstView, hSecondView);
cntrl->MoveFloatVideoWnd(100, 100);
HWND btnHwnd = CreateWindow(
TEXT("button"),
L"Open App",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
0, 0,
50, 25,
hFirstView,
(HMENU)100,
hInst,
NULL);
HWND btnHwnd2 = CreateWindow(
TEXT("button"),
L"Other",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
50, 0,
50, 25,
hFirstView,
(HMENU)101,
hInst,
NULL);
HWND btnHwnd3 = CreateWindow(
TEXT("button"),
L"Raise Hand",
WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
100, 0,
50, 25,
hFirstView,
(HMENU)103,
hInst,
NULL);
HDC hdc = GetDC(btnHwnd);
SetBkColor(hdc, GetSysColor(COLOR_BTNSHADOW));
SetTextColor(hdc, GetSysColor(COLOR_BACKGROUND));
ReleaseDC(btnHwnd, hdc);
int btnId = GetDlgCtrlID(btnHwnd);
//oldWndProc = (WNDPROC) GetWindowLong(hFirstView, GWL_WNDPROC);
oldWndProc = (WNDPROC) SetWindowLong(hFirstView,
GWL_WNDPROC, (LONG)WndProc);
SendMessage(btnHwnd, BM_SETSTATE, 1, 0);
SetWindowText(hFirstView, L"Title");
I want to handle click event for these buttons. I have tried to use SetWindowsLong to set another WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int id = GetWindowLong(hWnd, GWL_ID);
switch (message)
{
case WM_COMMAND: {
MessageBox(NULL, L"Sign of releaf!", L"Whoaa!", 0);
if (wParam == 1023) {
MessageBox(NULL, L"Sign of releaf!", L"Whoaa!", 0);
}
}
break;
}
return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
}
Now, this WndProc gets called for other events such mouse move etc. It is not working for my three buttons. I want to handle click event i.e WM_COMMAND or any other technique possible.
Because I can not go inside the sdk (they don't provide sources, only .lib) so I can not change their WndProc, nor their internal WM_PAINT. The buttons are sort of overlay on top.
You should not be calling SetBkColor() and SetTextColor() from outside a WM_PAINT handler. The correct way to color a button is to either:
have the parent window handle the WM_CTLCOLORBTN notification.
The WM_CTLCOLORBTN message is sent to the parent window of a button before drawing the button. The parent window can change the button's text and background colors. 
give the button the BS_OWNERDRAW style, and then have the parent window handle the WM_DRAWITEM notification.
Sent to the parent window of an owner-drawn button, combo box, list box, or menu when a visual aspect of the button, combo box, list box, or menu has changed.
Also, when a button sends a BN_CLICKED notification to its parent window, your subclass WndProc() doesn't need to use GetWindowLong(GWL_ID). First, you are calling it on the wrong HWND. And second, the button ID is carried in the message's wParam data.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case 100:
case 101:
case 103:
MessageBox(NULL, L"Sign of relief!", L"Whoaa!", 0);
break;
}
}
break;
}
}
return CallWindowProc(oldWndProc, hWnd, message, wParam, lParam);
}

Winapi detect button hovering

I've got a C++ project in which I'm using the Winapi to develop a window with a button and I want to change the text of the button when it's being hovered. For example, changing "Click me" to "Click me NOW!", when hovered. I've tried searching but I've not found any good ways to do this.
I noticed that when user hovers, the WM_NOTIFY message is received, but I don't know how to ensure that it has been called by the mouse hover. I've found that I can use TrackMouseEvent to detect hovering, but it's limited to a period of time and I want to execute an action every time the user hovers the button.
Here is how I create a button:
HWND Button = CreateWindow("BUTTON", "Click me",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_NOTIFY,
20, 240, 120, 20,
hwnd, (HMENU)101, NULL, NULL);
And this my window procedure:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_NOTIFY:
{
//??? Here is where I get a message everytime I hover the button, But I don't know any proper way to see if it has been executed by the button.
}
case WM_CREATE: //On Window Create
{
//...
}
case WM_COMMAND: //Command execution
{
//...
break;
}
case WM_DESTROY: //Form Destroyed
{
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Assuming you're using the common controls there is the BCN_HOTITEMCHANGE notification code for the WM_NOTIFY message. The message includes the NMBCHOTITEM structure, which includes information for whether the mouse is entering or leaving the hover area.
Here's an example:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_NOTIFY:
{
LPNMHDR header = *reinterpret_cast<LPNMHDR>(lParam);
switch (header->code)
{
case BCN_HOTITEMCHANGE:
{
NMBCHOTITEM* hot_item = reinterpret_cast<NMBCHOTITEM*>(lParam);
// Handle to the button
HWND button_handle = header->hwndFrom;
// ID of the button, if you're using resources
UINT_PTR button_id = header->idFrom;
// You can check if the mouse is entering or leaving the hover area
bool entering = hot_item->dwFlags & HICF_ENTERING;
return 0;
}
}
return 0;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
You can check the code of the WM_NOTIFY message to see if it is a NM_HOVER message.
switch(msg)
{
case WM_NOTIFY:
if(((LPNMHDR)lParam)->code == NM_HOVER)
{
// Process the hover message
}
else if (...) // any other WM_NOTIFY messages you care about
{}
}
You can use simply SFML to do this.
Code:
RectangleShape button;
button.setPosition(Vector2f(50, 50));
button.setSize(Vector2f(100, 40));
button.setFillColor(Color::Green);
if(button.getGlobalBounds().contains(static_cast<Vector2f>(Mouse::getPosition(/*your
window name*/window)
{
button.setFillColor(Color::Red);
}

c++ win32 api notify mouse inside button area

I've a Button made using Win32Api that I want to be able to notify whenever the user put the mouse inside the button rectangle.
I noticed that when user does that the WM_NOTIFY is called but I don't know which flag to use for ensure that the user has the mouse inside it's area.
Here is my button:
HWND Button = CreateWindow("BUTTON", "Test",
WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_NOTIFY,
20, 240, 120, 20,
hwnd, (HMENU)101, NULL, NULL);
And my WndProc:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_NOTIFY:
{
//??? Here is where I want to do it
}
case WM_CREATE: //On Window Create
{
wHWND = hwnd;
if (onCreate != NULL)
onCreate(hwnd);
break;
}
case WM_COMMAND: //Command execution
{
//...
break;
}
case WM_DESTROY: //Form Destroyed
{
if (onDestroy != NULL)
onDestroy(hwnd);
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
I don't know if I'm in the right path using WM_NOTIFY or not, maybe there is an easier way to do that. Thank you!
Try catching the WM_SETFOCUS message instead if you want a generic way of tracking focus.
BN_SETFOCUS is sent as WM_COMMAND in the upper 16 bits of WPARAM (HIWORD):
The parent window of the button receives this notification code through the WM_COMMAND message
If you are not talking about focus but just the mouse position then you can use ChildWindowFromPointEx or RealChildWindowFromPoint and a timer.

subclassing child window c++

I am working on windows and i chose the c++ as language. My application is a win32 application and I am not using MFC classes, but only the native API.
My main window contain a Tab control in which there is a Listview control. I tried to Subclass both the control, but it is not working even though i follow the msdn guide.
The function I used are:
SetWindowLong and GetWindowLong
so I changed the child's procedures but it seems the main window catches every messages that controls send ( through WM_NOTIFY ) and every WM messages. I thought that it may concern focus but I do not have any idea on how implement this kind of situation: a child control which is the parent of an another child control.
I have created the Tab control in this way.
code:
hwndTab = CreateWindow(WC_TABCONTROL, "",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, rcClient.right, rcClient.bottom,
winHandle, NULL, hInst, NULL);
OldUserTabProc = (WNDPROC)GetWindowLong(hwndTab, GWL_WNDPROC);
SetWindowLong(hwndTab, GWL_WNDPROC, (LONG_PTR)UserTabProc);
Here The Tab control procedure
LRESULT CALLBACK UserTabProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch ( message )
{
case WM_LBUTTONDOWN:
break;
case WM_RBUTTONUP:
break;
case TCN_SELCHANGE:
{
userCurrentTab = TabCtrl_GetCurSel( userTab );
break;
}
case TCN_SELCHANGING:
{
break;
}
};
return CallWindowProc( OldUserTabProc, hWnd, message, wParam, lParam );
}
The listview has as parent the Tab control.
Values like TCN_SELCHANGE are not message types and they aren't sent to the control themselves. They are notifications (the N stands for "notification"). Notifications are sent by the control to the parent using a message like WM_NOTIFY or WM_COMMAND. You cannot intercept them by subclassing the control.
If you want to change the way the parent handles those notifications, you need to subclass the parent.

Win32: C++: How do I re-focus on Parent Window after clicking in a child window?

In my Win32 CPP program, I have defined some Child Window to display various text strings using something like:
hnd_to_this_ch_window = CreateWindow(
L"EDIT",L"Some initial text", WS_VISIBLE | WS_CHILD | ES_LEFT,
position_of_this_window_X,
position_of_this_window_Y,
TEXTOUT_DEFAULT_WIDTH,
TEXTOUT_DEFAULT_HEIGHT,
handle_to_my_parent_window, NULL,
hinstance_variable_used_by_create_window,
NULL )
My problem is that if I click with my mouse to select the text in one of such child windows (to, say, copy it somewhere), the focus of the application goes to this child window and so any keypresses which used to be handled through my main windows CALLBACK (with case WM_KEYDOWN:) are now captured into the child window, where they appear as inputted characters. What magic function do I call to have the focus go back to the parent (so that my WM_KEYDOWN) can work again? I was hoping I could just click on the main Window's title bar and that would take it back to normal, but that isn't working (because, obviously, my program is lacking some extra logic).
Handle the WM_KILLFOCUS message in the window procedure of the window you want to focus, and restore the focus using the SetFocus function.
If you want to focus the window when it is clicked, handle the WM_LBUTTONDOWN message.
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
// Restore the focus when it was lost.
if (Msg == WM_KILLFOCUS) {
SetFocus(hWnd);
// Msg was handled, return zero.
return 0;
}
// Or when the window is clicked.
if (Msg == WM_LBUTTONDOWN) {
SetFocus(hWnd);
// Msg was handled, return zero.
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
case WM_KEYDOWN:
SetFocus(Parent_Hwnd);
return SendMessage(Parent_Hwnd,WM_KEYDOWN,wParam,lParam);