C++ 子类按钮不在每次重复单击时生成动画
我在我的C++ 子类按钮不在每次重复单击时生成动画,c++,user-interface,winapi,button,subclass,C++,User Interface,Winapi,Button,Subclass,我在我的WindowProc回调的WM_CREATE消息中创建了一个自定义子类按钮。以下是创建和子类化说明,以及用于控制按钮状态的结构: static button_state btnstateBtnInstall; hBtnInstall = CreateWindow(WC_BUTTON, L"Button", WS_CHILD | WS_VISIBLE, (window_width / 2) - (btn_install_width / 2), window_height - (window
WindowProc
回调的WM_CREATE
消息中创建了一个自定义子类按钮。以下是创建和子类化说明,以及用于控制按钮状态的结构:
static button_state btnstateBtnInstall;
hBtnInstall = CreateWindow(WC_BUTTON, L"Button", WS_CHILD | WS_VISIBLE, (window_width / 2) - (btn_install_width / 2), window_height - (window_height / 6) - (btn_install_height / 2), btn_install_width, btn_install_height, hwnd, (HMENU)HMENU_btn_install, NULL, NULL);
SetWindowSubclass(hBtnInstall, BtnInstallProc, 0, (DWORD_PTR)&btnstateBtnInstall);
结构定义如下:
struct button_state
{
bool pushed;
button_state() { pushed = false; }
};
LRESULT CALLBACK BtnInstallProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubClass, DWORD_PTR dwRefData)
{
button_state* state = (button_state*)dwRefData;
// Omitted part where I create brushes and font to be used for painting
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwnd, &pt);
BOOL hover = PtInRect(&rc, pt);
if (state->pushed)
{
// Pushed
FillRect(hdc, &rc, hBrPushed);
}
else if (hover)
{
// Mouse over
FillRect(hdc, &rc, hBrHover);
}
else
{
// Normal
FillRect(hdc, &rc, hBrNormal);
}
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(255, 255, 255));
SelectFont(hdc, SegoeUI);
static LPCWSTR InstallBtnTxt = L"Install";
static int InstallBtnTxtLen = static_cast<int>(wcslen(InstallBtnTxt)); // Should be a safe cast, for small arrays like this one
DrawText(hdc, InstallBtnTxt, InstallBtnTxtLen, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
}
case WM_LBUTTONDOWN:
{
state->pushed = true;
break;
}
case WM_LBUTTONUP:
{
state->pushed = false;
break;
}
// Omitted part where I handle WM_DESTROY to do cleanup
}
return DefSubclassProc(hwnd, msg, wParam, lParam);
}
子类程序编码如下:
struct button_state
{
bool pushed;
button_state() { pushed = false; }
};
LRESULT CALLBACK BtnInstallProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubClass, DWORD_PTR dwRefData)
{
button_state* state = (button_state*)dwRefData;
// Omitted part where I create brushes and font to be used for painting
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwnd, &pt);
BOOL hover = PtInRect(&rc, pt);
if (state->pushed)
{
// Pushed
FillRect(hdc, &rc, hBrPushed);
}
else if (hover)
{
// Mouse over
FillRect(hdc, &rc, hBrHover);
}
else
{
// Normal
FillRect(hdc, &rc, hBrNormal);
}
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(255, 255, 255));
SelectFont(hdc, SegoeUI);
static LPCWSTR InstallBtnTxt = L"Install";
static int InstallBtnTxtLen = static_cast<int>(wcslen(InstallBtnTxt)); // Should be a safe cast, for small arrays like this one
DrawText(hdc, InstallBtnTxt, InstallBtnTxtLen, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
}
case WM_LBUTTONDOWN:
{
state->pushed = true;
break;
}
case WM_LBUTTONUP:
{
state->pushed = false;
break;
}
// Omitted part where I handle WM_DESTROY to do cleanup
}
return DefSubclassProc(hwnd, msg, wParam, lParam);
}
LRESULT回调BtnInstallProc(HWND HWND,UINT msg,WPARAM WPARAM,LPARAM LPARAM,UINT_PTR uIdSubClass,DWORD_PTR dwRefData)
{
按钮状态*状态=(按钮状态*)dwRefData;
//省略了我创建用于绘画的画笔和字体的部分
开关(msg)
{
案例WM_油漆:
{
PAINTSTRUCT-ps;
HDC HDC=开始喷漆(hwnd和ps);
RECT rc=ps.rcPaint;
点pt;
GetCursorPos(&pt);
屏幕客户端(硬件、硬件和pt);
BOOL hover=PtInRect(&rc,pt);
如果(状态->推送)
{
//推动
FillRect(hdc和rc、hBrPushed);
}
否则如果(悬停)
{
//滑过
FillRect(hdc和rc、hBrHover);
}
其他的
{
//正常的
FillRect(hdc和rc、hBrNormal);
}
SetBkMode(hdc,透明);
SetTextColor(hdc、RGB(255、255、255));
选择字体(hdc、SegoeUI);
静态LPCWSTR InstallBtnTxt=L“安装”;
static int InstallBtnTxtLen=static_cast(wcslen(InstallBtnTxt));//对于像这样的小数组,应该是安全的强制转换
DrawText(hdc、InstallBtnTxt、InstallBtnTxtLen和rc、DT|U单线| DT|U中心| DT|U VCENTER);
端漆(hwnd和ps);
返回0;
}
案例WM_LBUTTONDOWN:
{
状态->推送=真;
打破
}
案例WM_LBUTTONUP:
{
state->push=false;
打破
}
//省略了我处理WM_DESTROY进行清理的部分
}
返回DefSubclassProc(hwnd、msg、wParam、lParam);
}
为了比较我的按钮与标准按钮的行为,我创建了另一个按钮,没有子类化,只使用标准的按钮
类和WS_VISIBLE | WS_CHILD
属性
从附加的gif中可以看到,当我反复点击一个标准类按钮时,每次点击都会产生动画,而当多次点击我用此代码创建的按钮时,它似乎并没有捕捉到每次点击,但(如你所见)大约有50%的点击
我错过了什么?为什么我的按钮没有标准类附带的按钮那么灵敏
TL;DR:当缓慢单击我的子类按钮时,绘画是正确的。正如你可以从我的附加图像中看到的那样,我的按钮和标准按钮之间的差异在重复点击它们时是巨大的。我如何使我的控制像标准控制一样灵敏
当您快速单击时,一些
WM_LBUTTONDOWN
消息被WM_lbuttondblck
替换(默认双击处理)。您需要像处理WM_LBUTTONDOWN
消息一样处理它
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK: // <-- Added
{
state->pushed = true;
break;
}
案例WM\u LBUTTONDOWN:
案例WM_lbuttondblck://push=true;
打破
}
快速按钮可能有一个缓存的位图,它可以以闪电般的速度绘制,而不是艰难地绘制。另外,您是在测试调试版本还是优化版本?不要浪费时间快速测试优化的构建。库按钮调用将根据gills进行优化,如果您的没有优化,则可能没有公平的比较。为什么您自定义绘制按钮而不提供BS_OWNERDRAW
样式?对于不清楚,我深表歉意。我不是说位图文件。绘制一次并缓存。如果对象被调整大小或以其他方式更改,则重新绘制并缓存;否则,显示缓存。由于您不使用所有者绘制或自定义绘制,系统仍会尝试渲染动画,这会干扰您自己的绘制代码。通过使用自定义绘图(首选方法,因为您可以保留所有窗口样式标志),告诉系统您不希望这样做。当您自己绘制所有内容时,自定义绘制不受主题化的影响。使用Handle而不是WM_PAINT
。不要设置BS_OWNERDRAW
,这在使用自定义绘图时是不需要的。你让我开心了!这解决了问题,无需使用NM\u CUSTOMDRAW