Update:
I've been living in a time capsule for the past 15 years and initially posted an answer that doesn't address how to solve the issues revolving around Visual Styles (see below).
With Visual Styles enabled there is a change in behavior for the WM_DRAWITEM
message: The DRAWITEMSTRUCT
s itemAction
field no longer has the ODA_FOCUS
bit set on focus loss. The result is that the solution to remove the focus rectangle towards the bottom of this answer can no longer be applied.
To remove the focus rectangle with visual styles enabled requires rendering the control again. The following code snippet for the message handler shows how to do this:
switch ( message ) {
// ...
case WM_DRAWITEM: {
const DRAWITEMSTRUCT& dis = *(DRAWITEMSTRUCT*)lParam;
if ( dis.itemAction & ODA_DRAWENTIRE ) {
// Render the control
// ...
// If the control has the input focus...
if ( dis.itemState & ODS_FOCUS ) {
// Render the focus rectangle
DrawFocusRect( dis.hDC, &dis.rcItem );
}
}
}
// ...
}
Redrawing the entire control upon focus loss is not required. DrawFocusRect
is rendered in XOR mode and can be removed by applying the same operation a second time.
The logic to render the focus rectangle consists of two parts:
- If
itemAction
contains ODA_FOCUS
render the focus rectangle irrespective of any other state. This toggles the visibility.
- Otherwise, only render the focus rectangle, if
itemState
contains ODS_FOCUS
. This is necessary so that the initial state is properly accounted for.
The following code demonstrates this strategy.
resource.h:
#define IDD_MAINDLG 101
DlgBasedWin32.rc (Declaring a simple dialog with just an OK and Cancel button):
#include "resource.h"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_MAINDLG DIALOGEX 0, 0, 309, 176
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "OK",IDOK,"Button",BS_OWNERDRAW | WS_TABSTOP,198,155,50,14
CONTROL "Cancel",IDCANCEL,"Button",BS_OWNERDRAW | WS_TABSTOP,252,155,50,14
END
DlgBasedWin32.cpp (Creating the main dialog and message loop):
#include <windows.h>
#include "resource.h"
// Forward declarations of functions included in this code module:
INT_PTR CALLBACK DlgProc( HWND, UINT, WPARAM, LPARAM );
int APIENTRY _tWinMain( HINSTANCE hInstance,
HINSTANCE /*hPrevInstance*/,
LPTSTR /*lpCmdLine*/,
int /*nCmdShow*/)
{
HWND hDlg = CreateDialogW( hInstance, MAKEINTRESOURCEW( IDD_MAINDLG ),
NULL, DlgProc );
ShowWindow( hDlg, SW_SHOW );
UpdateWindow( hDlg );
MSG msg = { 0 };
// Main message loop:
while ( GetMessageW( &msg, NULL, 0, 0 ) )
{
if ( !IsDialogMessageW( hDlg, &msg ) ) {
TranslateMessage( &msg );
DispatchMessageW( &msg );
}
}
return (int) msg.wParam;
}
DlgBasedWin32.cpp (Dialog message handler):
// Message handler for IDD_MAINDLG
INT_PTR CALLBACK DlgProc( HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam )
{
switch ( message )
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if ( LOWORD( wParam ) == IDOK || LOWORD( wParam ) == IDCANCEL ) {
DestroyWindow( hDlg );
return (INT_PTR)TRUE;
}
break;
case WM_DESTROY:
PostQuitMessage( 0 );
return (INT_PTR)TRUE;
case WM_DRAWITEM: {
WORD wID = (WORD)wParam;
const DRAWITEMSTRUCT& dis = *(DRAWITEMSTRUCT*)lParam;
// Focus change?
if ( dis.itemAction & ODA_FOCUS ) {
// Toggle focus rectangle
DrawFocusRect( dis.hDC, &dis.rcItem );
}
else if ( dis.itemAction & ODA_DRAWENTIRE ) {
// Not a focus change -> render rectangle if requested
if ( dis.itemState & ODS_FOCUS ) {
DrawFocusRect( dis.hDC, &dis.rcItem );
}
}
return (INT_PTR)TRUE;
}
}
return (INT_PTR)FALSE;
}
The code above displays a simple dialog with just an OK and Cancel button. The buttons have the BS_OWNERDRAW
style set, and the WM_DRAWITEM
handler merely renders the focus rectangle; the buttons remain otherwise invisible. Full keyboard and mouse support is implemented through IsDialogMessage
and the default message handler, respectively.