Introduction
This article demonstrates a quick and easy-to-use .NET solution for attaching buttons to the non-client area of the window title bar. This uses the ActiveButtons
code library, which is able to preserve the composition and transparency effects in Windows Vista.
Background
The Windows Vista operating system fundamentally changes the way the non-client area is rendered, making it almost impossible (if not actually impossible) to paint onto this area without adversely affecting the look and feel. This is because the new Windows Vista graphics engine renders the non-client area outside of the GDI using the new Desktop Windows Manager (DWM).
The DWM is able to render visual effects such as Aero glass by drawing directly to video memory. In doing this it allows the system to perform complex blending of content from multiple applications without adversely effecting performance.
The DWM does provide an API for customising the way it renders specific windows forms through the use of window attributes and Win32 calls. This provides limited control over the rendering of the non-client areas. For example in a previous article I discussed using the DWM API to extend the non-client into a windows form to increase the glass surface area. To date however there doesn't appear to be a solution for rendering a button cleanly onto the non-client area without loosing the visual effects.
In some ways not being able to draw in the non-client area is a good thing. It leads to cleaner more consistent interfaces and adheres to the recommended Microsoft standards for interface design. That said, sometimes there is a need for more flexibility in application design and a valid argument for making use of the non-client area without loosing the standard look & feel. The library discussed in this article provides one possible solution. The alternative would be to paint the entire title bar yourself including the system buttons, or disable composition effects in the application and use the traditional Win32 calls to draw within the NC area.
The Solution
The ActiveButtons
implementation overcomes the limitations of NC rendering in Vista with a unique approach that is not reliant on the DWM API. The solution supports Windows Vista, Windows XP and Windows 2000 from a single codebase, and allows the attachment of buttons to any .NET Windows form along-side the standard minimise, maximise and close buttons.
Example: adding button to title bar
// hook the windows form load event
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// get an instance of IActiveMenu for the current form
IActiveMenu menu = ActiveMenu.GetInstance(this);
// create a new button instance
ActiveButton button = new ActiveButton();
// set some properties
button.BackColor = Color.LightBlue;
button.Text = "One";
// attach a click handler
button.Click += new EventHandler(button_Click);
// add the button to the menu instance
menu.Items.Add(button);
}
How it works
To achieve the desired effect the library uses some trickery to overlay the buttons onto the windows NC area. This means that the rendering is still preformed within the GDI and the current process, but the DWM still renders the standard title bar complete with any glass and transparency effects where applicable.
To accomplish this the ActiveMenu
instance creates an additional transparent Windows Form behind the scenes, which is then attached to the original window. This can done done from within the constructor as follows.
private ActiveMenu(Form form)
{
...
// the following line attaches the menu form
// as a child window of the original
this.Show(form);
...
}
This new Form is then floated over the title bar area allowing .NET controls to be attached in the usual way. As the DWM treats this as another Window, it still blends the buttons and renders them perfectly with the glass background in situ. Once this is achieved it's just a case of positioning and sizing the buttons correctly. To do this the menu hooks into the parent Form's Resize and Move events, and keeps the child menu correctly positioned.
...
// event handlers are attached in constructor routine
parentForm.SizeChanged += new EventHandler(parent_Refresh);
parentForm.Move += new EventHandler(parent_Refresh);
...
// on change of parent form, the menu is repositioned
protected void parent_Refresh(object sender, EventArgs e)
{
this.Top = parentForm.Top;
this.Left = parentForm.Left +
parentForm.Width - this.Width -
(SystemInformation.CaptionButtonSize.Width*3) - 2;
}
The approach is fairly radical, so feedback and bug reports are greatly appreciated. Feel free to experiment with the ActiveButtons.dll library in your own applications.