Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Extending the Non Client Area in Aero

0.00/5 (No votes)
9 Feb 2010 1  
Demonstrates how to extend the client area into frame or vice versa.
NcRenderer

Introduction

Just a little ToDo for another project, but I thought I'd share some discoveries... Looking at the spec for the DMW API on MSDN, it seemed fairly straightforward, and there is even a nice little article on the subject with some sample code, so I thought... this should only take a couple of minutes.. Well, seven hours later, I finally finished. I only found one other example of this, and that was in Jose Menendez Póo's article Vista Aero ToolStrip on Non-Client Area. Both that implementation and the article on MSDN recommend consuming the WM_NCCALCSIZE message, in effect creating a borderless form, the problem is that if you start resizing the form, after a time, something breaks and the frame turns black. So I went in search of a better solution.

Implementing the intended behavior of extending the Non Client frame in the client area is actually a fairly trivial task. The problem arose when trying to shrink the NonClient, so that I could put some buttons up there, like on Office or MovieMaker. So, I worked with the example provided in the article, to build the basic model, then adjusted the NCCALCSIZE_PARAMS structure to let Windows know the new client area dimensions, something like this:

case WM_NCCALCSIZE:
    {
        if (m.WParam != IntPtr.Zero && m.Result == IntPtr.Zero)
        {
            if (_bExtendIntoFrame)
            {
                _bSizeProcessed = true;
                NCCALCSIZE_PARAMS nc = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure
					(m.LParam, typeof(NCCALCSIZE_PARAMS));
                nc.rect0.Top -= (_tMargins.cyTopHeight > 
			CaptionHeight ? CaptionHeight : _tMargins.cyTopHeight);
                nc.rect1 = nc.rect0;
                Marshal.StructureToPtr(nc, m.LParam, false);
                m.Result = (IntPtr)WVR_VALIDRECTS;
            }
            base.WndProc(ref m);
        }
        else
        {
            base.WndProc(ref m);
        }
        break;
    }

Then, using a PAINTSTRUCT, you simply paint the intended NonClient areas black, which zeroes their alpha values:

case WM_PAINT:
{
    PAINTSTRUCT ps = new PAINTSTRUCT();
    if (!_bPainting)
    {
        _bPainting = true;
        BeginPaint(m.HWnd, ref ps);
        PaintThis(ps.hdc, ps.rcPaint);
        EndPaint(m.HWnd, ref ps);
        _bPainting = false;
        base.WndProc(ref m);
    }
    else
    {
        base.WndProc(ref m);
    }
    break;
}

using (ClippingRegion cp = new ClippingRegion(hdc, clientRect, rc))
{
    if (IsAero())
    {
        FillRect(hdc, ref rc, GetStockObject(BLACK_BRUSH));
...

Lastly, you only need to extend the NonClient area using the DwmExtendFrameIntoClientArea API. This is done when the WM_ACTIVATE message is sent.

The only problem I had left, was that Windows is storing the wrong window size when SC_MAXIMIZE is called, in effect causing the window to grow in height each time the window was restored as per the WM_NCCALCSIZE routine. This was solved by intercepting the SC_RESTORE message through WM_SYSCOMMAND, and setting the proper height.

case WM_SYSCOMMAND:
    {
        UInt32 param;
        if(IntPtr.Size == 4)
             param = (UInt32)(m.WParam.ToInt32());
          else
             param = (UInt32)(m.WParam.ToInt64());
        if ((param & 0xFFF0) == SC_RESTORE)
        {
            this.Height = _iStoreHeight;
        }
        else if (this.WindowState == FormWindowState.Normal)
        {
            _iStoreHeight = this.Height;
        }
        base.WndProc(ref m);
        break;
    }

What We Have Here

To use this code, you only need to copy the contents of the 'Extend Frame' region, update your using directives, and add a single command to the form's class constructor. I cannot make it easier to use than that.

There are three example projects, demonstrating shrinking the Non Client, expanding the Non Client, and a completely transparent window. All of these use the ExtendMargins method. Passing any negative value in will cause the whole form to be transparent. There is also the option to extend into frame, and draw caption (drawn manually when extended into frame).

public Form1()
{
    ExtendMargins(0, 36, 0, 0, true, true);
    SetStyle(ControlStyles.ResizeRedraw, true);
    InitializeComponent();
    DoubleBuffered = true;
}

Updates

  • Posted last update January-30-10, built to work on all frame types

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here