Click here to Skip to main content
15,887,683 members
Articles / Desktop Programming / MFC
Article

Skins in a Dialog Based Application

Rate me:
Please Sign up or sign in to vote.
3.56/5 (15 votes)
9 Jun 2002 248.8K   5.7K   120   36
An Easy way to skin a Dialog Based Application

Sample Image

Introduction

This is a very simple example to implement a skin in a Dialog Based Application. The code is actually written by a friend (sun@codefinger.de ) of mine, so thanks go to him. I am just showing you how simple it is to use. Also thanks to Davide Calabro who gave me permission to use his CButtonST class.

Example

In my example I am using a simple Dialog based App called “Skin”. Import a Bitmap you like for your Dialog. In our case it's labeled IDB_MAIN. Create the following variables and function in your Dialog header file.

CSkinDlg : public CDialog
{
public:
        CSkinDlg();
    HBITMAP m_hBmp;
    HRGN m_hWndRgn;
    HRGN DIBToRgn(HBITMAP hBmp,COLORREF BkColor,BOOL Direct); // Handle the Skin
    .
    .
    .
In the Constructor do the following:
CSkinDlg::CSkintDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CSkinDlg::IDD, pParent)
{
    //{{AFX_DATA_INIT(CSkinDlg)
    //}}AFX_DATA_INIT
    m_hBmp=(HBITMAP)LoadImage(AfxGetApp()->m_hInstance,
                                   MAKEINTRESOURCE(IDB_MAIN),
                                   IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);

    m_hWndRgn=DIBToRgn(m_hBmp,0x00ff00,FALSE);
}

Insert the function called HRGN DIBToRgn(HBITMAP hBmp, COLORREF BkColor, BOOL Direct) to your Dialog class and paste the following code to it.

HRGN CSkinDlg ::DIBToRgn(HBITMAP hBmp, COLORREF BkColor, BOOL Direct)
{
    // use to return the handle of the HGRN
      HRGN hRgn = NULL;                    
    #define MAX_ALLOC_RECTS  100
    //the difference of the color
    COLORREF  Tolerance=0x00101010;
    if (hBmp)
    {
        //creat the dib to save the dc
        HDC hMemDC = CreateCompatibleDC(NULL);        
        if (hMemDC)
        {
            BITMAP bm;
            //get the info of the bitmap
            GetObject(hBmp, sizeof(bm), &bm);    

            BITMAPINFOHEADER BmpInfoh = {     // the struct of the bitmap
                    sizeof(BITMAPINFOHEADER), // biSize
                    bm.bmWidth,               // biWidth;
                    bm.bmHeight,              // biHeight;
                    1,                        // biPlanes;
                    32,                       // biBitCount
                    BI_RGB,                   // biCompression;
                    0,                        // biSizeImage;
                    0,                        // biXPelsPerMeter;
                    0,                        // biYPelsPerMeter;
                    0,                        // biClrUsed;
                    0                         // biClrImportant;
            };
            //design a void point to point to the bitmap
            LPVOID pBit32; 
            //creat a DIB
            HBITMAP hDib32 = CreateDIBSection(hMemDC, 
                    (BITMAPINFO *)&BmpInfoh, 
                    DIB_RGB_COLORS, &pBit32, NULL, 0);
            if (hDic32)
            {
                //copy dib to DC
                HBITMAP hOldib32 = (IBITMAP)SelectObject(hMemDC, hDib32);
                // create a DC to save orgin bitmap
                HDC hDC = CreateCompatibleDC(hMemDC);
                if (hDC)
                {
                    BITMAP bm32;
                    // get the new 34 bit Dib size
                    GetObject(hDib32, sizeof(bm32), &bm32);
                    //make sure the 32Dib's every line pilex's is 4 's times
                    while (bm32.bmWidthBytes % 4)
                        bm32.bmWidthBytes++;
                    //copy the orginal dib to DC
                    HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
                    //copy dib to memory DC
                    BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDC, 0, 0, SRCCOPY);
                    DWORD MaxRects = MAX_ALLOC_RECTS;
                    SYSTEM_INFO  Sysinfo;
                    //get memory size
                    GetSystemInfo(&Sysinfo);
                    //make a stack which can chang big
                    //alloct memory
                    HANDLE hRcData=HeapCreate(HEAP_GENERATE_EXCEPTIONS,Sysinfo.dwPageSize, 0);
                    RGNDATA * pRcData=(RGNDATA*)HeapAlloc(hRcData,HEAP_ZERO_MEMORY,
                        sizeof(RGNDATAHEADER)+sizeof(RECT)*MaxRects);
                     //fill the the RGNDATA struck
                    pRcData->rdh.dwSize = sizeof(RGNDATAHEADER);
                    pRcData->rdh.iType = RDH_RECTANGLES;
                    pRcData->rdh.nCount = pRcData->rdh.nRgnSize = 0;
                    SetRect(&pRcData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
                             BYTE hr,hg,hb,lr,lg,lb;
                    switch(BkColor)
                    {
                    case RGB(255,255,255):    //if the bkcolor is white
                        hr = GetRValue(BkColor);
                        hg = GetGValue(BkColor);
                        hb = GetBValue(BkColor);
                        lr = min(0xff, hr - GetRValue(Tolerance));
                        lg = min(0xff, hg - GetGValue(Tolerance));
                        lb = min(0xff, hb - GetBValue(Tolerance));
                        break;
                    case RGB(0,0,0):    //if the bkcolor is black
                        lr = GetRValue(BkColor);
                        lg = GetGValue(BkColor);
                        lb = GetBValue(BkColor);
                        hr = min(0xff, lr + GetRValue(Tolerance));
                        hg = min(0xff, lg + GetGValue(Tolerance));
                        hb = min(0xff, lb + GetBValue(Tolerance));
                        break;
                    default:        //if the bkcolor is other color
                        Tolerance=0x111111;
                        lr =max(0, GetRValue(BkColor)-GetRValue(Tolerance));
                        lg = max(0,GetGValue(BkColor)-GetGValue(Tolerance));
                        lb = max(0,GetBValue(BkColor)-GetBValue(Tolerance));
                        hr=min(0xff,GetRValue(BkColor)+GetRValue(Tolerance));
                        hg=min(0xff,GetGValue(BkColor)+GetGValue(Tolerance));
                        hb=min(0xff,GetBValue(BkColor)+GetBValue(Tolerance));
                        break;
                    }
                    // Get the bit point and do the search
                    BYTE *pBits = (BYTE *)bm32.bmBits + 
                                         (bm32.bmHeight - 1) * bm32.bmWidthBytes;
                    for (int y = 0; y < bm.bmHeight; y++)
                    {
                        for (int x = 0; x < bm.bmWidth; x++)
                        {
                            int x0 = x;
                            DWORD *pColor = (DWORD *)pBits + x;
                            BYTE dr,dg,db;
                            while (x < bm.bmWidth)
                            {
                                dr=GetRValue(*pColor);
                                dg=GetGValue(*pColor);
                                db=GetBValue(*pColor);

                                if ((dr>= lr && dr<= hr) && (dg>=lg&&dg<=hg) 
                                                         && (db>=lb&&db<=hb))
                                {
                                    if(Direct)
                                        break;
                                    else
                                    {
                                        pColor++;
                                        x++;
                                    }
                                  }
                                else if(Direct)
                                {
                                    pColor++;
                                    x++;
                                }
                                else
                                    break;

                            }
                            if (x > x0)
                            {
                                if (pRcData->rdh.nCount >= MaxRects)
                                {
                                    MaxRects += MAX_ALLOC_RECTS;
                                    //re alloc the stack
                                    pRcData=(RGNDATA*)HeapReAlloc(
                                    hRcData,HEAP_ZERO_MEMORY,pRcData, 
                                    sizeof(RGNDATAHEADER)+sizeof(RECT)*MaxRects);
                                }
                                RECT *pr = (RECT *)&pRcData->Buffer;
                                SetRect(&pr[pRcData->rdh.nCount], x0, y, x, y+1);
                                pRcData->rdh.rcBound.left = x0;
                                pRcData->rdh.rcBound.top = y;
                                pRcData->rdh.rcBound.right = x;
                                pRcData->rdh.rcBound.bottom = y+1;
                                pRcData->rdh.nCount++;

                                if (pRcData->rdh.nCount == 3000)
                                {    
                                    HRGN tmphRgn = ExtCreateRegion(NULL,
                                    sizeof(RGNDATAHEADER) + (sizeof(RECT) * MaxRects),
                                    pRcData);
                                    if (hRgn)
                                    {
                                        CombineRgn(hRgn, hRgn, tmphRgn, RGN_OR);
                                        DeleteObject(tmphRgn);
                                    }
                                    else
                                        hRgn = tmphRgn;
                                    pRcData->rdh.nCount = 0;
                                    SetRect(&pRcData->rdh.rcBound, 
                                    MAXLONG, MAXLONG, 0, 0);
                                }
                            }
                        }

                        // search next line
                        pBits -= bm32.bmWidthBytes;
                    }
                    HRGN tmphRgn = ExtCreateRegion(NULL, 
                            sizeof(RGNDATAHEADER) + (sizeof(RECT) * MaxRects), pRcData);
                    if (hRgn)
                    {
                        CombineRgn(hRgn, hRgn, tmphRgn, RGN_OR);
                        DeleteObject(tmphRgn);
                    }
                    else
                        hRgn = tmphRgn;
                    // make a rect ,use this rect xor to the  BkColor
                    //then we can get the rect we want
                    if(!Direct)
                    {
                        HRGN hRect=CreateRectRgn(0,0,bm.bmWidth,bm.bmHeight);
                                        if(hRect)
                        {
                            CombineRgn(hRgn,hRgn,hRect,RGN_XOR);
                            DeleteObject(hRect);
                        }
                        else
                            return NULL;
                    }
                    //release the memory
                    HeapFree(hRcData,HEAP_NO_SERIALIZE,pRcData);
                    SelectObject(hDC, holdBmp);
                    DeleteDC(hDC);
                    DeleteObject(holdBmp);
                }
                SelectObject(hMemDC,hOldib32);
                DeleteDC(hMemDC);
                DeleteObject(hOldib32);
                DeleteObject(hDib32);
            }
            else
                DeleteDC(hMemDC);
        }
    }
    return hRgn;
  }

Add a handler for the ON_WM_ERASEBKND message to erase the background of the Dialog:

BOOL CSkinDlg::OnEraseBkgnd(CDC* pDC) 
{
    if(m_hBmp)
    {
        BITMAP bm;
        GetObject(m_hBmp,sizeof(bm),&bm);
        HDC hMemdc=CreateCompatibleDC(pDC->m_hDC); 
        if(hMemdc)
        {
           HBITMAP hOldBmp=(HBITMAP)SelectObject(hMemdc,m_hBmp);
           if(hOldBmp)
           {
               BitBlt(pDC->m_hDC,0,0,bm.bmWidth,bm.bmHeight,hMemdc,0,0,SRCCOPY);
               SelectObject(hMemdc,hOldBmp);
               DeleteDC(hMemdc);
               DeleteObject(hOldBmp);
               return TRUE;
           }
           else
             DeleteDC(hMemdc);
        }
    }
    return CDialog::OnEraseBkgnd(pDC);
}

In your OnInitDialog enter the following code:

BOOL CSkinDlg::OnInitDialog()
{
    .
    .
    .
    // Show the Skin
    if(m_hWndRgn)
        SetWindowRgn(m_hWndRgn,TRUE);

    return TRUE;  // return TRUE  unless you set the focus to a control
}

The 'minimise' and 'close' boxes on the dialog are implemented using Davide Calabro's CButtonST class

That's it ! Run your app and you will see your Great Skin. This Article is Provided by www.codefinger.de

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralDealing with system font settings on different machines Pin
1Bob7-Oct-05 3:04
1Bob7-Oct-05 3:04 
GeneralI can&#180;t make the dialog moves! Pin
XaHoiBuon25-Sep-05 20:43
XaHoiBuon25-Sep-05 20:43 
Generaluskin a nother personal free skin lib Pin
thirdwolf26-Jun-05 17:29
thirdwolf26-Jun-05 17:29 
Generalskins in MDI Pin
Ambareen Awan20-Apr-05 9:07
Ambareen Awan20-Apr-05 9:07 
GeneralRe: skins in MDI Pin
Y_R30-Mar-06 10:18
Y_R30-Mar-06 10:18 
GeneralSkinnable Applications in C# Pin
sim_san6-Mar-05 22:46
sim_san6-Mar-05 22:46 
Questionrounded corners? Pin
hoobyjuice20-May-04 1:13
hoobyjuice20-May-04 1:13 
AnswerRe: rounded corners? Pin
Anonymous1-Jan-05 19:02
Anonymous1-Jan-05 19:02 
GeneralRe: rounded corners? Pin
Don Miguel21-Mar-06 1:44
Don Miguel21-Mar-06 1:44 
GeneralRe: rounded corners? Pin
digitalmythology11-Dec-06 21:02
digitalmythology11-Dec-06 21:02 
Generalhttp://www.appspeed.com/ Pin
Tome11113-Apr-03 7:50
sussTome11113-Apr-03 7:50 
GeneralRe: SkinMagic Toolkit Pin
pinkpanther13-Apr-03 6:39
pinkpanther13-Apr-03 6:39 
GeneralRe: SkinMagic Toolkit Pin
ActiveSkin13-Apr-03 7:55
sussActiveSkin13-Apr-03 7:55 
GeneralRe: SkinMagic Toolkit Pin
pinkpanther13-Apr-03 8:23
pinkpanther13-Apr-03 8:23 
GeneralSkinMagic SUCKS!!! Pin
Bill SerGio, The Infomercial King25-May-03 0:07
Bill SerGio, The Infomercial King25-May-03 0:07 
Generalwont work Pin
m1k3d3s23-Jan-03 7:34
m1k3d3s23-Jan-03 7:34 
GeneralRe: wont work Pin
Sonu Kapoor13-Apr-03 20:13
Sonu Kapoor13-Apr-03 20:13 
GeneralRe: The better solution for skinnable application GUI Pin
pinkpanther12-Apr-03 12:42
pinkpanther12-Apr-03 12:42 
GeneralRe: The better solution for skinnable application GUI Pin
GoodTool13-Apr-03 8:02
sussGoodTool13-Apr-03 8:02 
GeneralRe: The better solution for skinnable application GUI Pin
pinkpanther13-Apr-03 8:24
pinkpanther13-Apr-03 8:24 
GeneralRe: The better solution for skinnable application GUI Pin
Misty_Blue10-Oct-04 17:51
Misty_Blue10-Oct-04 17:51 
QuestionTransparent background? Pin
mykone22-Dec-02 20:22
mykone22-Dec-02 20:22 
QuestionWhere is the AppIcon? Pin
netseeker12-Aug-02 0:48
netseeker12-Aug-02 0:48 
AnswerRe: Where is the AppIcon? Pin
sunyuzhe13-Aug-02 13:55
sunyuzhe13-Aug-02 13:55 
The information in this article applies to:

Microsoft Visual C++, 32-bit Editions, versions 5.0, 6.0
SYMPTOMS
It is possible to create an MFC dialog box-based application with no title bar by turning off the dialog box's standard Title bar style. However, when the application runs, the shell taskbar displays a blank button with no icon and no text, even though the icon resource exists.
CAUSE
For the shell's taskbar to display an icon, the dialog template needs the System menu style WS_SYSMENU. The resource editor automatically disables that style and others if the Title bar style is not turned on.
RESOLUTION
Manually add the WS_SYSMENU style by editing the resource as text. If you will be editing any resources afterwards, you will also need to move the dialog resource to the res\<projname>.rc2 file. This prevents the resource editor from overwriting your change, but it also prevents you from being able to edit the dialog box with the resource editor.
STATUS
Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article.
MORE INFORMATION
For some applications, a dialog box functions conveniently as a main window. Visual C++ provides a dialog-based option in the MFC AppWizard (exe) for that purpose. If you want a main dialog window without a standard title bar in such an application, you must edit the dialog properties in ResourceView. To do this, follow these steps in your open project:
From the File menu, select New. Select the MFC AppWizard (exe). Name it NoTitle and click OK.
In Step 1 of the AppWizard, select Dialog-based. Click Finish, then click OK on the confirmation dialog box.
In ResourceView, expand the Dialog resources. Double-click IDD_NOTITLE_DIALOG.
Right-click an unused area of the dialog box, and select Properties.
In the Properties editor, click the Styles tab, and clear the Title bar check box.
If you build and run the project at this point, you will notice that the button that the shell normally displays in the taskbar for your running application is completely blank. There is no icon, and there is no text.
If you go back to the Properties editor for the main dialog box at this point, you will notice that various items in the Styles tab are disabled. One of those items is the System menu check box. That check box controls adding the WS_SYSMENU style to the dialog resource. The dialog needs this style in order for the application icon to appear in the taskbar. For additional information on how to display an icon in the title bar, click the article number below to view the article in the Microsoft Knowledge Base:
Q179582 HOWTO: Set the Title Bar Icon in a Dialog Box
Though the resource editor prevents adding the WS_SYSMENU style to a dialog box with no title, the combination is possible. You can manually edit the <projname>.rc file to put the WS_SYSMENU style in and verify that it causes an icon to appear in the taskbar button:
In the open project created earlier, from the File menu, select Open.
In the Open as dropdown list, select Text.
Type NoTitle.rc in the File name edit box, then click Open. If the ResourceView is open for the project, you will see a warning dialog box that the resource editor will be closed. Click OK to allow that.
Move down to the Dialog section of the .rc file. Locate the IDD_NOTITLE_DIALOG DIALOGEX resource.
On the line labeled STYLE following the dialog name, append:
| WS_SYSMENU
Now, select the lines defining the dialog resource, starting with the resource name down to and including the END tag. Using the standard editor, select by dragging the mouse over those lines.
From the Edit menu, select Cut. The selected resource disappears.
Open the NoTitle.rc2 resource file. On the workspace pane, click the FileView tab. Expand the project's Resource Files folder, and double-click the NoTitle.rc2 file
Position the cursor at the end of any text in the NoTitle.rc2 file (or at another convenient location), then from the Edit menu, click Paste. The dialog resource has now been transferred to the manually edited resource file.
Press CTRL+F5 to build and run the project. Note that an icon now appears on the taskbar button associated with your application.
Clearing the Title bar check box also disables the Caption field in the dialog resource General properties. If you add a CAPTION entry in the resource with or without a WS_CAPTION style, you will get a title bar anyway. To display text in the taskbar button, you must call SetWindowText() in the OnCreate() handler or some other appropriate place:
From the View menu, select ClassWizard.
On the Message Maps tab, select the main dialog class from the Class name drop down list.
In the Messages list, double-click the WM_CREATE message. Click the Edit code button.
Under the TODO line, just before the return from the function, add:
SetWindowText("MyTitle");
Press CTRL+F5 to build and run the project. Notice that the application has no title bar, but an icon and the text you entered in the SetWindowText() call appear on the taskbar button.

> -------------------------------------------------
> Codefinger
> Sun Yuzhe
> Software Developer and Consultant
> sun@codefinger.de
> www.codefinger.de
> Your Programming partner in India and Germany
> -------------------------------------------------



> -------------------------------------------------
> Codefinger
> Sun Yuzhe
> Software Developer and Consultant
> sun@codefinger.de
> www.codefinger.de
> Your Programming partner in India and Germany
> -------------------------------------------------
Generalthe bitmap moves! Pin
NightBlade17-Jul-02 10:07
NightBlade17-Jul-02 10:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.