INTERMEDIATE  Ask the VB Pro


Toggle the Titlebar
(and Other Form Tricks)

by Karl E. Peterson

  Q. Toggle the Titlebar
I like the way some multimedia player utilities offer users a chance to toggle between normal (rectangular, captioned) and more free-form (irregular, captionless) interfaces. Is there a simple way to toggle the titlebar on and off? Removing the titlebar and the form's borders would certainly ease calculations needed for SetWindowRgn.

ABOUT THIS COLUMN
Ask the VB Pro provides you with free advice on programming obstacles, techniques, and ideas. Read more answers from our crack VB pros. You can submit your questions, tips, or ideas on the site, or access a comprehensive database of previously answered questions.

A. As you've found, the BorderStyle property is read-only at run time. Ideally, you'd need to assign only this property, toggling it between either Sizable (2) or Fixed Dialog (3) and None (0). Microsoft didn't make this option available, so you need to go straight to the API for this flexibility.

Normally, a form with a BorderStyle other than None (0) doesn't display a titlebar if, and only if, the ControlBox property is set to False and the Caption property is assigned an empty string. But ControlBox is another "read-only at run time" property, so your choices are truly few if you want users to be able to close, minimize, or maximize your form. You can achieve your goal by starting with a normal, bordered form and twiddling its style bits.

Flipping the WS_CAPTION style bit on or off controls whether the titlebar is displayed on your form (see Listing 1). Call GetWindowLong, passing your chosen form's hWnd and the GWL_STYLE constant, to obtain the form's current style bits as the function's return value. Use the Or operator to toggle the WS_CAPTION bit on, or And Not WS_CAPTION against the style bits to turn this bit off. Call SetWindowLong, passing the new mask to change your form's styles. You can make one last call to GetWindowLong if you want to confirm that the new style bits are what you expect.

Quite often, changing the style bits is only the first step. For the changes to take effect, a call to SetWindowPos is required, using SWP_FRAMECHANGED, SWP_NOMOVE, SWP_NOZORDER, and SWP_NOSIZE as flags. This combination causes a WM_NCCALCSIZE message to be sent to the window, and forces a redraw using the new style settings, but it doesn't change the size, position, or z-order of the window.


Q.
Prevent Form Resizes Selectively
Sometimes I don't want users to be able to resize my forms, though most of the time it's just fine if they do. Given the BorderStyle property is read-only at run time, is there an API approach I can use to achieve the same effect?

A.The answer here is nearly identical to the situation I just described. Simply substitute WS_THICKBORDER where I mentioned WS_CAPTION in the previous answer, and you've nailed the workaround. We seem to be finding a pattern here. In fact, you might want to be aware of a handful of useful style bits (see Table 1).

I worked up a CFormBorder class that encapsulates setting many normally untouchable properties at run time. Exposed are properties such as ControlBox, MaxButton, MinButton, Sizable, Titlebar, ToolWindow, TopMost, and WhatsThisButton.

Setting most of the class properties is as simple as passing the desired style constant and an On or Off Boolean value to a generic FlipBit(Ex) function that sets or clears the requested bit and redraws the window (see Listing 2). The WS_EX_TOPMOST extended style is rather unique in that it's set using SetWindowPos instead of SetWindowLong, but you can still query it using GetWindowLong.


Q.
Deal With a Captionless Form
My form is without a titlebar, but I'd like to allow users to drag the form about the screen. It would be nice to offer a standard system menu as well. Are these capabilities no longer available?

A.You can both drag a form and display a system menu by sending an appropriate message to your form.

Initiate dragging in the Form_MouseDown event when the user depresses the left mouse button anywhere on your form, or only within chosen areas. First, test for the left mouse button, and optionally for your desired coordinates. VB calls SetCapture anytime the user depresses the mouse, so you need to reverse this call with a call to ReleaseCapture. Finally, send your form a WM_NCLBUTTONDOWN message, indicating the mouse is within the titlebar by passing HTCAPTION in wParam (see Listing 3).

Popping a system menu is even easier, once you know the secret. Send WM_GETSYSMENU, an undocumented message, to display the standard system menu at any given coordinates. Pass 0 in wParam, and combine the X and Y screen coordinates into a single Long value passed in lParam. The best time to send this message is after testing for the right mouse button in the Form_MouseUp event.

I chose to extend the CFormBorder class to sink its Client form events, allowing auto-handling of these two common functions.


Karl E. Peterson is a GIS analyst with a regional transportation planning agency and serves as a member of the Visual Basic Programmer's Journal Technical Review and Editorial Advisory Boards. Online, he's a Microsoft MVP and a section leader on several VBPJ forums. Find more of Karl's VB samples at www.mvps.org/vb.

 
  Get the original code for this article here.

Updated samples based on this article:
FormBdr