Creating an Aero Info/Navigation Panel in VB.Net & C#

Posted by Blake on 7/25/2009
)

There is a very cool project over at CodePlex called Windows Forms Aero that has some WinForm implementations of Aero like controls. The control library is written in C#. When looking through the Windows 7 RC, I decided that I wanted to create two panels, one that was a vertical navigation/info panel like exists in the control panel of Vista/Windows 7 and one that’s horizontal like the one you see at the bottom of the Computer (My Computer) dialog that has computer and domain information. Both of these, when laid out on a Glass form have a very nice aesthetic look and feel. Basically, I took a screen shot of the Windows 7 control panel, extracted the colors from the image using Paint.Net and then wrote a custom panel control that draws that look. My code here is in Visual Basic. I’m converting the code to C# also for inclusion in the aforementioned CodePlex project located at: http://www.codeplex.com/windowsformsaero.

Imports System.Drawing
Imports System.Windows.Forms
Imports System.Drawing.Drawing2D

''' <summary>
''' An information colored frame.
''' </summary>
''' <remarks>
''' All of my initial controls are written in VB.  I am porting this control to C# also and it will be included
''' in the "Windows Forms Aero" project at codeplex located at:
''' 
''' http://www.codeplex.com/windowsformsaero
''' 
''' </remarks>
Public Class AeroVerticalPanel
    Inherits Panel
    '****************************************************************************************
    '
    '       Control:  AeroVerticalPanel
    '    Written By:  Blake Pell
    '  Initial Date:  7/24/2009
    '
    '****************************************************************************************

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <remarks></remarks>
    Sub New()
        ' This call is required by the Windows Form Designer.
        InitializeComponent()
        Me.BackColor = Color.Transparent
    End Sub

    ''' <summary>
    ''' Paints our control.  
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks>
    ''' This will work on a non Aero glass form.. on an Aero Glass form however this will need to
    ''' be repainted _if_ it's laid directly on top of glass.  To do that, we'll override the WndProc
    ''' and call our procedure to redraw this control as an image.
    ''' </remarks>
    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        Dim aeroColor1 As Color = System.Drawing.Color.FromArgb(204, 217, 234)
        Dim aeroColor2 As Color = System.Drawing.Color.FromArgb(217, 227, 240)
        Dim aeroColor3 As Color = System.Drawing.Color.FromArgb(232, 238, 247)
        Dim aeroColor4 As Color = System.Drawing.Color.FromArgb(237, 242, 249)
        Dim aeroColor5 As Color = System.Drawing.Color.FromArgb(240, 244, 250)
        Dim aeroColor6 As Color = System.Drawing.Color.FromArgb(241, 245, 251)
        Dim rect As Rectangle = New Rectangle(Me.Width - 1, 0, 1, Me.Height)
        Dim sb As New SolidBrush(aeroColor1)
        e.Graphics.FillRectangle(sb, rect)
        rect = New Rectangle(Me.Width + 1, 0, 1, Me.Height)
        sb.Color = aeroColor1
        e.Graphics.FillRectangle(sb, rect)
        rect = New Rectangle(Me.Width - 2, 0, 1, Me.Height)
        sb.Color = aeroColor2
        e.Graphics.FillRectangle(sb, rect)
        rect = New Rectangle(Me.Width - 3, 0, 1, Me.Height)
        sb.Color = aeroColor3
        e.Graphics.FillRectangle(sb, rect)
        rect = New Rectangle(Me.Width - 4, 0, 1, Me.Height)
        sb.Color = aeroColor4
        e.Graphics.FillRectangle(sb, rect)
        rect = New Rectangle(Me.Width - 5, 0, 1, Me.Height)
        sb.Color = aeroColor5
        e.Graphics.FillRectangle(sb, rect)
        rect = New Rectangle(0, 0, Me.Width - 5, Me.Height)
        sb.Color = aeroColor6
        e.Graphics.FillRectangle(sb, rect)
        sb.Dispose()
    End Sub

    Private _renderOnGlass As Boolean = False
    ''' <summary>
    ''' Whether or not the control should render also on the glass surface.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' This is false by default because of the slight overhead involved with rendering
    ''' this as a bitmap to draw the image out.
    ''' </remarks>
    Public Property RenderOnGlass() As Boolean
        Get
            Return _renderOnGlass
        End Get
        Set(ByVal value As Boolean)
            _renderOnGlass = value
        End Set
    End Property

    ''' <summary>
    ''' Changes the default behavior of given controls when they're added.
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnControlAdded(ByVal e As System.Windows.Forms.ControlEventArgs)
        MyBase.OnControlAdded(e)
        If TypeOf e.Control Is LinkLabel Then
            e.Control.Font = New Font("Segoe UI", 9, FontStyle.Regular, GraphicsUnit.Point, Nothing)
            DirectCast(e.Control, LinkLabel).LinkBehavior = LinkBehavior.HoverUnderline
            DirectCast(e.Control, LinkLabel).LinkColor = Color.FromArgb(64, 64, 64)
            DirectCast(e.Control, LinkLabel).ActiveLinkColor = Color.Blue
        End If
    End Sub

    ''' <summary>
    ''' Processing of incoming messages.  We're going to get a bitmap of the control and then
    ''' redraw it onto the form when the WM_PAINT message comes through.
    ''' </summary>
    ''' <param name="m"></param>
    ''' <remarks>
    ''' </remarks>
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        MyBase.WndProc(m)
        Dim WM_PAINT As Integer = &HF
        Select Case m.Msg
            Case WM_PAINT
                If Me.RenderOnGlass = True Then
                    RedrawControlAsBitmap(Me.Handle)
                End If
        End Select
    End Sub

    ''' <summary>
    ''' Redraws a given control as a bitmap ontop of itself.
    ''' </summary>
    ''' <param name="hwnd"></param>
    ''' <remarks></remarks>
    Public Sub RedrawControlAsBitmap(ByVal hwnd As IntPtr)
        Dim c As Control = Control.FromHandle(hwnd)
        Using bm As New Bitmap(c.Width, c.Height)
            c.DrawToBitmap(bm, c.ClientRectangle)
            Using g As Graphics = c.CreateGraphics
                g.DrawImage(bm, New Point(-1, -1))
            End Using
        End Using
        c = Nothing
    End Sub

End Class