Skins in a Dialog-Based Application



Click here for a larger image.

Environment: VC6

Introduction

This is a very simple example to implement a skin in a dialog-based application. The code is actually written by a friend of mine (sun@codefinger.de), 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 use 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 functions in your dialog's 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) into your Dialog class and paste the following code into 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)
    {
      //create 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 orginal 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 that can change big
                  //allocate 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 struct
                  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 dialog's background:

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.

Downloads

Download demo project - 52 Kb



About the Author

Sonu Kapoor

Sonu Kapoor is an ASP.NET MVP and MCAD. He is the owner of the popular .net website http://dotnetslackers.com. DotNetSlackers publishs the latest .net news and articles - it contains forums and blogs as well. His blog can be seen at: http://dotnetslackers.com/community/blogs/sonukapoor/

Comments

  • if the bkcolor is white, black, other color.... Then why let the user decide the color?

    Posted by Legacy on 08/27/2003 12:00am

    Originally posted by: You

    Besides this article is just doooooingggggg "recreate the wheel", there have been quite some articles on Region Dialog already!
    0 point to you!

    Reply
  • Support you

    Posted by Legacy on 03/25/2003 12:00am

    Originally posted by: sandodo

    for those who think it is a lame, why donot you just ignore it?
    Good is good.

    Reply
  • SkinMagic SDK : I think it's the best solution for write skinnable application for C/C++ user!

    Posted by Legacy on 01/22/2003 12:00am

    Originally posted by: Joe

    http://www.appspeed.com
    http://www.appspeed.com
    SkinMagic SDK : I think it's the best solution for write skinnable application for C/C++ user!

    Reply
  • Use SkinBoxer

    Posted by Legacy on 11/08/2002 12:00am

    Originally posted by: Fabian

    I personally use ActiveSkin from www.softshape.com along with a very nitt plugg-in from www.smartbrainsoftware.com (to skin even windows common dialogs).

    Reply
  • How to skin Property Sheet/page etc ?

    Posted by Legacy on 10/05/2002 12:00am

    Originally posted by: vikas

    Please tell me more about skinning. thanks

    Reply
  • Are you really that bored?

    Posted by Legacy on 08/02/2002 12:00am

    Originally posted by: Eli Gassert

    To Everyone that has responded to the "boohoo skinning is lame" post:

    Grow up already! The guy submitted a nice article for those of you that would like to skin. You don't think skinning is good? Then don't read the article! Gee that was a tough one to resolve, wasn't it?

    I know you're all going to flame this response because you have nothing better; after all if you had something better to do you wouldn't have wasted your time with flaming this guy's code in the first place.

    Reply
  • Skinning

    Posted by Legacy on 07/09/2002 12:00am

    Originally posted by: Brno Stankovic

    Skinning should be considered the source of all evil.

    If software needs to be skinnable, apparently it has no worth whatsoever.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • The proliferation of cloud computing options has begun to change the way storage is thought about, procured, and used. IT managers and departments need to think through how cloud options might fit into and complement their onsite data infrastructures. This white paper explains cloud storage and backup, providing advice about the tools and best practices for its implementation and use. Read this white paper for some useful takeaways about how to take advantage of cloud storage for high availability, backup and …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds