Menu button arrow with raw Win32
• ∞
MFC has the CMFCMenuButton class to create menu buttons, but the raw Win32 API only provides split buttons (and even that only on Vista and later). To create a simple button with an arrow on the right, I only found complex solutions which involved owner-drawing the entire button by hand.
I decided to use a far simpler approach that makes use of the standard Win32 button control and is compatibile with all versions of Windows.
First, we define a subclass procedure that will handle WM_PAINT and draw the arrow
on the right of the button. Note that the example uses the ComCtl32.dll version 6
subclassing approach, but it should be trivial to modify it to target earlier versions
of Common Controls library if needed. Also note that an undocumented flag is used, which
may or may not cause issues in the future.
LRESULT CALLBACK MenuButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_PAINT:
{
LRESULT result = DefSubclassProc(hWnd, uMsg, wParam, lParam);
RECT buttonRect;
GetClientRect(hWnd, &buttonRect);
int arrowX = buttonRect.right - 18;
int arroyY = buttonRect.top + 4;
RECT arrowRect = { arrowX, arroyY, arrowX + 14, arroyY + 14 };
// Draw arrow on top of the button
HDC dc = GetDC(hWnd);
const UINT DFCS_MENUARROWDOWN = 0x0010; // Undocumented
DWORD drawFlags = DFCS_TRANSPARENT | DFCS_MENUARROWDOWN | (IsWindowEnabled(hWnd) ? 0 : DFCS_INACTIVE);
DrawFrameControl(dc, &arrowRect, DFC_MENU, drawFlags);
ReleaseDC(hWnd, dc);
return result;
}
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, MenuButtonProc, uIdSubclass);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Then, simply call the SetWindowSubclass function on the button handle(s) and
you’re good to go:
HWND button = GetHandleFromSomewhere();
SetWindowSubclass(button, MenuButtonProc, NULL, NULL);