Click to See Complete Forum and Search --> : How do you set an image for an owner drawn button?


YourSurrogateGod
June 13th, 2004, 09:40 AM
I want to use the BS_BITMAP style in the creation of my button in my program. But personally I have no idea where to even begin, does anyone have any tutorials, examples or suggestions?

Here is my window procedure and another function.

#define ID_SMALLER 1
#define ID_LARGER 2

#define BTN_WIDTH (8 * cxChar)
#define BTN_HEIGHT (4 * cyChar)
........................
void Triangle(HDC hdc, POINT pt[])
{
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
Polygon(hdc, pt, 3);
SelectObject(hdc, GetStockObject(WHITE_BRUSH));
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hwndSmaller;
static HWND hwndLarger;
static int cxClient;
static int cyClient;
static int cxChar;
static int cyChar;
int cx;
int cy;
LPDRAWITEMSTRUCT pdis;
POINT pt[3];
RECT rc;

switch(message)
{
case WM_CREATE:
cxChar = LOWORD(GetDialogBaseUnits());
cyChar = HIWORD(GetDialogBaseUnits());

hwndSmaller = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_BITMAP,
0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_SMALLER,
((LPCREATESTRUCT) lParam) -> hInstance, NULL);

hwndLarger = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_BITMAP,
0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_LARGER,
((LPCREATESTRUCT) lParam) -> hInstance, NULL);

return 0;

case BM_SETIMAGE:

return 0;

case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);

MoveWindow(hwndSmaller, cxClient / 2 - 3 * BTN_WIDTH / 2, cyClient / 2 - BTN_HEIGHT / 2,
BTN_WIDTH, BTN_HEIGHT, TRUE);

MoveWindow(hwndLarger, cxClient / 2 + BTN_WIDTH / 2, cyClient / 2 - BTN_HEIGHT / 2,
BTN_WIDTH, BTN_HEIGHT, TRUE);

return 0;

case WM_COMMAND:
GetWindowRect(hwnd, &rc);

switch(wParam)
{
case ID_SMALLER:
rc.left += cxClient / 20;
rc.right -= cxClient / 20;
rc.top += cyClient / 20;
rc.bottom -= cyClient / 20;

break;

case ID_LARGER:
rc.left -= cxClient / 20;
rc.right += cxClient / 20;
rc.top -= cyClient / 20;
rc.bottom += cyClient / 20;

break;
}

MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);

return 0;

case WM_DRAWITEM:
pdis = (LPDRAWITEMSTRUCT)lParam;

FillRect(pdis -> hDC, &pdis -> rcItem, (HBRUSH)GetStockObject(WHITE_BRUSH));

FrameRect(pdis -> hDC, &pdis -> rcItem, (HBRUSH)GetStockObject(BLACK_BRUSH));

cx = pdis -> rcItem.right - pdis -> rcItem.left;
cy = pdis -> rcItem.bottom - pdis -> rcItem.top;

switch(pdis -> CtlID)
{
case ID_SMALLER:
pt[0].x = 3 * cx / 8;
pt[0].y = 1 * cy / 8;
pt[1].x = 5 * cx / 8;
pt[1].y = 1 * cy / 8;
pt[2].x = 4 * cx / 8;
pt[2].y = 3 * cy / 8;

Triangle(pdis -> hDC, pt);

pt[0].x = 7 * cx / 8;
pt[0].y = 3 * cy / 8;
pt[1].x = 7 * cx / 8;
pt[1].y = 5 * cy / 8;
pt[2].x = 5 * cx / 8;
pt[2].y = 4 * cy / 8;

Triangle(pdis -> hDC, pt);

pt[0].x = 5 * cx / 8;
pt[0].y = 7 * cy / 8;
pt[1].x = 3 * cx / 8;
pt[1].y = 7 * cy / 8;
pt[2].x = 4 * cx / 8;
pt[2].y = 5 * cy / 8;

Triangle(pdis -> hDC, pt);

pt[0].x = 1 * cx / 8;
pt[0].y = 5 * cy / 8;
pt[1].x = 1 * cx / 8;
pt[1].y = 3 * cy / 8;
pt[2].x = 3 * cx / 8;
pt[2].y = 4 * cy / 8;

Triangle(pdis -> hDC, pt);

break;

case ID_LARGER:
pt[0].x = 5 * cx / 8;
pt[0].y = 3 * cy / 8;
pt[1].x = 3 * cx / 8;
pt[1].y = 3 * cy / 8;
pt[2].x = 4 * cx / 8;
pt[2].y = 1 * cy / 8;

Triangle(pdis -> hDC, pt);

pt[0].x = 5 * cx / 8;
pt[0].y = 5 * cy / 8;
pt[1].x = 5 * cx / 8;
pt[1].y = 3 * cy / 8;
pt[2].x = 7 * cx / 8;
pt[2].y = 4 * cy / 8;

Triangle(pdis -> hDC, pt);

pt[0].x = 3 * cx / 8;
pt[0].y = 5 * cy / 8;
pt[1].x = 5 * cx / 8;
pt[1].y = 5 * cy / 8;
pt[2].x = 4 * cx / 8;
pt[2].y = 7 * cy / 8;

Triangle(pdis -> hDC, pt);

pt[0].x = 3 * cx / 8;
pt[0].y = 3 * cy / 8;
pt[1].x = 3 * cx / 8;
pt[1].y = 5 * cy / 8;
pt[2].x = 1 * cx / 8;
pt[2].y = 4 * cy / 8;

Triangle(pdis -> hDC, pt);

break;
}

if(pdis -> itemState & ODS_SELECTED)
{
InvertRect(pdis -> hDC, &pdis -> rcItem);
}

if(pdis -> itemState & ODS_FOCUS)
{
pdis -> rcItem.left += cx / 16;
pdis -> rcItem.top += cy / 16;
pdis -> rcItem.right -= cx / 16;
pdis -> rcItem.bottom -= cy / 16;

DrawFocusRect(pdis -> hDC, &pdis -> rcItem);
}

return 0;

case WM_DESTROY:
PostQuitMessage(0);

return 0;
}

return DefWindowProc(hwnd, message, wParam, lParam);
}

Thanks in advance.

BorisKK
June 14th, 2004, 06:12 AM
A button with style BS_BITMAP is not owner-drawn. All you need to do in order to display a bitmap in it is:
[list=1]
Obtain a bitmap handle, for example by loading a bitmap from application resources.
Call SendMessage(hButton, BM_SETIMAGE, (WPARAM) IMAGE_BITMAP, (LPARAM) hBitmap) or the equivalent SendDlgItemMessage(...).
[/list=1]
The button will take care of redrawing itself.

YourSurrogateGod
June 14th, 2004, 11:13 AM
Originally posted by BorisKK
A button with style BS_BITMAP is not owner-drawn.

That I know all too well. The reason why I put up the code that I did, was because I was hoping that I'd make it clear what the program was supposed to do, so as to avoid as much confusion as possible. I was hoping that someone would show me an example on how to accomplish the 2 steps that you listed above. Thanks for the tip Boris.

kirants
June 14th, 2004, 01:03 PM
You could do the 2 steps after creating the 2 buttons in your WM_CREATE handler. Check the hWnds to make sure the buttons are created though.

YourSurrogateGod
June 14th, 2004, 02:18 PM
Originally posted by kirants
You could do the 2 steps after creating the 2 buttons in your WM_CREATE handler. Check the hWnds to make sure the buttons are created though.

The 2 buttons are created, I know, I'm certain. I compiled the above program and when I executed it, I saw 2 large gray squares in the middle of the client-area, so I know that the buttons are fine. The problem is that I don't know where to proceed from there.

kirants
June 14th, 2004, 02:22 PM
did u try what Boris suggested in the WM_CREATE handler ?


case WM_CREATE:
cxChar = LOWORD(GetDialogBaseUnits());
cyChar = HIWORD(GetDialogBaseUnits());

hwndSmaller = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_BITMAP,
0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_SMALLER,
((LPCREATESTRUCT) lParam) -> hInstance, NULL);

hwndLarger = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_BITMAP,
0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_LARGER,
((LPCREATESTRUCT) lParam) -> hInstance, NULL);

//Now set the bitmaps
hBitmap = LoadImage... ..
SendMessage(hwndSmaller, BM_SETIMAGE, (WPARAM) IMAGE_BITMAP, (LPARAM) hBitmap)
return 0;

YourSurrogateGod
June 14th, 2004, 02:53 PM
Well this is what I have at the moment...

static HWND hBitmap1;
static HWND hBitmap2;
.................
switch(message)
{
case WM_CREATE:
cxChar = LOWORD(GetDialogBaseUnits());
cyChar = HIWORD(GetDialogBaseUnits());

hwndSmaller = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_BITMAP,
0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_SMALLER,
((LPCREATESTRUCT) lParam) -> hInstance, NULL);

hwndLarger = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_BITMAP,
0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_LARGER,
((LPCREATESTRUCT) lParam) -> hInstance, NULL);

hBitmap1 = LoadImage(((LPCREATESTRUCT) lParam) -> hInstance, TEXT("bitmap1"),
IMAGE_BITMAP, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_DEFAULTCOLOR);
hBitmap2 = LoadImage(((LPCREATESTRUCT) lParam) -> hInstance, TEXT("bitmap2"),
IMAGE_BITMAP, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_DEFAULTCOLOR);

SendMessage(hwndSmaller, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap1);
SendMessage(hwndLarger, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap2);

return 0;

... the 2 images are resources in the program. Except for the above changes in the processing of the WM_CREATE message and the 2 static handles to 2 bitmaps, the program is exactly the same. I'll get rid of the other portions of the program that deal with the drawing of the triangles later once the 2 pictures show up. What am I doing wrong?

kirants
June 14th, 2004, 03:05 PM
Works well for me. Only difference was, I used a MAKEINTRESOURCE(id of bitmap) for the second param to LoadImage.

Check the hBitmap and see if you are receiving a NULL after LoadImage. Probably , that's the culprit.

YourSurrogateGod
June 14th, 2004, 03:47 PM
Well, you're right, the handle to the bitmap says "unused", so pretty much it's empty. Here's what I did differently...

switch(message)
{
case WM_CREATE:
cxChar = LOWORD(GetDialogBaseUnits());
cyChar = HIWORD(GetDialogBaseUnits());

hwndSmaller = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_SMALLER,
((LPCREATESTRUCT) lParam) -> hInstance, NULL);

hwndLarger = CreateWindow(TEXT("button"), TEXT(""), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
0, 0, BTN_WIDTH, BTN_HEIGHT, hwnd, (HMENU)ID_LARGER,
((LPCREATESTRUCT) lParam) -> hInstance, NULL);

hBitmap1 = LoadImage(((LPCREATESTRUCT) lParam) -> hInstance,
MAKEINTRESOURCE(BITMAP2_SMALL), IMAGE_BITMAP, LR_DEFAULTSIZE, LR_DEFAULTSIZE,
LR_DEFAULTCOLOR);
hBitmap2 = LoadImage(((LPCREATESTRUCT) lParam) -> hInstance,
MAKEINTRESOURCE(BITMAP2_LARGE), IMAGE_BITMAP, LR_DEFAULTSIZE, LR_DEFAULTSIZE,
LR_DEFAULTCOLOR);

SendMessage(hwndSmaller, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap1);
SendMessage(hwndLarger, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap2);

return 0;

However, I get an error pointing to the LoadImage function where it saying that BITMAP2_SMALL and BITMAP2_LARGE are not recognized. Darn, I know there is a way to get around this problem, I just forgot how to :( .

kirants
June 14th, 2004, 03:49 PM
Those 2 should be defined in some header file. Include that header file to compile it.

YourSurrogateGod
June 14th, 2004, 03:57 PM
D'oh. That's the one. Thanks kirants, it works now :wave: .