winapi BN_CLICKED how to identify which button was clicked? - winapi

I'm creating a simple win32 program using c++, although I think i'm only using c in this app. I need to determine which HWND button was pressed on the app. I've searched msdn reference and it only told me HIWORD is the notification code, and LOWORD is the identifier, for the BN_CLICKED message. I've managed to get as far as determining when a button is clicked, but it only applies for all buttons. All my buttons are created in the WM_CREATE message. This is what i managed to whip up so far:
case: WM_CREATE:
HWND hPlus = CreateWindowEx( 0, L"BUTTON", L"+", WS_CHILD | WS_VISIBLE, 130, 240, 35, 30, hwnd, ( HMENU )IDC_MENU, GetModuleHandle( NULL ), NULL );
HWND hEquals = CreateWindowEx( 0, L"BUTTON", L"=", WS_CHILD | WS_VISIBLE, 170, 205, 65, 65, hwnd, ( HMENU )IDC_MENU, GetModuleHandle( NULL ), NULL );
break;
case WM_COMMAND:
switch( HIWORD( wParam ) )
{
case BN_CLICKED:
MessageBox( hwnd, L"OK", "OK", MB_OK );
break;
}
break;
I've tried comparing hEquals to LOWORD( wParam ) but that gave me an error when compiling. I think I also tried comparing it to HIWORD and LOWORD of lParam as well, which also didn't compile. Now I'm clueless for what to do next.

Give each button it's own ID, and pass it to CreateWindowEx in the hMenu parameter, which is used for that:
A handle to a menu, or specifies a child-window identifier, depending
on the window style.
#define BTN_PLUS 100
#define BTN_EQUAL 101
CreateWindowEx( 0, L"BUTTON", L"+", WS_CHILD | WS_VISIBLE, 130, 240, 35, 30,
hwnd, ( HMENU )BTN_PLUS, GetModuleHandle( NULL ), NULL );
CreateWindowEx( 0, L"BUTTON", L"=", WS_CHILD | WS_VISIBLE, 170, 205, 65, 65,
hwnd, ( HMENU )BTN_EQUAL , GetModuleHandle( NULL ), NULL );
Then, in WM_COMMAND, you can test for the ID:
case WM_COMMAND: {
if ( LOWORD( wParam ) == BTN_PLUS ) {
[...]
}
[...]
break;
}

You just need to look at the lParam it's the button handle:
if ((HWND)lParam == hPlus)
{
// "plus" clicked ... etc.
}
Though in your code, you'll need to keep the HWND's in global variables to do the comparison.
// somewhere global
HWND hPlus = NULL;
HWND hEquals = NULL;
// in your WndProc ...
case: WM_CREATE:
hPlus = CreateWindowEx( 0, L"BUTTON", L"+", WS_CHILD | WS_VISIBLE, 130, 240, 35, 30, hwnd, ( HMENU )IDC_MENU, GetModuleHandle( NULL ), NULL );
hEquals = CreateWindowEx( 0, L"BUTTON", L"=", WS_CHILD | WS_VISIBLE, 170, 205, 65, 65, hwnd, ( HMENU )IDC_MENU, GetModuleHandle( NULL ), NULL );
break;
case WM_COMMAND:
switch( HIWORD( wParam ) )
{
case BN_CLICKED:
// see which button was clicked
if ((HWND)lParam == hPlus)
{
MessageBox( hwnd, L"hPlus was clicked", "OK", MB_OK );
}
break;
}
break;
You get the idea, I'm sure....

Related

Message after clicking on the edit control

I’m new to WinAPI development.
The main window contains three Edit controls.When clicking with the left mouse button on Edit1, the message "Edit1 choosed" is to be displayed. When you click on Edit2, it is supposed to display the message "Edit2 choosed". Similarly, when you click on Edit3, the message "Edit3 choosed" should be displayed. I tried using various combinations of WM_LBUTTONDOWN and WM_SETFOCUS, unfortunately the program does not work. Could someone please help me?
#include <windows.h>
#define Edit1 501
#define Edit2 502
#define Edit3 503
HWND TextBox1, TextBox2, TextBox3;
HFONT HF = CreateFont (30, 0, 00, 00, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {
switch(Message)
{
case WM_SETFOCUS:
if(wParam == Edit1)
{
MessageBox(NULL, "Edit1 choosed","Edit1",MB_ICONEXCLAMATION|MB_OK);
}
break;
if(wParam == Edit2)
{
MessageBox(NULL, "Edit2 choosed","Edit2",MB_ICONEXCLAMATION|MB_OK);
}
break;
if(wParam == Edit3)
{
MessageBox(NULL, "Edit3 choosed","Edit3",MB_ICONEXCLAMATION|MB_OK);
}
break;
case WM_CREATE:
{
TextBox1 = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_RIGHT,
30, 70, 190, 40, hwnd, (HMENU)Edit1, GetModuleHandle(NULL), NULL);
SendMessage(TextBox1, WM_SETFONT,( WPARAM ) HF, 0 );
TextBox2 = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_RIGHT,
30, 130, 190, 40, hwnd, (HMENU)Edit2, GetModuleHandle(NULL), NULL);
SendMessage(TextBox2, WM_SETFONT,( WPARAM ) HF, 0 );
TextBox3 = CreateWindowEx( WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_RIGHT,
30, 190, 190, 40, hwnd, (HMENU)Edit3, GetModuleHandle(NULL), NULL);
SendMessage(TextBox3, WM_SETFONT,( WPARAM ) HF, 0 );
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
memset(&wc,0,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = "WindowClass";
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc)) {
MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,"WindowClass","Caption",WS_VISIBLE|WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL,NULL,hInstance,NULL);
if(hwnd == NULL) {
MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
the "break" in the message loop under WM_SETFOCUS is always executed. So the program never reaches the point of the second edit control. Move the break to before the closing "}" and retry.
{
MessageBox(NULL, "Edit1 choosed","Edit1",MB_ICONEXCLAMATION|MB_OK);
break;
}
Furthermore, consider using 'else if'.

change label's text fails after set its background color

In order to make the static control background transparent, I did handle the WM_CTLCOLORSTATIC like this:
// make label transparent
case WM_CTLCOLORSTATIC:
{
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(0,0,0));
SetBkMode(hdcStatic, TRANSPARENT);
return (LRESULT)GetStockObject(NULL_BRUSH);
}
the label is created like this:
LRESULT CALLBACK Win2Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
hTab3_label =
CreateWindowW(L"Static", L"This is my dear text!",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
50, 90, 130, 25, hwnd, (HMENU) 18, NULL, NULL);
break;
// ...
}
}
the GUI is fine so far:
but when I set a new text to it from a button call, like this:
case WM_COMMAND:
switch(LOWORD(wParam))
{
// ....
case ID_TAB1_BUTTONB:
//MessageBox(NULL, L"Click on B button", L"", MB_OK);
SetWindowText(hTab3_label, L"This is the new text!");
break;
}
break;
the static control gets messed up, without replace the previous text for some reason, like the image below. If I remove the case WM_CTLCOLORSTATIC the text is replaced properly. What am I missing?

DialogBox not drawing the dialog correctly

So I have the following dialogbox template which I saved in a .rc file:
DIALOG_TEST DIALOG 0, 0, 186, 95
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "Ms Shell Dlg"
{
PUSHBUTTON "Cancel", IDD_CAN, 129, 24, 50, 14, 0, WS_EX_LEFT
DEFPUSHBUTTON "OK", IDD_OK, 129, 7, 50, 14, 0, WS_EX_LEFT
}
which I call using DialogBox in the following way:
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HWND button;
switch (message) /* handle the messages */
{
case WM_CREATE:
button = CreateWindowEx(WS_EX_CLIENTEDGE, "BUTTON",
"Test",
WS_VISIBLE | WS_CHILD | WS_BORDER,
200, 40, 200, 30,
hwnd, (HMENU) 1, NULL, NULL);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case 1:
DialogBox(GetModuleHandle(NULL), TEXT("DIALOG_TEST"),
hwnd, AboutDlgProc);
break;
}
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
All works fine except that for some reason the window is not being drawn correctly:
it's like the dialogbox is being drawn twice, what am I doing wrong?
As for the Window Procedure for the dialogbox, the only thing it does is close the window when the "Cancel" is pressed.
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
switch(HIWORD(wParam))
{
case BN_CLICKED:
switch (LOWORD (wParam))
{
case IDD_CAN:
EndDialog(hDlg, TRUE);
return TRUE ;
}
break;
}
break;
}
}
A button can send more than one kind of notification in a WM_COMMAND message, so if you want to create the dialog box only when the button is clicked you need to check to make sure that the notification code (in HIWORD(wParam)) is BN_CLICKED. See WM_COMMAND documentation.

Detect Button Press in Tab Page

I am using the WinAPI to create a GUI utility. I have two Tabs and each tab has some buttons. I am creating the buttons with this function:
CreateWindowEx(NULL,"button", "Clear", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
150, 175, 100, 25, tab_window_2, (HMENU) CLEAR_DATA, instance_handle, NULL);
In the Windows proc function, I don't know how to detect when the above button is pressed. I also tried handling CLEAR_DATA in the WM_COMMAND switch construct, as follows.
switch ( message ) {
case WM_COMMAND:{
switch(LOWORD(wparam)) {
case CLEAR_DATA : break
}
}
How can I detect and handle those buttons being pressed?
I am creating the tab_window_2 tab as follows :
class frame_window {
private:
LPCSTR window_class_name;
HINSTANCE instance_handle;
HCURSOR cursor_arrow;
HWND window_handle;
HWND tab_handle;
HWND tab_window_1;
HWND tab_window_2;
HWND current_tab_window;
RECT client_rectangle;
public:
frame_window(LPCSTR window_class_identity) : window_class_name(window_class_identity) {
INITCOMMONCONTROLSEX common_controls;
common_controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
common_controls.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&common_controls);
int screen_width = GetSystemMetrics(SM_CXFULLSCREEN);
int screen_height = GetSystemMetrics(SM_CYFULLSCREEN);
instance_handle = GetModuleHandle(NULL);
WNDCLASS window_class = { CS_OWNDC, main_window_proc, 0, 0,
instance_handle, NULL,
NULL, NULL, NULL,
window_class_name };
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Create a standard frame window
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RegisterClass(&window_class);
window_handle = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
window_class_name,
"IR Remote and Barcode Demo",
WS_OVERLAPPEDWINDOW |
WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
100, 100, screen_width-1600,
screen_height-490, NULL, NULL,
instance_handle, NULL);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Get the size of the client rectangle for the window we have just created
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RECT client_rect;
GetClientRect(window_handle, &client_rect);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Create the tab control window.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tab_handle = CreateWindowEx(NULL, WC_TABCONTROL, NULL,
WS_CHILD | WS_VISIBLE,
10, 10, client_rect.right-client_rect.left-20,
client_rect.bottom-client_rect.top-10,
window_handle, NULL,
instance_handle, NULL);
// Create three tabs.
TCITEM tab_info;
memset(&tab_info, 0, sizeof(tab_info));
tab_info.mask = TCIF_TEXT;
tab_info.pszText = "tab#1";
tab_info.cchTextMax = 5;
SendMessage(tab_handle, TCM_INSERTITEM, 0, (LPARAM)&tab_info);
tab_info.pszText = "tab#2";
SendMessage(tab_handle, TCM_INSERTITEM, 1, (LPARAM)&tab_info);
RECT tab_rectangle;
GetClientRect(tab_handle, &tab_rectangle);
SendMessage(tab_handle, TCM_ADJUSTRECT, FALSE, (LPARAM)&tab_rectangle);
// Create the tab view windows
tab_window_1 = CreateWindowEx(NULL, "STATIC", " ",
WS_CHILD | WS_VISIBLE|SS_OWNERDRAW,
tab_rectangle.left+10, tab_rectangle.top+10,
tab_rectangle.right-tab_rectangle.left,
tab_rectangle.bottom -tab_rectangle.top,
tab_handle, (HMENU)1,
instance_handle, NULL);
SetParent(tab_window_1, window_handle);
current_tab_window = tab_window_1;
tab_window_2 = CreateWindowEx(NULL, "STATIC", " ",
WS_CHILD|SS_OWNERDRAW,
tab_rectangle.left+10, tab_rectangle.top+10,
tab_rectangle.right-tab_rectangle.left,
tab_rectangle.bottom -tab_rectangle.top,
tab_handle, (HMENU)2,
instance_handle, NULL);
CreateWindowEx(NULL,"button", "Clear", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
150, 175, 100, 25, tab_window_2, (HMENU) CLEAR_DATA, instance_handle, NULL);
SetParent(tab_window_2, window_handle);
SetCursor(LoadCursor(NULL, IDC_ARROW));
SetWindowLongPtr(window_handle, GWL_USERDATA, (LONG)this);
ShowWindow(window_handle, SW_SHOW);
UpdateWindow(window_handle);
}
~frame_window() {
UnregisterClass(window_class_name, instance_handle);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Windows main entry point
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int WINAPI wWinMain(HINSTANCE instance_handle, HINSTANCE, LPWSTR, INT) {
frame_window main_window("my base window");
main_window.run();
return 0;
}
The WM_COMMAND for handling the events from a Push Button must be in the Window Procedure of it's parent. Your code show tab_window_2 as the parent for the CLEAR_DATA button.
Move the WM_COMMAND code in the right Window Proc (or Dialog Proc).
EDIT: I think you didn't understand how the Tab Control works.
If you want each Tab View to contain more than a dummy static control, you should use modeless and borderless DialogBox, children of the Tab Control, and show/hide them accordingly to the TCN_SELCHANGE notification.
If you don't realy need the Dialog Box functionalities, you could create regular child windows (with RegisterClass/CreateWindow) and then add your controls as child windows.
In either case, the show/hide code must react to TCN_SELCHANGE.
With regular child windows or with Dialog Box the net effect is the same: you will have a Window Procedure, or a Dialog Procadure, in which WM_COMMAND will deal with user actions.
Don't play with SetParent. But, if you absolutly want it, then use:
HWND hWndPushClear = CreateWindowEx(NULL,"button", "Clear", [...]
SetParent( hWndPushClear, window_handle );
You may have weird Paint/Focus problems, however.
EDIT: Unfortunately, it seems that there is no good Tab Control tutorial in line.
I recommend updating your actual source code. As a starting point, do the following:
Replace CreateWindowEx by CreateDialog for tab_window_1 end tab_window_2 (only one Dialog must be visible)
Suppress all SetParent calls
Your CreateWindowEx() and WM_COMMAND code are seems correct.
So,
Check return value of CreateWindowEx() whether is NULL. NULL means which is failed to create control.
Check HWND of parent is valid. In your code, it is tab_window_2.
Also, if you do not want to any WS_EX_XXX style, you can just use CreateWindow().
This code is just sample.
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
switch(iMessage) {
case WM_CREATE:
CreateWindow("button","Clear",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
20,20,100,25,hWnd,(HMENU)0,g_hInst,NULL);
return 0;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case 0:
MessageBox(hWnd,"Clear Button Clicked","Button",MB_OK);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

SendMessage(..., CB_GETCURSEL, 0, 0) always returns 0 (Winapi)

First of all forgive me, but I am total newb. I am trying to write a program that recognize my combobox selection when I click on the screen (I pick the item I want to place there). However I cannot, because the SendMessage function always returns 0. How can I get the proper result?
HWND g_Combobox;
/* ... */
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
/* ... */
switch (message)
{
case WM_CREATE:
{
HWND g_Combobox = CreateWindowEx( WS_EX_CLIENTEDGE, L"COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER |
CBS_DROPDOWNLIST, 5, 25, 180, 200, hWnd, (HMENU) ID_MYCOMBO, hInst, NULL );
SendMessage( g_Combobox, CB_ADDSTRING, 0,( LPARAM ) L"item 1" );
SendMessage( g_Combobox, CB_ADDSTRING, 0,( LPARAM ) L"item 2" );
SendMessage( g_Combobox, CB_ADDSTRING, 0,( LPARAM ) L"item 3" );
/* ... */
}
break;
case WM_LBUTTONDOWN:
{
switch (SendMessage(g_Combobox, CB_GETCURSEL, 0, 0))
{
case 0: //always picks this one
MessageBox( NULL, L"0", L"Oh noes!", MB_ICONEXCLAMATION );
break;
default:
MessageBox( NULL, L"something diffrent than 0", L"Yeah...", MB_ICONEXCLAMATION );
break;
}
}
What am I doing wrong?
HWND g_Combobox = CreateWindowEx(...
Replace with:
g_Combobox = CreateWindowEx(...
Your current code fills local variable, leaving global variable unchanged. This is why SendMessage working with global variable gives unexpected results.
To solve such kind of problems in the future:
Use debugger.
Use maximal available compiler warning level.

Resources