How To Skin Your Gui's In C\c++

Hi all, in this tutorial I will be teaching you the basics of skinning your Win32 GUIs(Graphical-User-Interface’s) using C\C++. I hope you enjoy and learn something from this tutorial.



Questions and answers:

Q. What do you mean by skinning?

If you have ever used Windows media player or Winamp, your notice they use fancy looking buttons and that they don’t use the standard rectangular windows your used to seeing with Windows. These tutorials aim to teach you how to make your standard Windows GUI application look more visually appealing.



Q. What are the drawbacks?

More of an annoyance than a drawback, should the end user of your application be using a non standard Windows theme, then your application wont match there theme, which may be a minus in some case’s.



Q. What will I need to know to keep up with this tutorial?

This tutorial assumes you have a decent knowledge of creating GUI’s using dialogs or the standard win32 API in C\C++. It also assumes you have a working knowledge of C\C++.



Ok lets get started with create non-rectangular windows.



Firstly there are two to ways to create non-rectangular windows, the first involves using regions and the second involves using layered windows, we can do two things using layered windows.



Firstly we can make whole windows transparent (see figure 1.1), secondly we can make a certain pixel colour in our window transparent\invisible (see figure 1.2), we can do this using the following windows API call:



CODE
SetLayeredWindowAttributes(HWND hWnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);




Figure 1.1. Example of using transparent windows, note the calculator.





Figure 1.2. Example of making a single pixel colour invisible.





Note: the SetLayeredWindowAttributes(…) function is not compatible with Windows 98 and below.



What about regions?



I chose to keep this tutorial simple, regions are mainly used for backward compatibly should you use windows 98 or below. So I opted not to cover them in this tutorial.



Note: regions are also used to create non-rectangular buttons; I will cover this in part 3 of these tutorials.



Shall we start coding?



Not yet, were nearly there though, “No pain no gain”…

There are a few things you need understand before you can start using the SetLayeredWindowAttributes(…) function.



Firstly we need to load the function from User32.dll so our code will run on Win9x machines (otherwise export will fail and our application may crash).

But before we can load the function we must declare it:

CODE
// defines, we will need these later

#define LWA_COLORKEY            0x00000001

#define LWA_ALPHA               0x00000002

#define g_ColourKey         0xFF00FF // 0,0,255(pink) in RGB hex value



// declare the function

typedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND hWnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);

lpfnSetLayeredWindowAttributes SetLayeredWindowAttributes;



// NOTE: this code goes above main() …




Now we need to import the function:

CODE
// get a handle to the DLL

HMODULE hUser32 = GetModuleHandle(("USER32.DLL"));



// get pointer to function from DLL  

SetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes)GetProcAddress(hUser32, "SetLayeredWindowAttributes");



// error importing function   

if(SetLayeredWindowAttributes == NULL)

        MessageBox(0, "Error importing function", "Error!", MB_ICONSTOP | MB_OK);           



// NOTE: this code goes just after main()




So to recap, we have now declared and imported our function, time to start using it.



To use layered windows we need to give our window an extended window style, that being the following:

CODE
WS_EX_LAYERED




To set this style on our window we need to add the following code into our GUI’s WM_INITDIALOG or WM_CREATE message, depending if your using dialogs or not.



The code:

CODE
if(SetLayeredWindowAttributes != NULL)

               {

                SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);

               }




Done that? Good.



Now we need to actually to make our window layered, we can do this using the following code:

CODE
SetLayeredWindowAttributes(hwnd, g_ColourKey, 0, LWA_COLORKEY);

/* NOTE: put this code into the “if(SetLayeredWindowAttributes != NULL)” statement */




After you call this function any pixels with an RGB value of 0,0,255 will become invisible. If your using dialogs your WM_INITDIALOG message should look like this:

CODE
case WM_INITDIALOG:

          {

            if(SetLayeredWindowAttributes != NULL)

            {

              SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);

              SetLayeredWindowAttributes(hwnd, g_ColourKey, 0, LWA_COLORKEY);

            }    

           

            break;

          }.




Ok so we have managed to make any pixel with an RGB value of 0,0,255 invisible, lets make this useful. We will need to find a nice background for our application; I chose to use this (see figure 1.3). Note I did not make it, I just edited it.



Figure 1.3, Example background.







Note the pink, remember our code makes pink pixels transparent, so we know have nice skin and a non-rectangular window… Well we should have, but there is a little bit more work to do.



We have declared our function, we have imported it, we have set our windows style to layered, we have told our window to make any pixels with an RGB value of 0,0,255 invisible… All we need to do now is load and draw the bitmap (our skin), which isn’t to hard. Save your skin as skin.bmp.

Ok so first we need to make the bitmap a resource, in your projects directory create a 2 files, one called Res.rc and the second called Res.h,. Res.rc is our resource file, we will make our bitmap a resource using this file, the 2nd is used to declare our resource so our main application can see it. Lets begin.



Open Res.rc and add the following code:

CODE
IDB_SKIN BITMAP DISCARDABLE "skin.bmp"




Ok our bitmap is now included as a resource in our application, we now need to declare it in Res.h.



Open Res.h and add the following code:

CODE
#define IDB_SKIN 1001




Now in your main project’s file add:

CODE
#include “res.h”


Into the includes section.



You now have your bitmap contained in your file as a resource, lets load it and start using it in our application.



To load a bitmap we need to use the LoadBitmap() function, which looks like this:

CODE
HBITMAP LoadBitmap(

  HINSTANCE hInstance,  // handle to application instance

  LPCTSTR lpBitmapName  // name of bitmap resource

);




and its description:

QUOTE
The LoadBitmap function loads the specified bitmap resource from a module's executable file. This function has been superseded by the LoadImage function.




Ok so once we load our bitmap we need to get a handle to it, a bitmap has a special type of handle, which is known as “HBITMAP”. Lets see some code.

To load our bitmap:

CODE
// OTHER CODE HERE

HBITMAP hSkinMBmp = NULL; // note: this is a global variable

// OTHER CODE HERE



// ENTRY POINT (WinMain())



  hSkinMBmp = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_SKIN));     

            if(hSkinMBmp == NULL)  

            {                                                         

             MessageBox(0, "Could not load Skin", "Warning", MB_OK |   MB_ICONEXCLAMATION);

            }




Pretty easy isn’t it… All that’s left is to draw our bitmap.



Scroll through your main source file until you reach your dialogs callback function.



We need to add a WM_PAINT message to the dialog, this message is sent every time the dialog redraws itself, so we need to tell the dialog to draw the bitmap, here is my WM_PAINT implementation:

CODE
   case WM_PAINT:

           {

            BITMAP bm;

            PAINTSTRUCT ps;

            HDC hdc = BeginPaint(hwnd, &ps);

            HDC dcSkin = CreateCompatibleDC(hdc);

            GetObject(hSkinMBmp, sizeof(bm), &bm);

            SelectObject(dcSkin, hSkinMBmp);

            BitBlt(hdc, 0,0,500,500, dcSkin, 0, 0, SRCCOPY);

            DeleteDC(dcSkin);

            EndPaint(hwnd, &ps);

            break;

           }




once more thing we need to do is destroy our windows caption bars and make it non-resizable, I wrote this function to do it for us:

CODE
void DestroyCaption(HWND hwnd, int windowWidth, int windowLentgh)

{

HWND hWnd = hwnd;

DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);

dwStyle &= ~(WS_CAPTION|WS_SIZEBOX);

SetWindowLong(hWnd, GWL_STYLE, dwStyle);

InvalidateRect(hWnd, NULL, TRUE);

SetWindowPos(hWnd, NULL, 0,0,windowWidth, windowLentgh, SWP_NOMOVE | SWP_NOZORDER); 

}




If you thought about what I just said you may be thinking how would I move my window if there was no caption bar? To do this we need to add one more message to our dialogs callback rouitine, see the code below:

CODE
case WM_LBUTTONDOWN:

           {

            PostMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);

            break;

           }


This makes our window think we our clicking the caption bar no matter where we click on it, so it means we can drag the window by clicking anywhere on it.



Ok the last thing we need to do is clear up, this is simple, in your dialogs WM_CLOSE message add a call to “DeleteObject(hSkinMBmp);”.



Your WM_CLOSE message should look like this:

CODE
  case WM_CLOSE:

           {    

            DeleteObject(hSkinMBmp);

            EndDialog(hwnd, 0);

            break;

           }




Ok your now finally done, it was a lot of hard work I know, but you did it, you can now create non-rectangular windows and impress your friends with your better looking than there’s GUI biggrin.gif (lol). Just in case you had trouble piecing together all the source in this tutorial, I wrote a complete example in Dev-CPP, you can find the download link below. Well I hope you enjoyed this tutorial, I know I missed a few details but I wanted to keep it simple, I will expand on what you have learnt here in part 2 of this tutorial. Any comments, questions just let me know, KOrUPt smile.gif.





Source code download link:

http://rapidshare.com/files/66820612/Skinn...xample.rar.html

Post a Comment

Previous Post Next Post