Creating Custom Window In Win32

     

You know what is custom window,the window that looks different. In this article we will create fixed size window having only minimize & close options.

Download the source code for both Visual Studio & MinGW and also demo.
 
Here's the article to customize windows forms in C#:
But we are talking about customize window in win32 using C/C++. Before you go to start to customize window,you must have some basics of win32 programming.

Following articles covers the basics of win32 programming. 
Now lets move to customize the window.

First step you need to do is that you must set Window Style to WS_POPUP or WS_POPUPWINDOW.

WS_POPUP or WS_POPUPWINDOW styles set your window borderless with no any controls.
 
Now go to the WndProc() function.
 
In WM_CREATE message, you need to add Minimize & Close buttons at the top layered of the window at top-right location.

Buttons can be created using CreateWindow() statement by passing default text as "button"

I am using minimize_button & close_button HWND variables.
 
Syntax:

HWND button = CreateWindow(TEXT("button"), button-text,
                            WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
                             x, y, width, height, HWND parent, action ID,HISTANCE, NULL);
 
Description 
  1. CreateWindow() : this function create a button by passing "button" text to it.
  2. button-text : It is the text display on the button.
  3. W_CHILD : By setting this property the created button is added to the currently created window.
  4. WS_SVISIBLE : By setting this property the created button will be visible.
  5. BS_DEFPUSHBUTTON : This is style of button.there are many styles of buttons like:   

    BS_PUSHBUTTON
    BS_DEFPUSHBUTTON
    BS_FLAT
    BS_CHECKBOX 
    BS_AUTOCHECKBOX
    BS_RADIOBUTTON
    BS_3STATE
    BS_AUTO3STATE
    BS_GROUPBOX
    BS_AUTORADIOBUTTON
    BS_OWNERDRAW

  6. x : Button location X.
  7. y : Button location Y.
  8. width & height. 
See following complete code.

Download the source code for both Visual Studio & MinGW and also demo.

Creating & Adding buttons to window by setting button style to BS_OWNERDRAW
  1. case WM_CREATE :  
  2.   
  3.     minimize_button = CreateWindow(TEXT("button"), TEXT(""),  
  4.         WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,  
  5.         538, 0, 30, 30, hwnd, (HMENU)ID_MINIMIZE,  
  6.         hInst, NULL);  
  7.   
  8.   
  9.     close_button = CreateWindow(TEXT("button"), TEXT(""),  
  10.         WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,  
  11.         568, 0, 30, 30, hwnd, (HMENU)ID_CLOSE,  
  12.         hInst, NULL);   
Next step is to draw borders on the window.this can be done in WM_PAINT message.

Here i drawn a top latered border,window text,icon & left,right,bottom rectangles.
 
See WM_PAINT code in following complete code.

Ok now you added buttons & also drawn a top layered border on window.
Now it's time to customize buttons.
This can be done by specifying WM_DRAWITEM message.
First declare the LPDRAWITEMSTRUCT structure variable.
 
LPDRAWITEMSTRUCT : Long Pointer to Draw Item Structure
 
First define the function that draw on button.
Here's the function that draw a black colored close button.
  1. void DrawCloseButton(HDC hdc)  
  2. {  
  3.     RECT rc;  
  4.     rc.left = 0;  
  5.     rc.top = 0;  
  6.     rc.right = 30;  
  7.     rc.bottom = 30;  
  8.     HBRUSH br = CreateSolidBrush(RGB(0, 0, 0));  
  9.     FillRect(hdc, &rc, br);  
  10.   
  11.     SetBkColor(hdc, RGB(0, 0, 0));  
  12.     SetTextColor(hdc, RGB(255, 255, 255));  
  13.   
  14.     TextOut(hdc, 10, 8, L"X", 1);  
  15. } 
Now define statement by declaring the condition that LPDRAWITEMSTRUCT variable pointes to CtlID. And call the above drawn close button function by defining the ID of that button.

I have declared the ID_CLOSE to 0x001 and added this id while creating the close button in WM_CREATE message.

See following complete code for better understanding.
  1. LPDRAWITEMSTRUCT pdis;  
  2.   
  3.  case WM_DRAWITEM:  
  4.      pdis = (LPDRAWITEMSTRUCT)lParam;  
  5.   
  6.      switch (pdis->CtlID)  
  7.      {  
  8.      case ID_CLOSE:  
  9.          //draw close button  
  10.          DrawCloseButton(pdis->hDC);  
  11.          break;  
  12.      } 
Moving the window :
 
Window can be moved by mouse by specifying WM_NCHITTEST message in switch statement.

Here i just want move the window when cursor in on top layered border.

This can be done by getting the cursor position & window rect.

Here's the code that move window by mouse.
  1. LRESULT move = NULL;  
  2.   
  3. case WM_NCHITTEST:  
  4.     RECT rc;  
  5.     POINT pt;  
  6.   
  7.     GetCursorPos(&pt);  
  8.   
  9.     GetWindowRect(hwnd, &rc);  
  10.     rc.bottom = rc.bottom - 466;  
  11.   
  12.     //if cursor position is within top layered drawn rectangle then  
  13.     //set move to HTCAPTION for moving the window from its client  
  14.     if (pt.x <= rc.right && pt.x >= rc.left && pt.y <= rc.bottom && pt.y >= rc.top)  
  15.     {  
  16.         move = DefWindowProc(hwnd, message, wParam, lParam);  
  17.         if (move == HTCLIENT)  
  18.         {  
  19.             move = HTCAPTION;  
  20.         }  
  21.     }  
  22.   
  23.     return move;  
  24.   
  25.     break 
Download the source code for both Visual Studio & MinGW and also demo.
 
Here's the complete code to create black colored window in Visual Studio. 
  1. #include"stdafx.h"  
  2. #include<Windows.h>  
  3.   
  4. // define id for close, minimize & demo buttons  
  5. #define ID_CLOSE 0x001  
  6. #define ID_MINIMIZE 0x002  
  7. #define ID_DEMOBUTTON 0x00F  
  8.   
  9. BOOL isMouseDownOnCloseButton = FALSE;  
  10. BOOL isMouseDownOnMinimizeButton = FALSE;  
  11. BOOL isMouseDownOnDemoButton = FALSE;  
  12.   
  13. //define HISTANCE variable  
  14. HINSTANCE hInst;  
  15.   
  16. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lparam);  
  17.   
  18. //main function  
  19. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)  
  20. {  
  21.     TCHAR appname[] = TEXT("Windows Programming");  
  22.     WNDCLASS wndclass;  
  23.     MSG msg;  
  24.     HWND hwnd;  
  25.   
  26.     wndclass.cbClsExtra = 0;  
  27.     wndclass.cbWndExtra = 0;  
  28.     wndclass.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(60, 60, 60)));//set back color to window  
  29.     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);  
  30.     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);  
  31.     wndclass.hInstance = hInstance;  
  32.     wndclass.lpfnWndProc = WndProc;  
  33.     wndclass.lpszClassName = appname;  
  34.     wndclass.lpszMenuName = NULL;  
  35.     wndclass.style = CS_HREDRAW | CS_VREDRAW;  
  36.   
  37.     // check this window class is registered or not  
  38.     if (!RegisterClass(&wndclass))  
  39.     {  
  40.         MessageBox(NULL, TEXT("Window class is not registered"), TEXT("Error...."), MB_ICONERROR);  
  41.         return 0;  
  42.     }  
  43.   
  44.     hwnd = CreateWindow(appname,  //window name  
  45.         appname,   // window text  
  46.         WS_POPUP, //set POPUP window style for no border & controls  
  47.         100,   //window position x  
  48.         100,   //window position y  
  49.         600,   //width  
  50.         500,    // height  
  51.         NULL,  
  52.         NULL,  
  53.         hInstance,  
  54.         NULL  
  55.         );  
  56.   
  57.     // show & update created window  
  58.     ShowWindow(hwnd, iCmdShow);  
  59.     UpdateWindow(hwnd);  
  60.   
  61.     // get message from queue  
  62.     while (GetMessage(&msg, NULL, 0, 0))  
  63.     {  
  64.         TranslateMessage(&msg);  
  65.         DispatchMessage(&msg);  
  66.     }  
  67.   
  68.     return msg.wParam;  
  69. }  
  70.   
  71.   
  72. // function to draw close button  
  73. // for setting back color to when mouse down otherwise set black red & draw X text  
  74. void DrawCloseButton(HDC hdc)  
  75. {  
  76.     if (isMouseDownOnCloseButton)  
  77.     {  
  78.         RECT rc;  
  79.         rc.left = 0;  
  80.         rc.top = 0;  
  81.         rc.right = 30;  
  82.         rc.bottom = 30;  
  83.         HBRUSH br = CreateSolidBrush(RGB(200, 30, 30));  
  84.         FillRect(hdc, &rc, br);  
  85.   
  86.         SetBkColor(hdc, RGB(200, 30, 30));  
  87.         SetTextColor(hdc, RGB(255, 255, 255));  
  88.   
  89.         TextOut(hdc, 10, 8, L"X", 1);  
  90.     }  
  91.     else  
  92.     {  
  93.         RECT rc;  
  94.         rc.left = 0;  
  95.         rc.top = 0;  
  96.         rc.right = 30;  
  97.         rc.bottom = 30;  
  98.         HBRUSH br = CreateSolidBrush(RGB(0, 0, 0));  
  99.         FillRect(hdc, &rc, br);  
  100.   
  101.         SetBkColor(hdc, RGB(0, 0, 0));  
  102.         SetTextColor(hdc, RGB(255, 255, 255));  
  103.   
  104.         TextOut(hdc, 10, 8, L"X", 1);  
  105.     }  
  106.   
  107. }  
  108.   
  109. //draw minimize button for back color &  
  110. // _  text  
  111. void DrawMinimizeButton(HDC hdc)  
  112. {  
  113.     if (isMouseDownOnMinimizeButton)  
  114.     {  
  115.         RECT rc;  
  116.         rc.left = 0;  
  117.         rc.top = 0;  
  118.         rc.right = 30;  
  119.         rc.bottom = 30;  
  120.         HBRUSH br = CreateSolidBrush(RGB(60, 60, 100));  
  121.         FillRect(hdc, &rc, br);  
  122.   
  123.         SetBkColor(hdc, RGB(60, 60, 100));  
  124.         SetTextColor(hdc, RGB(255, 255, 255));  
  125.         TextOut(hdc, 10, 5, L"_", 1);  
  126.     }  
  127.     else  
  128.     {  
  129.         RECT rc;  
  130.         rc.left = 0;  
  131.         rc.top = 0;  
  132.         rc.right = 30;  
  133.         rc.bottom = 30;  
  134.         HBRUSH br = CreateSolidBrush(RGB(0, 0, 0));  
  135.         FillRect(hdc, &rc, br);  
  136.   
  137.         SetBkColor(hdc, RGB(0, 0, 0));  
  138.         SetTextColor(hdc, RGB(255, 255, 255));  
  139.         TextOut(hdc, 10, 5, L"_", 1);  
  140.     }  
  141. }  
  142.   
  143. //draw demo button for back color &  
  144. // Hello World text  
  145. void DrawDemoButton(HDC hdc)  
  146. {  
  147.     if (isMouseDownOnDemoButton)  
  148.     {  
  149.         RECT rc;  
  150.         rc.left = 0;  
  151.         rc.top = 0;  
  152.         rc.right = 260;  
  153.         rc.bottom = 80;  
  154.         HPEN hpen;  
  155.   
  156.         //draw gradient button  
  157.         for (int i = 0; i < 80; i++)  
  158.         {  
  159.             hpen = CreatePen(PS_SOLID, 4, RGB(150 - i, 0, 0));  
  160.             SelectObject(hdc, hpen);  
  161.             Rectangle(hdc, 0, 0 + i, 262, 1 + i);  
  162.             DeleteObject(hpen);  
  163.         }  
  164.   
  165.         SetBkColor(hdc, RGB(130, 0, 0));  
  166.         SetTextColor(hdc, RGB(255, 255, 255));  
  167.         TextOut(hdc, 90, 27, L"Hello World", 11);  
  168.     }  
  169.     else  
  170.     {  
  171.         RECT rc;  
  172.         rc.left = 0;  
  173.         rc.top = 0;  
  174.         rc.right = 260;  
  175.         rc.bottom = 80;  
  176.         HPEN hpen;  
  177.   
  178.         //draw gradient button  
  179.         for (int i = 0; i < 80; i++)  
  180.         {  
  181.             hpen = CreatePen(PS_SOLID, 4, RGB(0, 0, 150 - i));  
  182.             SelectObject(hdc, hpen);  
  183.             Rectangle(hdc, 0, 0 + i, 262, 1 + i);  
  184.             DeleteObject(hpen);  
  185.         }  
  186.   
  187.         SetBkColor(hdc, RGB(0, 0, 130));  
  188.         SetTextColor(hdc, RGB(255, 255, 255));  
  189.         TextOut(hdc, 90, 27, L"Hello World", 11);  
  190.     }  
  191.   
  192. }  
  193.   
  194. // function to fill rectangles to look like border for  
  195. // left,right & bottom direction  
  196. void Draw_LeftRightBottom_Rectangles(RECT rect, HDC hdc, HBRUSH brush, int width, int height)  
  197. {  
  198.     RECT leftrect, rightrect, bottomrect;  
  199.     leftrect.left = 0;  
  200.     leftrect.top = rect.bottom - 266;  
  201.     leftrect.right = 4;  
  202.     leftrect.bottom = height;  
  203.     //fill left rect of window for border  
  204.     FillRect(hdc, &leftrect, brush);  
  205.   
  206.     rightrect.left = width - 4;  
  207.     rightrect.top = rect.bottom - 266;  
  208.     rightrect.right = width;  
  209.     rightrect.bottom = height;  
  210.     //fill right rect of window  
  211.     FillRect(hdc, &rightrect, brush);  
  212.   
  213.     bottomrect.left = 0;  
  214.     bottomrect.top = height - 4;  
  215.     bottomrect.right = width;  
  216.     bottomrect.bottom = height;  
  217.     //fill bottom rect of window  
  218.     FillRect(hdc, &bottomrect, brush);  
  219. }  
  220.   
  221.   
  222. // WndProc function  
  223. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
  224. {  
  225.     HDC hdc;  
  226.     PAINTSTRUCT ps;  
  227.     HWND minimize_button, close_button, demo_button;  
  228.     static int X, Y;  
  229.     LRESULT move = NULL;  
  230.     LPDRAWITEMSTRUCT pdis;  
  231.     HICON hIcon;  
  232.   
  233.     switch (message)  
  234.     {  
  235.     case WM_CREATE:  
  236.         //create minimize button with owner draw style  
  237.         // location (538,0) & size (30,30)  
  238.         minimize_button = CreateWindow(TEXT("button"), TEXT(""),  
  239.             WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,  
  240.             538, 0, 30, 30, hwnd, (HMENU)ID_MINIMIZE,  
  241.             hInst, NULL);  
  242.   
  243.         //create close button with owner draw style  
  244.         // location (568,0) & size (30,30)  
  245.         close_button = CreateWindow(TEXT("button"), TEXT(""),  
  246.             WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,  
  247.             568, 0, 30, 30, hwnd, (HMENU)ID_CLOSE,  
  248.             hInst, NULL);  
  249.   
  250.         //create demo button with owner draw style  
  251.         // location (160,200) & size (260,80)  
  252.         demo_button = CreateWindow(TEXT("button"), TEXT(""),  
  253.             WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,  
  254.             160, 200, 260, 80, hwnd, (HMENU)ID_DEMOBUTTON,  
  255.             hInst, NULL);  
  256.   
  257.         break;  
  258.   
  259.   
  260.     case WM_PAINT:  
  261.         hdc = BeginPaint(hwnd, &ps);  
  262.   
  263.         RECT rect;  
  264.         HBRUSH brush;  
  265.   
  266.         GetClientRect(hwnd, &rect);  
  267.         rect.bottom = rect.bottom - 466;  
  268.         brush = CreateSolidBrush(RGB(0, 0, 0));  
  269.   
  270.         // fill rectangle for top layered title border  
  271.         FillRect(hdc, &rect, brush);  
  272.   
  273.         // call above function  
  274.         Draw_LeftRightBottom_Rectangles(rect, hdc, brush, X, Y);  
  275.   
  276.         //draw text as a window by setting white text color & back color  
  277.         SetBkColor(hdc, RGB(0, 0, 0));  
  278.         SetTextColor(hdc, RGB(255, 255, 255));  
  279.         TextOut(hdc, 35, 7, TEXT("Custom Window in Win32"), 22);  
  280.   
  281.         //draw text on window  
  282.         SetBkColor(hdc, RGB(60, 60, 60));  
  283.         SetTextColor(hdc, RGB(200, 200, 200));  
  284.         TextOut(hdc, 160, 120, TEXT("Custom Black Colored Window in Win32"), 36);  
  285.   
  286.         //draw icon  
  287.         hIcon = (HICON)LoadIcon(hInst, IDI_APPLICATION);  
  288.   
  289.         DrawIconEx(hdc, 5, 5, hIcon, 20, 20, 0, brush, 0);  
  290.   
  291.         EndPaint(hwnd, &ps);  
  292.   
  293.         break;  
  294.   
  295.         //draw item  
  296.     case WM_DRAWITEM:  
  297.         pdis = (LPDRAWITEMSTRUCT)lParam;  
  298.   
  299.         switch (pdis->CtlID)  
  300.         {  
  301.         case ID_CLOSE:  
  302.             //draw close button  
  303.             DrawCloseButton(pdis->hDC);  
  304.             break;  
  305.   
  306.         case ID_MINIMIZE:  
  307.             //draw minimize button  
  308.             DrawMinimizeButton(pdis->hDC);  
  309.             break;  
  310.   
  311.         case ID_DEMOBUTTON:  
  312.             DrawDemoButton(pdis->hDC);  
  313.             break;  
  314.         }  
  315.   
  316.         //if button is selected then change values  
  317.         if (pdis->itemState && ODS_SELECTED)  
  318.         {  
  319.             isMouseDownOnCloseButton = TRUE;  
  320.             isMouseDownOnMinimizeButton = TRUE;  
  321.             isMouseDownOnDemoButton = TRUE;  
  322.         }  
  323.         else  
  324.         {  
  325.             isMouseDownOnCloseButton = FALSE;  
  326.             isMouseDownOnMinimizeButton = FALSE;  
  327.             isMouseDownOnDemoButton = FALSE;  
  328.         }  
  329.   
  330.   
  331.         break;  
  332.   
  333.         //actions of buttons  
  334.     case WM_COMMAND:  
  335.   
  336.         switch (wParam)  
  337.         {  
  338.         case ID_CLOSE:  
  339.             PostQuitMessage(EXIT_SUCCESS);  
  340.             return 0;  
  341.   
  342.         case ID_MINIMIZE:  
  343.             SendMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, lParam);  
  344.             break;  
  345.   
  346.         case ID_DEMOBUTTON:  
  347.             MessageBox(NULL, TEXT("Custom Window in Win32"), TEXT("Hello World"), MB_OK);  
  348.             break;  
  349.   
  350.         }  
  351.   
  352.         break;  
  353.   
  354.         //size  
  355.     case WM_SIZE:  
  356.         X = LOWORD(lParam);  
  357.         Y = HIWORD(lParam);  
  358.         break;  
  359.   
  360.         //move window  
  361.     case WM_NCHITTEST:  
  362.         RECT rc;  
  363.         POINT pt;  
  364.   
  365.         GetCursorPos(&pt);  
  366.   
  367.         GetWindowRect(hwnd, &rc);  
  368.         rc.bottom = rc.bottom - 466;  
  369.   
  370.         //if cursor position is within top layered drawn rectangle then  
  371.         //set move to HTCAPTION for moving the window from its client  
  372.         if (pt.x <= rc.right && pt.x >= rc.left && pt.y <= rc.bottom && pt.y >= rc.top)  
  373.         {  
  374.             move = DefWindowProc(hwnd, message, wParam, lParam);  
  375.             if (move == HTCLIENT)  
  376.             {  
  377.                 move = HTCAPTION;  
  378.             }  
  379.         }  
  380.   
  381.         return move;  
  382.   
  383.         break;  
  384.   
  385.   
  386.     case WM_DESTROY:  
  387.         PostQuitMessage(EXIT_SUCCESS);  
  388.         return 0;  
  389.   
  390.     }  
  391.   
  392.     return DefWindowProc(hwnd, message, wParam, lParam);  
  393.   
  394. }