1

So my program works, all apart from one thing, I would like for my button, 'pushBtn' , aka BTN_PUSH_TALK , to send a BN_PUSHED or BN_UNPUSHED message so I can handle it accordingly.

Following steps online, as well as trial and improvement, right now the only response I ever get is once I am done holding / clicking the button.

 pushBtn = CreateWindowEx(0, L"BUTTON", L"TALK", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON , 0 , 290 , 50, 50, hWnd,(HMENU)BTN_PUSH_TALK, GetModuleHandle(NULL), NULL); 

Handler (or at least what matters) :

 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { bool asd; switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); // Parse the menu selections: switch (wmId) { case BTN_PUSH_TALK: switch (HIWORD(wParam)) { case BN_UNPUSHED: if (connected && inputChoiceStr == "Push To Talk") { tplug->setDuck(false); } break; case BN_PUSHED: if (connected && inputChoiceStr == "Push To Talk") { tplug->setDuck(true); } break; } break; 

I expected once i clicked and held down the button , that the BN_PUSHED case would be entered, however it is not. On letting go, I expect the BN_UNPUSHED case to be entered, but this was not the case either. case BTN_PUSH_TALK is reached, meaning the button is identifiable, however the switch case within this block of code is never reached.

4
  • What's the BS_PUSHLIKE button style for? Mostly to confuse your users. Commented May 28, 2019 at 14:06
  • Edit: Changed BS_PUSHLIKE to BS_DEFPUSHBUTTON Commented May 28, 2019 at 14:30
  • BS_PUSHLIKE and BS_DEFPUSHBUTTON are unrelated. Exchanging one for the other doesn't make sense. The Button Control Overviews explain the fundamentals. Commented May 29, 2019 at 9:36
  • To handle different button state you may really want to use BS_PUSHLIKE? I can't get BN_PUSHED or BN_UNPUSHED work on windows 10. What's your windows version? Commented Jun 13, 2019 at 0:48

2 Answers 2

2

If I'm reading the question right, your goal is to get notifications when a standard push button is initially pushed by the user, whereas standard notification behavior of buttons only posts WM_COMMANDs on "clicks" where a click is the whole mouse down plus mouse up sequence.

Historically in order to get the BN_PUSHED and BN_UNPUSHED notifications in your WM_COMMAND handler you had to use the BS_NOTIFY window style when creating the button. However, if you read the documentation for BN_PUSHED or BN_UNPUSHED you will see

This notification code is provided only for compatibility with 16-bit versions of Windows earlier than version 3.0. Applications should use the BS_OWNERDRAW button style and the DRAWITEMSTRUCT structure for this task.

These were very old notifications that from what I can tell are not just deprecated but no longer even supported. You can do, however, as the documentation suggests: use an owner drawn button i.e. a button created with the BS_OWNERDRAW style.

This turns out to be more difficult than just creating the button with BS_NOTIFY turned on, because the button will no longer perform default painting by itself. Given this added chore, I'd recommend not doing it this way unless you want to custom paint your buttons anyway -- unless you happen to want some nonstandard visual look-and-feel for these buttons as well as nonstandard notification behavior. Otherwise, I would probably just do Win32 subclassing as someone else suggested to trap WM_LBUTTONDOWN etc. and then call the standard button WNDPROC after doing some action on the events i cared about.

Anyway the minimal owner drawn button that reports button down and button up events is like the following. (I post the button events as custom messages but you could do whatever you wish there)

#include <windows.h> #define BTN_ID 101 #define WM_PUSHBUTTONDOWN WM_APP + 1 #define WM_PUSHBUTTONUP WM_APP + 2 HINSTANCE g_instance = 0; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { g_instance = hInstance; MSG msg = { 0 }; WNDCLASS wc = { 0 }; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_BACKGROUND); wc.lpszClassName = L"owner_draw_btn"; if (!RegisterClass(&wc)) return -1; if (!CreateWindow(wc.lpszClassName, L"foobar", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, NULL)) return -1; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } LRESULT HandleDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) { auto* dis = reinterpret_cast<DRAWITEMSTRUCT*>(lParam); if (dis->CtlType != ODT_BUTTON) return 0; auto style = (dis->itemState & ODS_SELECTED) ? DFCS_BUTTONPUSH | DFCS_PUSHED : DFCS_BUTTONPUSH; auto rect = &dis->rcItem; DrawFrameControl(dis->hDC, rect, DFC_BUTTON, style); TCHAR text[512]; auto n = GetWindowText(dis->hwndItem, text, 512); DrawText(dis->hDC, text, n, rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER); if (dis->itemAction == ODA_SELECT) { PostMessage( hWnd, (dis->itemState & ODS_SELECTED) ? WM_PUSHBUTTONDOWN : WM_PUSHBUTTONUP, dis->CtlID, 0 ); } return 0; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: CreateWindow( L"button", L"foobar", BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, 10, 10, 150, 35, hWnd, (HMENU) BTN_ID, g_instance, 0 ); return 0; case WM_DRAWITEM: return HandleDrawItem(hWnd, wParam, lParam); case WM_PUSHBUTTONDOWN: OutputDebugString(L"Button down event\n"); break; case WM_PUSHBUTTONUP: OutputDebugString(L"Button up event\n"); break; case WM_CLOSE: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd, message, wParam, lParam); } 
Sign up to request clarification or add additional context in comments.

Comments

1

Buttons send WM_COMMAND on click. To achieve a push/release notification you must subclass the button class (SetWindowLongPtr() with GWLP_WNDPROC) and then handle WM_LBUTTONDOWN and WM_LBUTTONUP in your new Window Proc.

6 Comments

Anywhere I can find an example of this being done? As in what do you mean by subclass the button class?
Because the button doesn't do what you want by default, you need to replace its Window Procedure -> that's subclassing. In this procedure you will receive WM_LBUTTONDOWN/WM_LBUTTONUP messages and handle them. More on subclassing here
Like so : SetWindowLongPtrA(pushBtn, GWLP_WNDPROC, (LRESULT)mouseStuff); ? Mouse stuff being LRESULT CALLBACK mouseStuff(HWND, UINT, WPARAM, LPARAM);
No, this is the function that will now receive ALL messages for the button. You will handle mouse messages, and the rest of them are to be passed to the old one by using CallWindowProc().
I understand you're not trying to give it all away but your reply just now has made me even more confused, what function are you talking about? I was under the impression that setWindowLongPtrA makes mouseStuff my main callback, what do you mean by "you will handle mouse messages". Are you saying I should use use mousetuff to handle mouse messages but if its not a mouse message I should pass the message off to the old handler?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.