| home
My latest tiny application program needed a custom title. I played with many things to do this, grabbing non-client messages trying to prevent paint events, or just making a window with no caption. Nothing was quite satisfactory. Quite accidentally while trying another window trick to round off the corners I stumbled on a very simple way to completely customize a windows complete appearance, color and shape. This may make more sense if you see the application it is intended for. Please have a look at Timer and get yourself a copy and run it a few times. Be advised, it's still a work in progress, though it seems not to crash your system, just lacks some appearance functionality (oh yeah, and it's timer accuracy is for crap). This method works by simply converting the entire displayed portion of the window to client area. The caption and border areas are still there, but never get displayed, and as an additional benefit you can access the whole window in one set of co-ordinates. This is done by applying a region to the window before we display it. I'm going to describe the code in general, even giving short pieces of it, while allowing you to see the entire program by downloading the source. |
||
|
The window I was making to looks like this: It's unconventional in several respects. First, the caption is the wrong color. And the corners are rounded. Gee, weird buttons too. And those numbers... yetch. Well, to start, all the window face consists of is a bitmap. In fact, the actual app bitmap is very similar to the one you see here. What we need to do is |
![]() |
|
| create a window with a client area just big enough to fit this bitmap, and then we'll cut away the portions we don't need. Let's look at some system metrics (another yetch). | ||
|
We need to compute the window at run time because we can't be sure how our user will have his system set up. Caption heights can change, trust me. (Don't trust me? Right click the desktop, select Appearance, Scheme, pick "Windows Standard (extra large)"). The border and caption sizes are returned in pixel units with a GetSystemMetrics call, using the constants as indicated in this picture. We need to compute the size of the smallest window to hold our bitmap, and the 4 corners of the |
![]() |
|
| client
area (two x-y pairs), not in client co-ordinates, but in window co-ordinates.
wWin == main window width
mov eax, wBackBmp
invoke CreateRoundRectRgn, x1Reg, y1Reg, x2Reg, y2Reg, 20, 20CreateRoundRectRgn is the key to this method. The region is in main window co-ordinates, which we so skillfully defined as the edges of the client area. It differs from a simple rectangle by allowing rounded corners, the rounding defined by an ellipse who's x-y is the last 2 parameters. I used 20,20 for a circular round over. Hard coding these numbers is OK, because the region size never varies. That's the window. We manipulate it in the message pump. At WM_CREATE time we create a device context for our bitmap, and load the bitmap from a resource. I'll explain later why I'm setting the text color (it's a special surprise). Of course, at WM_DESTROY time we release all our resources like a good application should. .IF uMsg == WM_CREATE invoke GetDC, hWnd mov hdc, eax invoke CreateCompatibleDC, hdc mov hdcBuf, eax invoke LoadBitmap,hInstance,IDB_BACKGROUND mov hBmBackground, eax invoke SelectObject, hdcBuf, hBmBackground mov hBmOldBuf, eax invoke SetTextColor, hdcBuf, WHITE invoke SetBkColor, hdcBuf, BROWN invoke InvalidateRect, hWnd, NULL, FALSE .ELSEIF uMsg == WM_DESTROY invoke SelectObject, hdcBuf, hBmOldBuf invoke DeleteDC,hdcBuf invoke DeleteObject, hBmBackground invoke PostQuitMessage,NULL .ELSEIF uMsg == WM_PAINT mov hdc,eax invoke DrawText, hdcBuf,ADDR AppName,-1, ADDR rectCAPTION, DT_SINGLELINE or DT_LEFT or DT_VCENTER invoke BitBlt, hdc, 0, 0, 135,187, hdcBuf, 0, 0, SRCCOPY invoke EndPaint,hWnd,addr ps .ELSEIF uMsg == WM_LBUTTONDOWN mov eax,lParam ; lParam has mouse XY in packed form shr eax,16 ; shift hi word to lo mov lMouseY0, eax ; save Y mov eax, lParam ; get lP back and eax, 0FFFFh ; mask lo word mov lMouseX0, eax ; save X mov KeyDown, TRUE invoke SetCapture, hWnd .ELSEIF uMsg == WM_LBUTTONUP invoke ReleaseCapture .ELSEIF uMsg == WM_MOUSEMOVE mov eax,lParam and eax,0FFFFh ; get the new mouse X co-ord sub eax, lMouseX0 ; and compute dif from first down add rect.left, eax ; and add offset to window left mov eax,lParam shr eax,16 ; mouse Y co-ord sub eax, lMouseY0 ; compute dif from start add rect.top, eax ; and add offset to window top invoke SetWindowPos, hWndMain, NULL, rect.left, rect.top, NULL, NULL, ; -------------------------------- .ELSEIF uMsg == WM_RBUTTONUP .ELSEIF uMsg == WM_NCACTIVATE and eax, 0FFFFH .IF ( eax ) invoke InvalidateRect, hWnd, NULL, FALSE mov retval, 1 For any comments, I may be reached here. |
||