Adding caption buttons to the Non-client area on Vista

Download binaries, source and demos. Please ask permission before using commerically.

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.

Update: The full source code is now available from the download above.

History

  • Initial release, Sept 2007
  • Additional detail added on how it works, Sept 2007
  • Improved demo application and minor bug fixes, Sept 2007
  • Added ActiveButtonsBasic source code, Sept 2007
  • Added full source code, Dec 2009

Further reading

6 comments:

Jonathan Fleury said...

Exactly what I needed. Thanks !

Mohammad said...

Excellent work.
Thank you.

shahid_hazoor said...

I want to add buttons on every (Win 32) window when user opens.
So can you suggest me any Hook or any idea?
please help me out?

Shahid Hazoor
shahid_hazoor@hotmail.com

Anonymous said...

It is definitely possible to draw on the titlebar, as Office 2007 does this to achieve the ribbon quick access toolbar...

Just have a look at:
DwmSetWindowAttribute & DWMWA_ALLOW_NCPAINT

Anonymous said...

Maybe this will help:

http://www.menendezpoo.com/a.php?h=a4971f920f120b

Harry-D said...

Hello,
is this function also available for Visual C++??

Post a Comment