Transparent controls for WinAPI applications
If you look at Windows CE applications, quite a few use a bitmap as the background. I guess this has to do with how phones need to be colorful and multi-channel noisy ;) Anyway, since the space is limited, it may make sense sometimes to put a logo on the background instead of wasting precious space.
As a first step, you'll need to add a bitmap to your projects resource files, and give it a simple name, like BACKGROUND. To use this bitmap as your forms standard background, you'll have to use the WM_CREATE message of your main message procedure:
var FHDCBis: THandle; // you'll need this again!
function WndMessageProc(hWnd: HWND; Msg: UINT; wParam: wPARAM; lParam: LPARAM): LongInt; stdcall;
// ...
var hDC, hBmp, hOldBmp: THandle;
// ...
begin
// ...
case Msg of
WM_CREATE: begin
hDC := GetDC(hwnd);
hBmp := LoadImage(hInstance, 'BACKGROUND',
IMAGE_BITMAP, 0, 0, 0);
FHDCBis := CreateCompatibleDC(hDC);
hOldBmp := SelectObject(FHDCBis, hBmp);
ReleaseDC(hwnd, hDC);
end;
This gets the device context for the form (GetDC), loads the bitmap from the files resources (LoadImage), and puts it (SelectObject) on a newly created (CreateCompatibleDC) device context.
WM_ERASEBKGND: begin
// This fills the background with
// the contents of the background image
GetClientRect(hwnd, @r);
BitBlt(wParam, r.Left, r.Top, r.Right-r.Left,
r.Bottom-r.Top, FHDCBis, 0, 0, SRCCOPY);
Result := 1;
Exit;
end;
Now that you have a background available, you need to put it on the form. To do this, we override the message used for erasing the background, WM_ERASEBKGND, by getting the dimensions of the form (or control), and copying the background into it using BitBlt. Here you can also see why FHDCBis needs to be global - since it's needed in others run of WndMessageProc than just the one that called WM_CREATE.
WM_CTLCOLORSTATIC: begin
if (lParam=FLabelHandle)
or (lParam=FGroupHandle)
or (lParam=FGroupLabelHandle)
then begin
SetBkMode(wParam, TRANSPARENT);
GetClientRect(lParam, @rClient);
GetWindowRect(lParam, @r);
MapWindowPoints(HWND_DESKTOP, hwnd, @r, 2);
BitBlt(wParam, 0, 0,
rClient.right, rClient.bottom, FHDCBis,
r.left, r.top, SRCCOPY);
hBrush := GetStockObject(NULL_BRUSH);
Result := hBrush;
Exit;
end;
end;
This handler is also needed in the message procedures of any controls that you use as a container (containing other controls inside them). SetBkMode is to make sure our nice background isn't filled before drawing, then the forms and controls rectangles are used in MapWindowPoints to get a background brush that has the right coordinates for the control (otherwise, the background of each control would be the same as the main background beginning from the top left corner). Finally, we draw this using BitBlt again and return an empty brush (see GetStockObject) so that Windows won't overpaint it.
WM_DESTROY: begin
DeleteDC(FHDCBis);
end;
Don't forget to free this device context on WM_DESTROY (DeleteDC).
// ...
end;
// ...
end;
That's it for today! There's more to it - group box captions are not properly drawn this way, and a few things may be improved for handling rotating displays, but that's left for a future blog entry, which will then also update the example file. Credits for this hint go to kidpaddle2, who posted the idea on the developpez.com forum.

0 Comments:
Post a Comment
<< Home