I'm puzzled on this one. I use a program recommended on the forums to convert 32bit PNG to 32bit bitmaps with alpha channel. I add these to the resource editor and place them via the toolbox onto a Dialog. I have read (what I thought was extensively) on bitmap transparency and the limitations in Visual Studio.
What's puzzling for me is that I add the picture control via the Visual Studio resource editor to my dialog box. For example, I have two red balls, one in 24bit bitmap and one with 32bit bitmap. In the test mode of visual studio and when opening the .rc with the resource editor the transparency looks fine.
However, when I call DialogBox programmatically, I don't get transparency.
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1),
NULL, DialogProc);
When I click the test button in Visual Studio, it must be calling a routine DialogBox or similar to show the bitmap. When I place the bitmap in the resource editor to the dialog it shows transparency. What is it Microsoft does that I'm not doing?
I'm developing without MFC, intentionally. Is that the issue, only in MFC can Dialogbox's be loaded with transparency (I realize it reduces to CreateWindowEX).
I realize that various bitblt methods also can be used. Is that what Visual Studio does behind the scenes? Also reviewed material on WM_CTLCOLORSTATIC etc.
What do you all think? Is there some simple way to call DialogBox and get transparent BMPs on the dialog? Or we're all forced to use MFC? or the user has to write the routines to erase the background/paint etc.
It seems a bit odd to me that without an image with transparency, a dialogbox is easy. Need a non-square image? That's somehow a problem. Now the software engineer has to add lots of code to the Dialog Callback or program. Seems more of a bug to me.
Thank you for your review.
#include <windows.h>
#include <winuser.h>
#include "resource.h"
INT_PTR CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDOK:
EndDialog(hwnd, LOWORD(wParam));
break;
case IDCANCEL:
EndDialog(hwnd, LOWORD(wParam));
return TRUE;
}
case WM_PAINT:
break;
case WM_DESTROY:
EndDialog(hwnd, LOWORD(wParam));
break;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1),
NULL, DialogProc);
return 0;
}
There are two possible solutions to the problem which both have advantages and disadvantages.
Solution 1 fixes the original problem stated by the OP by simply adding an application manifest file to the resources. There is no coding required for this solution. The transparency achieved with that solution is not perfect, but is supported on all Windows versions since Windows XP.
Solution 2 is more advanced as it creates a layered child window that provides true transparency of the image over the dialog background aswell as any overlapping child controls. The disadvantages are that at least Windows 8 is required and a decent amount of non-trivial code must be written (but you are lucky as I already did this for you ;-)).
Solution 1 - add an application manifest
The native static control supports bitmaps with alpha transparency only if you add an application manifest that specifies common controls version 6.0.0.0. From the "old-school" look of the controls in your screenshot we can see that you don't have such a manifest yet.
Save the following snippet into a file called "manifest.xml" and put it into your application resource folder. In Visual Studio, right-click your project, go to "manifest tool" > "input and output" > "additional manifest files" > enter relative path of "manifest.xml" without quotation marks.
No further coding is required, just select the bitmap resource for the picture control (which actually is a static control) in the resource editor.
The bitmap to use should be a 32 bpp bitmap, bottom-up row order, non-premultiplied alpha. If you use PixelFormer to convert from PNG, use format A8:R8:G8:B8 (32 bpp) without selecting the other check boxes in the export dialog. If you use XnView to convert, simply save as BMP, it will use this format by default.
Result:
As we can see, we only get "fake" transparency. Any other controls underneath the image will be clipped at the boundaries of the static control.
Solution 2 - using a layered child window
True transparency is possible using a layered child window (WS_EX_LAYERED extended style). This is supported since Windows 8. It requires some coding though.
I wrapped the required code into a function
SetLayeredWindowFromBitmapResource()
which might be called from the WM_INITDIALOG handler of the dialog. The function throws any error asstd::system_error
exception, so you must add a try/catch block to handle the errors (this is shown further below in the "usage" example).Usage:
The function may be called in WM_INITDIALOG handler of your dialog box procedure, see example below. The example also shows how to handle errors.
NOTE: I'm calling MessageBoxA() here because
std::exception::what()
returns aconst char*
which is apparently a multibyte (ANSI) encoded string that contains a localized error message from the OS (with VS2015 or newer).Result:
Pitfalls:
Application must have a manifest resource specifying at least Win 8 compatibility:
The image to load must be a 32 bpp, top-down bitmap with premultiplied alpha channel.
A regular PNG can be converted into this format using PixelFormer for instance. Open image, then File > Export. Select bitmap, format A8:R8:G8:B8 (32 bpp), premultiplied alpha, top-down row order.
Building on @Zett42's answer, a simple pragma can also direct the compiler to use the 6 0 0 0 controls rather than using the manifest file.