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