如何在winApi上正确创建按钮以及处理其消息

How to correctly create push buttons on winApi as well as Handle its messages

本文关键字:按钮 处理 消息 创建 winApi      更新时间:2023-10-16

所以我的程序可以工作,除了一件事,我想让我的按钮'pushBtn',又名BTN_PUSH_TALK,发送BN_PUSHED或BN_UNPUSHED消息,以便我可以相应地处理它。

按照在线步骤以及试用和改进,现在我得到的唯一回应是一旦我完成按住/单击按钮。

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);

处理程序(或至少重要):

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;

我希望一旦我单击并按住按钮,就会输入BN_PUSHED案例,但事实并非如此。 放手后,我预计会进入BN_UNPUSHED案,但事实并非如此。 达到大小写 BTN_PUSH_TALK,这意味着按钮是可识别的,但永远不会到达此代码块中的开关大小写。

如果我没看错,您的目标是在用户最初按下标准按钮时获得通知,而按钮的标准通知行为仅在"单击"上发布WM_COMMANDs,其中单击是整个鼠标按下加上鼠标向上序列。

过去,为了在WM_COMMAND处理程序中获取BN_PUSHED和BN_UNPUSHED通知,您必须在创建按钮时使用BS_NOTIFY窗口样式。但是,如果您阅读BN_PUSHED或BN_UNPUSHED的文档,您将看到

提供此通知代码仅用于与低于版本 3.0 的 16 位版本的 Windows 兼容。应用程序应为此任务使用 BS_OWNERDRAW 按钮样式和 DRAWITEMSTRUCT 结构。

这些是非常旧的通知,据我所知,它们不仅被弃用,而且甚至不再受支持。但是,您可以按照文档的建议进行操作:使用所有者绘制的按钮,即使用BS_OWNERDRAW样式创建的按钮。

事实证明,这比仅创建打开BS_NOTIFY的按钮更困难,因为按钮将不再自行执行默认绘画。鉴于这增加了繁琐的工作,我建议不要这样做,除非您无论如何都想自定义绘制按钮 - 除非您碰巧想要这些按钮的一些非标准的视觉外观以及非标准的通知行为。否则,我可能只会按照其他人的建议进行 Win32 子类化以捕获WM_LBUTTONDOWN等,然后在对我关心的事件执行一些操作后调用标准按钮 WNDPROC。

无论如何,报告按钮关闭和按钮打开事件的最小所有者绘制按钮如下所示。(我将按钮事件作为自定义消息发布,但您可以在那里做任何您想做的事情)

#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 eventn");
break;
case WM_PUSHBUTTONUP:
OutputDebugString(L"Button up eventn");
break;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}

按钮在单击时发送WM_COMMAND。要实现推送/释放通知,您必须对按钮类(带有GWLP_WNDPROC的 SetWindowLongPtr() 进行子类化,然后在新的 Window Proc 中处理WM_LBUTTONDOWN和WM_LBUTTONUP。