(continued)
Click here for larger image
This article describes a technique for creating dialogs with any form you want for them.
In order to define the form of the dialog you only have to create a bitmap with your usual graphics programm (say Photoshop or Microsoft Paint,...). In this bitmap you have to paint the form of your dialog using as many colors as you want (it can be RGB or paletted!), but remembering to choose one color to be interpreted as the only transparent (or only opaque) color. It can be any color you want, but you have to know its RGB-components, because they will be one of the parameters to pass to the BitmapRegion() function, that makes all the hard work for you! The other arguments of this function are the bitmap handle, and a boolean argument which tells the function to consider the passed color as transparent or opaque! What this function returns is a handle to a region object "hRegion", which can, and should, be passed to the member function of CWnd: SetWindowRgn(hRegion), in order to complete the definition of the form of the dialog.
Here are some samples of the use of this funtion:
Sample 1
hRegion=BitmapRegion(hBitmap,RGB(0,0,0));
if(hRegion)
SetWindowRgn(hRegion,TRUE);
Sample 2:
hRegion=BitmapRegion(hBitmap,RGB(255,255,0),FALSE);
if(hRegion)
SetWindowRgn(hRegion,TRUE);
This technique provides two ways of using the mentioned function.
One way is to use it for defining the clipping region for the dialog, as explained in the previous code. This function should be called from the OnCreate() message handler of the dialog, where the bitmap has to be loaded (further explanation below!).
Click here for larger image
WITHOUT
With this option the background of the dialog has the plain color of all the dialogs in the system. That is, the dialog has a special boundary-form, but it has the same color and appearance as all standard dialogs. You can place the controls with the Ressource Editor and work with them exactly like you would do with a normal dialog. You only have to pay attention to the position of the controls, because they have to be placed on the opaque areas of the bitmap in order to be visible!
The other way to use this technique is to get a completely owner drawn dialog. Not only the boundaries of the dialog are computed from the bitmap, but also the background image of the dialog! These option needs a bit more code to get done, but it isn't complicated and the effect is really cool (you can use this for the About... dialog of your application!)
Click here for larger image
WITH
With this option the background is filled with the bitmap. The controls (if you put some on the dialog) are drawn over the bitmap, so if you want them not to appear, put them on transparent areas of the dialog, or simply put them away! In the sample project, I use this option with a dialog in which I let no control be shown (in fact there remains only an "Ok" button!) and therefor I have to be able to close the dialog in another manner. The solution is to catch the clicking of the user over a special area of the bitmap. I painted a yellow cross on the bitmap and annotated the coordinates in pixels of the bounding rectangle of the cross. When the user clicks within this area, the dialog is closed with EndDialog(). In order to get the coordinates of the pointer in comparison with those of the mentiones area, the dialog has to have no caption- or title-bar, and it has to have no border! (study the sample project for a closer explanation.)
Another feature implemented in this sample project for the dialog with the bitmap painted on the background, is that the user can dragg the dialog clicking anywhere on it (except on the yellow-cross which closes the dialog!). In order to achieve this effect, You have to override the OnNCHitText() message handler of the dialog and return HTCAPTION everytime the user doesn't click on the yellow-cross. In this case you return HTCLIENT, in order to let the system send the WM_LBUTTONDOWN message to the dialog!
In either case (you want your bitmap to be painted on the background or not) you have to insert the following line in the .cpp file of your dialog implementation:
#include "AnyForm.h"
You have to include the files AnyForm.h and Anyorm.cpp in your project, in order to get things working!
Explanation 1: The bitmap is used only to compute the clipping region of the dialog
This option is shown in the previous photo called "Without".
In this case the code is rather simple. You have to override the OnCreate() message handler of the dialog, and insert a few lines of code, in order to obtain something like this:
int CMyDlgWithout::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
HBITMAP hBmp;
HRGN hRegion;
char *name="BitmapWithout.bmp";
hBmp=(HBITMAP)LoadImage(lpCreateStruct->hInstance,
name,
IMAGE_BITMAP,
0,
0,
LR_LOADFROMFILE
| LR_CREATEDIBSECTION);
name,
IMAGE_BITMAP,
0,
0,
LR_LOADFROMFILE
| LR_CREATEDIBSECTION);
hRegion=BitmapRegion(hBmp,RGB(0,0,0));
if(hRegion)
SetWindowRgn(hRegion,TRUE);
if(hBmp)
DeleteObject(hBmp);
return 0;
}
Explanation 2: The bitmap is used to compute the clipping region of the dialog, and to be painted on the background of the dialog
This option is shown in the previous photo called "With".
This case is a little more complicated, but it can be better understood through the code of the sample project. At first, you have to insert a few member variables in the dialog-class that controls your dialog. The class definition would look similar to this:
class CMyDlgWith : public CDialog
{
...
protected:
HBITMAP hBmp;
HBITMAP hPrevBmp;
HDC hMemDC;
HRGN hRegion;
BITMAP bmInfo;
...
};
You have to override the message handlers for the following messages: WM_CREATE, WM_DESTROY and WM_ERASEBKGND. The code for these functions would look like this:
int CMyDlgWith::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
char *name="BitmapWith.bmp";
hBmp=(HBITMAP)LoadImage(lpCreateStruct->hInstance,
name,
IMAGE_BITMAP,
0,
0,
LR_LOADFROMFILE
| LR_CREATEDIBSECTION);
name,
IMAGE_BITMAP,
0,
0,
LR_LOADFROMFILE
| LR_CREATEDIBSECTION);
hRegion=BitmapRegion(hBmp,RGB(0,0,0));
if(hRegion)
SetWindowRgn(hRegion,TRUE);
GetObject(hBmp,sizeof(bmInfo),&bmInfo);
hMemDC=CreateCompatibleDC(NULL);
hPrevBmp=(HBITMAP)SelectObject(hMemDC,hBmp);
return 0;
}
void CMyDlgWith::OnDestroy()
{
CDialog::OnDestroy();
SelectObject(hMemDC,hPrevBmp);
if(hBmp)
DeleteObject(hBmp);
}
BOOL CMyDlgWith::OnEraseBkgnd(CDC* pDC)
{
BitBlt(pDC->m_hDC,
0, 0,
bmInfo.bmWidth,
bmInfo.bmHeight,
hMemDC,
0, 0,
SRCCOPY);
return FALSE;
}
The last thing to do, is to provide the dialog a way to get closed by the user. As I mentioned before, the best thing is to draw a special figure anywhere on the bitmap and to catch the clicking of the mouse in this area, by catching the WM_LBUTTONDOWN message of the dialog. Another feature can be easily implemented: permit 'click and drag' the dialog clicking anywhere on it! This can be achieved by catching the WM_NCHITTEST message. The following code shows how this is implemented:
void CMyDlgWith::OnLButtonDown(UINT nFlags, CPoint point)
{
if(point.x>333 && point.x<354 && point.y>54 && point.y<77)
EndDialog(0);
CDialog::OnLButtonDown(nFlags, point);
}
UINT CMyDlgWith::OnNcHitTest(CPoint point)
{
ScreenToClient(&point);
if(point.x>333 && point.x<354 && point.y>54 && point.y<77)
return HTCLIENT;
else
return HTCAPTION;
}
Demo project
The demo project shows the two ways of using this technique. The source code has comments in order to get better explained.
Source code
The source code comprises only the two following files, that you have to include in your project: AnyForm.h and AnyForm.cpp.
Downloads
Download source - 4 Kb
Download demo project - 115 Kb