Search code examples
c++winapigdidev-c++

ellipse as checked icon in a menu


I'm creating a context menu for a window and I'd like to have in a submenu filled ellipses, each in a different colour, instead of texts - this submenu is responsible for choosing a colour. I don't know how to do it... Does anybody know any concrete example? I've read on MSDN pages about owner-drawn menu items, but there were no example concerning this specific task - so, I don't know how to do it. Later I tried to change checked icon for my menu item - but it turned out my Dev-C++ (under Windows 7) knows neither SetDCBrushColor nor DC_BRUSH, and I still don't know how to change checked icon without loading an image from a file. Then I added at the beginning of my program the following lines:

#define _WIN32_IE 0x0600
#define WINVER  0x0700
#define _WIN32_WINNT 0x0700

Then the compiler doesn't protest, however, the icon is always black, when I'm trying the following code and whatever colour I'm choosing:

HWND hwnd = GetDesktopWindow();
HDC hdc = GetDC( hwnd );
HDC hdcMem = CreateCompatibleDC( hdc );
SIZE size = { GetSystemMetrics( SM_CXMENUCHECK ), GetSystemMetrics( SM_CYMENUCHECK ) };
HBITMAP bitmap = CreateCompatibleBitmap( hdcMem, size.cx, size.cy );
HBITMAP bitmapOld = (HBITMAP) SelectObject( hdcMem, bitmap );
PatBlt( hdcMem, 0, 0, size.cx, size.cy, WHITENESS );
HBRUSH brushOld = (HBRUSH) SelectObject( hdcMem, GetStockObject( NULL_BRUSH ) );
Ellipse( hdcMem, 0, 0, size.cx, size.cy);
SetDCBrushColor( hdcMem, RGB(0,0,255) );
SelectObject( hdcMem, GetStockObject( DC_BRUSH ) );
Ellipse( hdcMem, 2, 2, size.cx-2, size.cy-2 );
SelectObject( hdcMem, brushOld );
SelectObject( hdcMem, bitmapOld );
DeleteDC( hdcMem );
ReleaseDC( hwnd, hdc );
SetMenuItemBitmaps( mnu_t, (30*M_MENU_T+25), MF_BYCOMMAND, bitmap, bitmap);// mnu_t and M_MENU_T are my variables

Can anyone help me?


Solution

  • The problem is here:

    HBITMAP bitmap = CreateCompatibleBitmap( hdcMem, size.cx, size.cy );
    

    This is a perfectly natural and rational statement to write, but it doesn't do what most people expect it to do. It's a common mistake and it can take forever to figure out that the source of the problem is here. I know this from making this very mistake more than once myself.

    CreateCompatibleBitmap does not create a bitmap that's compatible with the specified device context (DC). Well, it does--sort of--but it's actually more nuanced that: It creates a new bitmap that's compatible with the bitmap that's selected into the specified DC.

    When creating a memory DC with CreateCompatibleDC, the new DC gets a default bitmap that is 1 pixel wide by 1 pixel high and with a color depth of 1 bit. This is not intuitive at all, since you asked for the DC to be compatible with the screen DC, and the screen (almost certainly) has more than 1 bit of color depth.

    So when you call CreateCompatibleBitmap you get a new bitmap of the specified size that uses 1 bit per pixel. You can draw to it, and, if the stars align, you'll see a crude outline of what you drew. But if the stars aren't aligned, you'll end up with all the pixels being a single color.

    When you blit a 1-bit per pixel bitmap to another DC, the current text foreground and background colors are used. Since the foreground text color is often black, you end up with a black rectangle and virtually no clue as to where you went wrong.

    The solution is to specify the screen or window DC instead of the memory DC in the call the CreateCompatibleBitmap. This will create a new bitmap that has the same color depth as the bitmap used by the screen, which is far more useful.