已启用
残障人士

当我禁用按钮(使用BS_BITMAP样式标志创建)时,它会改变外观(请参见上图),编辑控件也会发生同样的情况。
禁用后如何使控件不变?
我可以通过对控件进行子类化来实现,但是有没有更简单的方法?
如果可能的话,我不想仅仅为此子类化控件。
您无需为此子类化控件即可,尽管我会说它会更干净。设置BS_OWNERDRAW样式和处理WM_DRAWITEM消息的替代方法。这意味着您要接管所有图形,但这没关系,因为您始终不希望它看起来像普通按钮。
我完全同意Jonathan Potter的观点,即无法向用户指示哪些按钮已启用,哪些按钮未启用,这是非常糟糕的UI设计。有多种方法可以做到这一点,但不这样做是不可行的选择。幸运的是,使用容易WM_DRAWITEM,因为它告诉您按钮的当前状态。
因此,使WM_DRAWITEM消息处理程序看起来像这样(在父窗口的窗口过程中):
case WM_DRAWITEM:
{
const DRAWITEMSTRUCT* pDIS = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
// See if this is the button we want to paint.
// You can either check the control ID, like I've done here,
// or check against the window handle (pDIS->hwndItem).
if (pDIS->CtlID == 1)
{
// Load the bitmap.
const HBITMAP hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
// Draw the bitmap to the button.
bool isEnabled = (pDIS->itemState & ODS_DISABLED) == 0;
DrawState(pDIS->hDC,
nullptr,
nullptr,
reinterpret_cast<LPARAM>(hBmp),
0, 0, 0, 0, 0,
DST_BITMAP | (isEnabled ? DSS_NORMAL : DSS_DISABLED));
// Delete the bitmap.
DeleteObject(hBmp);
// Draw the focus rectangle, if applicable.
if ((pDIS->itemState & ODS_FOCUS) && ((pDIS->itemState & ODS_NOFOCUSRECT) == 0))
{
DrawFocusRect(pDIS->hDC, &pDIS->rcItem);
}
// Indicate that we handled this message.
return TRUE;
}
break;
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以通过一次加载位图并将其缓存在全局对象中,而不是每次按钮需要绘制时都加载并销毁它,来进一步优化此代码。
请注意,我已经使用了DrawState函数,该函数可以在“正常”(DSS_NORMAL)或“禁用”(DSS_DISABLED)状态下绘制位图。这大大简化了代码,并使我们能够轻松地处理禁用状态,但不幸的是,结果看起来有点难看。这是因为该DrawState函数在应用除普通效果之外的任何其他效果之前,会将位图转换为单色。
您可能不喜欢这种效果,因此您需要做其他事情。例如,使用两个单独的图像,一个用于启用状态,另一个用于禁用状态,然后绘制适当的图像。或将正常的彩色图像转换为灰度,然后将其绘制为禁用状态。
而且,如果自定义绘图代码运行得太慢,则可以通过检查的值pDIS->itemAction并仅重新绘制必要的部分来进一步优化它。
然后,一旦您认为所有内容都经过了完善和有效的处理,不可避免的错误报告就会开始出现。例如,不支持键盘加速器。然后,一旦添加了对这些功能的支持,就需要在用户界面中进行说明。对于已经包含文本的位图,这将是困难的;带下划线的字母的唯一方法是自己绘制文本。这一切都证明所有者抽取是太多的工作。只是让Windows以正常方式绘制控件,不要仅仅因为某些设计师认为它“看起来很酷”就为您的用户破坏了一切。