I'm working on a windows program whose's goal is to display image buttons using GDIPlus and the Windows header file.
The images are attached to an global CAutoPtr array. Inside the button callback, I handle the WM_PAINT message by searching the image array (imageList) using the button's identifier (GetDlgCtrlID(hWnd)).
I am able to paint with the first image in imageList, however, when I try to paint the next button with imageList[2], it does not show any image.
Where exactly is the problem and why can I not display any image besides whatever's in the first slot of imageList?
Thank you!
This handles all of the button messages.
CAutoPtr<Gdiplus::Image> typedef GdiplusImagePtr;
GdiplusImagePtr imageList[50];
Rect imagePositions[50];
LRESULT CALLBACK CustomButtonProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg) {
case WM_CREATE:
{
// Same as WM_PAINT
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC newDC = BeginPaint(hWnd, &ps);
Gdiplus::Graphics newGraphics(hWnd);
newGraphics.DrawImage(imageList[GetDlgCtrlID(hWnd)], imagePositions[GetDlgCtrlID(hWnd)]);
EndPaint(hWnd, &ps);
ReleaseDC(hWnd, GetDC(hWnd));
DeleteDC(newDC);
break;
}
return CallWindowProc(customButtonProc, hWnd, msg, wp, lp);
}
I use this line of code to attach an image to imageList. I confirmed that imageList does hold the other images; I just can not display them.
imageList[1].Attach(new Gdiplus::Bitmap(L"TestImage.png"));
CAutoPtr<Gdiplus::Image> typedef GdiplusImagePtr;
GdiplusImagePtr imageList[50];
imageList[1].Attach(new Gdiplus::Bitmap(L"TestImage.png"));
You can't use Gdiplus::Image
in one place and Gdiplus::Bitmap
in another palce.
You can't use CAutoPtr
for creating arrays in that way. Furthermore it seems you are declaring array of pointers as global values. This doesn't get freed until the end of the program. There is no point to do it this way, even if it works. Note that when the program exits the system will free all the memory. The goal should be to minimize memory usage while program is running.
Use CAutoPtr
for one object and use CAutoPtrArray
for an array. But it's easier and more efficient to handle allocation and cleanup yourself. Example:
std::vector<Gdiplus::Image*> list;
LRESULT CALLBACK MyProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM)
{
switch (msg)
{
case WM_CREATE:
list.push_back(new Gdiplus::Image(L"pic1.bmp"));
list.push_back(new Gdiplus::Image(L"pic2.bmp"));
return TRUE;
case WM_NCDESTROY:
//free memory here:
for (auto e : list) delete e;
break;
...
}
}
For drawing
case WM_PAINT:
{
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics graphics(hWnd);
graphics.DrawImage(list[0], 0, 0);
EndPaint(hWnd, &ps);
return 0;
}
The cleanup is only one line. If you have older VS versions then use
for (size_t i = 0; i < list.size(); i++)
delete list[i];
If making a custom draw button you should handle WM_DRAWITEM
in button's parent. Otherwise WM_PAINT
in subclass will not work if theme is disabled (this applies to XP/Vista/Win7).
You might also consider saving only image name, and then load/draw/unload in order to save memory.
Also note:
ReleaseDC(hWnd, GetDC(hWnd));
DeleteDC(newDC);
You should remove the above 2 lines. Please read the documentation on their usage.