In Depth Banner
Skip Navigation Links

Non-client operations. Part 1, Drawing.

The standard appearance of windows controls seems to change from week to week.  The grey beveled appearance that was part of the original windows look and feel has been superseded by the visual studio style, Windows XP style and now the new Microsoft office 2003 styles.  Controls have gone from 3D to a flat appearance and now back to a subtle 3D appearance that uses advanced graphics to provide shading and shine on toolbars and buttons.   

Windows Forms controls provide a 3D or flat appearance as standard and the framework provides no other method of changing the appearance of controls and apparently do not enable users to customize their appearance other than through handling the Paint event and drawing directly on the control surface.  This method can provide the desired results but is by no means the easiest method simply because all drawing performed in the paint event happens on the client area and so somebody customizing  a control in this manner has to account for sizes of borders or other embellishments when drawing the functional part of the control.

Of course, the underlying windows system provides methods for creating highly customized controls and so is necessary to go back to those methods if Windows Forms controls are to be correctly managed and easy to use. 

Most controls, contain two distinct areas.  The client area in which the interesting functionality of a control is contained and the non-client area where things such as the title bar and sizable border is drawn.  Usually, the custom painting of a control is limited to the client area because it's assumed that all of the controls in an application or on a form will have a particular style and so the general appearance of the control is managed by the system which draws on the non-client area and only the window which defines the client area is passed to your code.  To radically change the appearance of a control you have to trap for non-client messages and inject your own graphics.

Specifying the client window area. 

Whenever a control is created a message, WM_NCCALCSIZE, is sent to the control which enables it to specify the size and position of its client area in relation to its non-client area.  This message has two forms, the simple form requires that a single rectangle is used to specify the size of the client area in relation to the window. This is the form used for simple controls derived from Control. The second form uses a structure containing three rectangles. The first rectangle contains the the proposed new window coordinates of a window that has been moved or resized. The second contains the coordinates of the window before it was moved or resized. The third contains the coordinates of the window's client area before the window was moved or resized. This is academic in the current context though because in practice the second from of the message seems never to be used for simple controls.

The client area is specified by altering the rectangle to suit the thickness of borders or the presence of a title bar. Figure 1 illustrates this.

Figure 1. The relationship between client and non-client areas.

In the case of a control with a simple but fancier border you could specify the client area by shrinking the rectangle by a certain amount.

Finding the non-client area.

Whenever the non-client area needs to be painted the system sends a WM_NCPAINT message to the control. Your response must be to paint the borders or title-bars or whatever else you consider as non-client stuff. This would seem straightforward but there are some caveats. The painting is performed on a different Device Context from that of the client area so you need to obtain the correct DC and wrap it with a GDI+ Graphics object. This is done using interop again to import the GetWindowDC method. The rectangle of the non-client area is also obtained using interop and the GetWindowRect method. The window rectangle coordinates are given in screen coordinates and so must be converted to client coordinates. This causes another little complication because the coordinates of the top and left of the window rectangle, when converted to client coordinates, can be negative so the resulting rectangle needs to be offset again to ensure that whatever is drawn is always in the bounds of the window DC.

Customizing the message loop.

To trap messages such as WM_NCCALCSIZE and WM_NCPAINT you have to create a custom WndProc override. This enables you to respond to the messages you're interested in and pass all the ones you're not interested in to the base class implementation.  This listing shows the custom WndProc for the sample control.    

Painting the non-client area.

The sample code which accompanies this article customizes the appearance of a control by painting a rectangular border with rounded corners. A property in the control enables the user to choose the radius of the corners so that controls may be more or less rounded.  Obviously, the greater the radius on the corner the smaller area there must be in the client rectangle.  The windows procedure code shown in the previous listing calls the RoundRect method listed here to draw the rectangular border.  

Working with Interop. 

The sample control imports three methods GetWindowRect, GetWindowDC and ReleaseDC. The prototypes for these methods are shown in this listing. In addition, the software uses the RECT structure which is slightly different to the Rectangle object inasmuch as it defines the top, left, right and bottom X and Y coordinates of a rectangle rather than the rectangle location and size. This structure has to be marshaled because it's passed as a pointer to a RECT by the Win32 code. The listing shows the RECT structure definition also.

Summary.

This article has shown you how to make the distinction between the client and non-client areas so that the two types of drawing can be kept separate as the design of the windows system dictates. Defining the relationship between client and non-client enables you to embellish controls in a consistent manner that leaves client behaviours alone and enables you to concentrate on control functionality rather than having to worry about non-client appearances at the same time. Next, we'll explore non-client mouse and button operations.

You can find the Source Code files here.

Read on..

Return to the main index.

Copyright © Bob Powell 2003-2009. All rights reserved