Create personalized dialog box of MFC Article http://tech.163.com/10:50:36 Source: csdn user comments 0 Forum
If you want to make your software stand out, you need to add a "color" to the software. A color-matching window is more eye-catching than a windows-like dark box. Imagine if all the web pages displayed in the html browser are white and black characters, will there be so many MMS who prefer to access the Internet? The popularity of the Internet may be halved. It may be difficult for a veteran to make a good interface, but it is a good choice for beginners to use BCGControlBar and Xtreme Toolkit, however, it seems that a small program uses such a large library. In fact, you can make a very "color" interface without using these giants. This article introduces several methods to color the Dialog Box Based on the frequently asked questions on the CSDN forum. The methods in this article are for the MFC program. For other methods, see "Create a personalized dialog box for ATL/WTL ".
Step 1: change the background color of the dialog box
The question of how to change the background color of the dialog box often appears on the Forum. It can be seen that you are not satisfied with the default gray dialog box in Windows. The simplest way to modify the background and text color of the dialog box of the MFC program is to call the SetDialogBkColor function. SetDialogBkColor is a member function of the CWinApp class. The following is the prototype of the function:
Void CWinApp: SetDialogBkColor (COLORREF clrCtlBk, COLORREF clrCtlText );
Note that the SetDialogBkColor function is not an encapsulation of an API of Windows. It is part of the MFC framework, so it is not convenient to use a program without using MFC. This function is easy to use. Just add a line of code to the InitInstance function of the CWinApp derived class:
SetDialogBkColor (RGB (188,197,230), RGB (13,125,188 ));
Figure. 1 shows the running effect:
Figure. 1 SetDialogBkColor
The use of SetDialogBkColor is also limited, that is, all controls have the same text color. Different text colors cannot be set for different controls, or the Edit control color cannot be set. Without using the SetDialogBkColor function, it is not difficult to directly write the background color of the code control dialog box and the text color of the control. In addition, this method can provide more flexible color settings, for example, you can use different text colors for different types of controls and highlight a widget with a high-brightness background color. The most important thing is that you can control the text and background colors of the Edit Control, this method is described below.
First, change the background color of the dialog box. When the Windows system needs to redraw the background of a window's client area, it will send the WM_ERASEBKGND message to the window. The window's processing process responds to the message and re-draws the background of the window, this process is called "Self-painting ". The principle of changing the background color of the dialog box is very simple, that is, to respond to this message, fill the background of the dialog box with a custom color, instead of the default background filling action in the dialog box window. Many new users often ask: "Why can't the WM_ERASEBKGND message in the dialog box be found in class wizard? Is this message unavailable in the dialog box "? In fact, the dialog box is also a window, and it also contains the WM_ERASEBKGND message, but it is filtered out by the dialog filter used by the MFC class wizard (only in the display of the message window, does not really do not respond to this message), in order to highlight the exclusive message and control events in the dialog box during code writing .. As shown in figure 2, if you change the message filter to Windows under the "class info" table label in the class wizard, You can see WM_ERASEBKGND in the message list of the dialog box.
Figure 2 modify a message Filter
Now you can use class wizard to add the message response function of WM_ERASEBKGND and modify this function as follows:
BOOL CCustDlgDlg: OnEraseBkgnd (CDC * pDC)
{
CRect rcClient;
GetClientRect (& rcClient );
PDC-> FillRect (& rcClient, & m_brBkgnd );
Return TRUE;
// Return CDialog: OnEraseBkgnd (pDC );
}
M_brBkgnd is a CBrush, which has been initialized before. The key code is to return TRUE at last, instead of calling the base class function by default. Returning TRUE is intended to tell Windows: "I have already painted the background. You should not draw any more ". Now let's take a look at the running effect:
Figure 3 Effect of repainting the background
It is not difficult to use bitmap as the background of the dialog box, that is, to draw a bitmap background in the entire customer area,
Step 2: change the color of the widget
The color and background color of the control text are not changed because we have not processed the WM_CTLCOLOR message. WM_CTLCOLOR is one of the most frequent notification messages sent by Windows controls to its parent window. For example, many controls send WM_CTLCOLOR messages to the parent window, allowing the parent window to provide a painting brush to draw their own background. The window class of MFC treats the notification message specially. If the parent window does not process the notification message, the window class of MFC sends the WM_CTLCOLOR message back to the control based on the source of the WM_CTLCOLOR notification message, let the control handle it by yourself. This is the so-called "Message reflection". Not only is WM_CTLCOLOR, but MFC does reflection on many notification messages. However, in today's example, "Message reflection" is not used ", we can process the notification message in the control's parent window, that is, the dialog box window. Note that the WM_CTLCOLOR message is a 16-bit Windows platform message, which is replaced by a series of more specific notification messages on a 32-bit Windows platform:
WM_CTLCOLORBTN button control
WM_CTLCOLORDLG dialog box
WM_CTLCOLOREDIT Edit Control
WM_CTLCOLORLISTBOX list box Control
WM_CTLCOLORSCROLLBAR scroll bar Control
WM_CTLCOLORSTATIC text Control
For compatibility considerations, MFC still uses onctlcolor to respond to these messages, but uses the nctlcolor parameter to differentiate them. In this function, we can change the drawing of the control by changing the attribute of the PDC parameter, and return the corresponding paint brush handle to the control. The control uses this paint brush to draw its own background. Here is the modified onctlcolor function:
Hbrush ccustdlgdlg: onctlcolor (CDC * PDC, cwnd * pwnd, uint nctlcolor)
{
HBRUSH hbr = CDialog: OnCtlColor (pDC, pWnd, nCtlColor );
PDC-> SetTextColor (m_clrText );
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brBkgnd; // because the CBrush class implements the HBRUSH type conversion Operator
// Return hbr;
}
Figure. 4 is the effect of this Code. Here we will return our own paint brush to all the controls, which looks good and the text color of the Edit Control is changed, however, it seems that the multi-row edit control is troublesome. It seems that special treatment is needed for the multi-row edit control.
Figure 4 effect after onctlcolor is reloaded
For special processing of the multi-row edit control, the preceding problem is solved as follows:
Hbrush ccustdlgdlg: onctlcolor (CDC * PDC, cwnd * pwnd, uint nctlcolor)
{
HBRUSH hbr = CDialog: OnCtlColor (pDC, pWnd, nCtlColor );
If (pwnd-> getdlgctrlid () = idc_edit_multi_line) // idc_edit_multi_line is the ID of the multi-row edir control.
{
PDC-> settextcolor (m_clrtext );
Return HBr;
}
Else
{
PDC-> settextcolor (m_clrtext );
PDC-> setbkmode (transparent );
Return (hbrush) m_brbkgnd;
}
}
The above code solves the idc_edit_multi_line problem, but the ID must be determined for each multi-line edit control. The following method can solve the problem of multi-line editing controls once and for all:
Hbrush ccustdlgdlg: onctlcolor (CDC * PDC, cwnd * pwnd, uint nctlcolor)
{
Hbrush HBr = cdialog: onctlcolor (PDC, pwnd, nctlcolor );
Tchar szclassname [64];
: Getclassname (pwnd-> getsafehwnd (), szclassname, 64 );
If (lstrcmpi (szclassname, _ T ("edit") = 0) // is the Edit Control
{
DWORD dwstyle = pwnd-> getstyle ();
If (dwstyle & es_multiline) = es_multiline) // multi-row Edit Control
{
PDC-> settextcolor (m_clrtext );
Return HBr;
}
Else
{
PDC-> settextcolor (m_clrtext );
PDC-> setbkmode (transparent );
Return (hbrush) m_brbkgnd;
}
}
Else // not an editing Control
{
PDC-> settextcolor (m_clrtext );
PDC-> setbkmode (transparent );
Return (hbrush) m_brbkgnd;
}
}
Next we will set a special color for each control. To distinguish between controls, we can use the control ID to modify the control background. It is also very easy to directly return the corresponding paint brush, the complete color settings code is as follows:
Hbrush ccustdlgdlg: onctlcolor (CDC * PDC, cwnd * pwnd, uint nctlcolor)
{
Hbrush HBr = cdialog: onctlcolor (PDC, pwnd, nctlcolor );
Tchar szclassname [64];
: Getclassname (pwnd-> getsafehwnd (), szclassname, 64 );
If (lstrcmpi (szclassname, _ T ("edit") = 0) // is the Edit Control
{
DWORD dwstyle = pwnd-> getstyle ();
If (dwstyle & es_multiline) = es_multiline) // multi-row Edit Control
{
PDC-> settextcolor (m_clrtext );
Return hbr;
}
Else
{
PDC-> SetTextColor (m_clrText );
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brBkgnd;
}
}
Else // not an editing Control
{
If (pWnd-> GetDlgCtrlID () = IDC_STC_REDTEXT)
{
PDC-> SetTextColor (RGB (255, 0, 0 ));
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brBkgnd;
}
Else if (pWnd-> GetDlgCtrlID () = IDC_STC_BLUETEXT)
{
PDC-> SetTextColor (RGB (0, 0, 255 ));
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brBkgnd;
}
Else if (pWnd-> GetDlgCtrlID () = IDC_STC_BLUETEXTWHITEBACK)
{
PDC-> SetTextColor (RGB (0, 0, 255 ));
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brControlBkgnd1;
}
Else if (pWnd-> GetDlgCtrlID () = IDC_CHK_GREEN)
{
PDC-> SetTextColor (RGB (0,255, 0 ));
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brBkgnd;
}
Else if (pWnd-> GetDlgCtrlID () = IDC_RAD_BLUE)
{
PDC-> SetTextColor (RGB (0, 0, 255 ));
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brBkgnd;
}
Else if (pWnd-> GetDlgCtrlID () = IDC_CHK_GREEN2)
{
PDC-> SetTextColor (RGB (0,255, 0 ));
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brControlBkgnd2;
}
Else if (pWnd-> GetDlgCtrlID () = IDC_RADIO2)
{
PDC-> SetTextColor (RGB (0, 0, 255 ));
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brControlBkgnd2;
}
Else
{
PDC-> SetTextColor (m_clrText );
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_brBkgnd;
}
}
}
Now let's take a look at the effect:
Figure 5 effect after onctlcolor is modified
The above Code sets the color based on the control ID. You can also set the color of a control based on the control type. The nctlcolor parameter is required, the nctlcolor parameter specifies the type of the control that sends the notification message. The nctlcolor can be of the following values:
Ctlcolor_btn
Ctlcolor_dlg
Ctlcolor_edit
Ctlcolor_listbox
Ctlcolor_msgbox
Ctlcolor_scrollbar
Ctlcolor_static
Step 3: Use bitmap as the background of the dialog box
It is also very easy to use bitmap as the background of the dialog box, that is, to fill the customer area with bitmap in onerasebkgnd, but in onctlcolor, you must note that the returned blank image brush replaces the original image brush, an empty image brush is returned to prevent the control from drawing its own background, which destroys the integrity of the bitmap background. However, sometimes returning an empty image brush will have adverse effects on other controls, therefore, we only process messages of the ctlcolor_btn and ctlcolor_static types:
Hbrush cbmpbkgnddlg: onctlcolor (CDC * PDC, cwnd * pwnd, uint nctlcolor)
{
HBRUSH hbr = CDialog: OnCtlColor (pDC, pWnd, nCtlColor );
If (nCtlColor = CTLCOLOR_BTN | nCtlColor = CTLCOLOR_STATIC)
{
PDC-> SetTextColor (RGB (0, 0, 255 ));
PDC-> SetBkMode (TRANSPARENT );
Return (HBRUSH) m_HollowBrush;
}
PDC-> SetTextColor (RGB (0, 0, 255 ));
PDC-> SetBkMode (TRANSPARENT );
Return hbr;
}
The following figure shows the effects of Bitmap backgrounds and empty image Brushes:
Figure 6 effect of using a bitmap background
Step 4: Process button controls separately
Now it seems that the button control still affects the overall effect. WM_CTLCOLORBTN seems to have no effect on the push button control, but the push button also supports self-painting. before using the self-painting button, let's take a look at the Principle of Self-painting controls. Windows controls have a default appearance, but many controls support "self-painting", that is, to allow users to customize the appearance of the control. After a custom style is specified for a control, when re-painting, the control sends the WM_MEASUREITEM and WM_DRAWITEM messages to the parent window. The parent window responds to the two messages, locates the control size, and draws the control to make the control have a custom appearance. However, the self-painting of each control is completed by the parent window, which increases the burden on the parent window and is not conducive to code reuse. Therefore, MFC performs reflection processing on these messages, that is, sending the message back control, the self-drawn code is encapsulated in the control class, which improves the reusability of the Code. Many control classes of MFC process these two messages by themselves. The Derived classes can overload MeasureItem and DrawItem to draw their own controls. CButton is such a control class.
Now we will create a self-drawn button class. First we will derive a class from CButton, We will name it CSMButton, and then reload DrawItem and PreSubclassWindow, the reason for reloading PreSubclassWindow is to add the BS_OWNERDRAW style to the button before the CSMButton subclass control. Otherwise, the button will not send the WM_DRAWITEM message to the parent window, and the message reflection of MFC will not occur, our DrawItem won't be called. Well, the consequences are very serious. Of course, the CSMButton user can also add BS_OWNERDRAW style to the button by himself, but it may make people feel that there is no Level, well, the consequences are also very serious. Next, we will add response functions for the four messages WM_CAPTURECHANGED, WM_MOUSEMOVE, WM_SETCURSOR, and WM_KILLFOCUS. The response to these four messages is to add more functions to the buttons, for example, make the button look like a button on the toolbar, and change the shape of the mouse.
The use of the CSMButton class is just like that of CButton. Just add a variable for the button. The Demo code contains the source code and usage of the class, which is not described here. The CSMButton class has very simple functions, but it completes a self-drawn button framework. You can modify the code to implement your own style. There are also many such classes on the Internet, providing more powerful functions, for example, STButton. Now let's take a look at the CSMButton effect:
Figure. 7 effect of using a self-drawn button
Step 5: Use the Picture Box Control
To display bitmap in a dialog Box, you can use complicated controls or libraries such as CxImage, or use Picture Box very easily. The default style of Picture Box enables Frame. You need to manually change it to Bitmap, as shown in:
Figure 8 use bitmap
The integration environment of VC6 does not support browsing and editing of 24-bit bitmaps, but it does not affect usage. The bitmaps used in this example are 24-bit. In order to save the processing of the color palette, I am relatively lazy. Use the following code to change the bitmap in the Picture Box:
M_hCat1 = (HBITMAP): LoadImage (AfxGetResourceHandle (), MAKEINTRESOURCE (IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );
Getdlgitem (idc_stc_picture)-> sendmessage (pai_setimage, image_bitmap, (lparam) m_hcat1 );
You can load bitmap as follows:
M_hCat1 = (HBITMAP): LoadImage (AfxGetResourceHandle (), (LPCTSTR) IDB_BITMAP1, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION );
This is the final result:
Figure 9 final result of the dialog box
Download DEMO code for http://blog.csdn.net/images/blog_csdn_net/orbit/CustDlg2.zip)