Click here to Skip to main content
Click here to Skip to main content

gTitleBar - Custom TitleBar for Borderless Forms (VB.NET)

, 4 Oct 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Simulates standard titlebar on a borderless form for easy placement of additional controls on the titlebar, freeing up space on the form. It can also be put on a panel to simulate an MDI Form.
gTitleBarForm1.png

Introduction

I often encounter the situation where I have a Form with only one large control like a Grid or Report on it. I want to use all the form space I can, but if I just want to add one other control like a save button, then you end up with a large amount of wasted empty space. If you could just put the extra control up on the TitleBar then all the form space can be used for the large control.

There are ways of forcing controls up onto the non-client area, but it was difficult and confusing. Then I got the idea of making a control that looks like a TitleBar and put it on a borderless form. Then additional controls could be easily added. In trying to avoid the difficulty of the non-client area, I backed right into the crazy world of Themes.

Control Properties

Here is a list of the primary properties:

  • AllowMove

    Get or Set if the gTitleBar allows moving with the mouse.

  • ControlBoxAffects

    Get or Set if clicking the Control Box Buttons affects the Form or triggers the custom Events.

  • FrameHeightAdj

    Get or Set a padding value for the bottom of the frame.

  • FrameShow

    Get or Set if the frame is visible around the parent.

  • FrameWidth

    Get or Set the width of the frame.

  • ShowCloseBox, ShowMinimizeBox, ShowMaximizeBox

    Get or Set if the Close, Minimize, Maximize button is visible.

  • TitleImage

    Get or Set the image to display in the TitleBar.

  • TitleImageSize

    Get or Set the size to make the image on the TitleBar.

  • TitleText

    Text to display on the Tile Bar.

Using the gTitleBar

Set the Form's FormBorderStyle to None and the TransparencyKey to some color like Magenta. Drop a gTitleBar on your Form, dock it to the top and set the BackColor to the same Color as the Form's TransparencyKey Color.

Points of Interest

Theming was quite an undertaking. It was hard to find a straight forward explanation, so I had to piece it together from many sources. First there is a huge amount of enumerations to sort through, plus a RECT structure and the Theme functions to boot.

The Theme functions include:

  • openThemeData
  • CloseThemeData
  • drawThemeBackground
  • isAppThemed

As the gTitleBar paints itself, it will force the form to paint the border on itself at the same time. isAppThemed is used to determine if the themes are currently active. I will explain how to handle it if they are not later. If they are, then we need to get the graphics object of the Parent and get a pointer for it.

Dim g As Graphics = Parent.CreateGraphics
Dim hdcGraphics As IntPtr = g.GetHdc

Next open the Theme data and get a pointer for that.

intptrWindowTheme = openThemeData(Parent.Handle, "Window")

Using the drawThemeBackground function, each side of the frame can be drawn. For example, here is how to draw the left side of the frame/border.

'Draw Left Side of the Frame
drawThemeBackground(intptrWindowTheme, hdcGraphics, _
	UxThemeWindowParts.WP_FRAMELEFT, _
	_IsFormActive, _
	New RECT(New Rectangle(0, 0, _
	_FrameWidth, Parent.Height - _FrameHeightAdj)), _
	IntPtr.Zero)

The parameters for drawThemeBackground are:

  • HTHEME
      Handle to a window's specified theme data. Use OpenThemeData to create an HTHEME.
  • HDC
      HDC used for drawing the theme-defined background image.
  • iPartId
      Value of type int that specifies the part to draw. See Parts and States.
  • iStateId
      Value of type int that specifies the state of the part to draw. See Parts and States.
  • pRect
      Pointer to a RECT structure that contains the rectangle, in logical coordinates, in which the background image is drawn.
  • pClipRect
      Pointer to a RECT structure that contains a clipping rectangle. This parameter may be set to NULL.

Continue this for the other sides and then the graphics pointer and the Theme pointer need to be released.

g.ReleaseHdc(hdcGraphics)
CloseThemeData(intptrWindowTheme)

Drawing the TitleBar uses the same drawThemeBackground function, but with a iPartId of UxThemeWindowParts.WP_CAPTION.

If Theming is not available, then the border and titlebar can be painted in the classic style using the ControlPaint method.

'Draw Left Side of the Frame
ControlPaint.DrawBorder3D(Parent.CreateGraphics, _
	New Rectangle(0, 0, _FrameWidth, _
	Parent.Height - _FrameHeightAdj), _
	Border3DStyle.Raised, _
	Border3DSide.All)

The last part is the Caption and Image which use basic GDI+ Draw functions to paint to the graphics object.

Control Box Buttons

The Close Minimize and Maximize buttons are just normal buttons. The Theming functions are used in the buttons Paint event. The button state is determined and set in the mouse events. Just like the frame, the drawThemeBackground function will use the iPartId and iStateId to paint the type of button and in what state.

Private Sub CloseWindowButton_Paint(ByVal sender As Object, _
  ByVal e As System.Windows.Forms.PaintEventArgs) _
  Handles CloseWindowButton.Paint
    IsButtonActive(CloseButtonState)
    If isAppThemed() Then
        intptrWindowTheme = openThemeData( _
        	CloseWindowButton.Handle, "Window")
        	
        drawThemeBackground(intptrWindowTheme, e.Graphics.GetHdc, _
        UxThemeWindowParts.WP_CLOSEBUTTON, _
        CloseButtonState, _
        New RECT(New Rectangle(0, 0, _
        CloseWindowButton.Width, CloseWindowButton.Height)), _
        IntPtr.Zero)
        
        e.Graphics.ReleaseHdc()
        CloseThemeData(intptrWindowTheme)
    Else
        ControlPaint.DrawCaptionButton(e.Graphics, _
            CloseWindowButton.DisplayRectangle, _
            CaptionButton.Close, _
            ConvertThemeToClassic(CloseButtonState))
    End If
    
End Sub

Resizing a Borderless Form

Because there isn't a real border to resize the form with, put this "Borderless Form Helper" in the Form's code. This will send the message where the border is and resize accordingly.

#Region "Borderless Form Helper"
 
    Private Const HTLEFT As Integer = 10
    Private Const HTRIGHT As Integer = 11
    Private Const HTTOP As Integer = 12
    Private Const HTTOPLEFT As Integer = 13
    Private Const HTTOPRIGHT As Integer = 14
    Private Const HTBOTTOM As Integer = 15
    Private Const HTBOTTOMLEFT As Integer = 16
    Private Const HTBOTTOMRIGHT As Integer = 17
    Private Const WM_NCHITTEST As Integer = &H84
 
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = WM_NCHITTEST Then
            Dim pt As New Point(m.LParam.ToInt32)
            pt = PointToClient(pt)
            If pt.X < GTitleBar1.FrameWidth AndAlso _
                pt.Y < GTitleBar1.FrameWidth Then
                m.Result = New IntPtr(HTTOPLEFT)
            ElseIf pt.X > (Width - GTitleBar1.FrameWidth) AndAlso _
                pt.Y < GTitleBar1.FrameWidth Then
                m.Result = New IntPtr(HTTOPRIGHT)
            ElseIf pt.Y < GTitleBar1.FrameWidth Then
                m.Result = New IntPtr(HTTOP)
            ElseIf pt.X < GTitleBar1.FrameWidth AndAlso _
                pt.Y > (Height - GTitleBar1.FrameWidth - _
                GTitleBar1.FrameHeightAdj) Then
                m.Result = New IntPtr(HTBOTTOMLEFT)
            ElseIf pt.X > (Width - GTitleBar1.FrameWidth) AndAlso _
                pt.Y > (Height - GTitleBar1.FrameWidth - _
                GTitleBar1.FrameHeightAdj) Then
                m.Result = New IntPtr(HTBOTTOMRIGHT)
            ElseIf pt.Y > (Height - GTitleBar1.FrameWidth - _
                GTitleBar1.FrameHeightAdj) Then
                m.Result = New IntPtr(HTBOTTOM)
            ElseIf pt.X < GTitleBar1.FrameWidth Then
                m.Result = New IntPtr(HTLEFT)
            ElseIf pt.X > (Width - GTitleBar1.FrameWidth) Then
                m.Result = New IntPtr(HTRIGHT)
            Else
                MyBase.WndProc(m)
            End If
        Else
            MyBase.WndProc(m)
        End If
    End Sub
 
    Private Sub Form1_Activated(ByVal sender As Object, _
		ByVal e As System.EventArgs) Handles Me.Activated
        GTitleBar1.IsFormActive = True
    End Sub
 
    Private Sub Form1_Deactivate(ByVal sender As Object, _
		ByVal e As System.EventArgs) Handles Me.Deactivate
        GTitleBar1.IsFormActive = False
    End Sub
 
#End Region

This works great if the form is getting the message that the mouse is on the edge, but the gTitlebar covers the top edge so the form doesn't get the message. To fix this, I needed to monitor the mouse over the gTitleBar and send the message through to the Form when it is over the edge. in the "Resize-Move form" code, the direction of the resize is set, the cursor is updated, and the message is sent to the form.

Private Sub MoveForm()
    ReleaseCapture()
    SendMessage(Parent.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0)
    Invalidate()
End Sub

Private Sub ResizeForm(ByVal direction As ResizeDirection)
    Dim dir As Integer = -1
    Select Case direction
        Case ResizeDirection.Left
            dir = HTLEFT
        Case ResizeDirection.TopLeft
            dir = HTTOPLEFT
        Case ResizeDirection.Top
            dir = HTTOP
        Case ResizeDirection.TopRight
            dir = HTTOPRIGHT
        Case ResizeDirection.Right
            dir = HTRIGHT
        Case ResizeDirection.BottomRight
            dir = HTBOTTOMRIGHT
        Case ResizeDirection.Bottom
            dir = HTBOTTOM
        Case ResizeDirection.BottomLeft
            dir = HTBOTTOMLEFT
        Case ResizeDirection.None
    End Select
    
    If dir <> -1 Then
        ReleaseCapture()
        SendMessage(ParentForm.Handle, WM_NCLBUTTONDOWN, dir, 0)
    End If
End Sub

Floaters

In the screen shot above, you may notice that there are buttons floating below the Form outside the frame. This is just a trick using the FrameHeightAdj Property. This pads the frame up leaving a space on the Form. Place a Panel there and color it with the forms TransparentKey color. Put whatever controls you want there and they will appear to be floating outside the Form.

Other Features

Form2 shows how you can change the TitleBar Height and Border width. You can also see a Form inside a Form. This is really a Panel with the gTitleBar added. This could be used as a limited type of popup if you want. It can be moved or locked in place. Setting the ControlBoxAffects property to EventTrigger will let you take over the button's event.

gTitleBarForm2.png

MDI Forms

Form3 shows how you can use the gTitleBar on a MDI form. Use the gTitleBarMDI as the Child MDI Form.

gTitleBarForm3.png

History

  • Version 1.0.0 - September 2010
    • First version
  • Version 1.1.0 - October 2011
    • Added MDI support

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

SSDiver2112
Software Developer
United States United States
I first got hooked on programing with the TI994A. After it finally lost all support I reluctantly moved to the Apple IIe. Thank You BeagleBros for getting me through. I wrote programs for my Scuba buisness during this time. Currently I am a Database manager and software developer. I started with VBA and VB6 and now having fun with VB.NET

Comments and Discussions

 
QuestionMy Vote Of 4 Pinmemberpitoloko31-Dec-13 18:48 
GeneralMy vote of 5 PinprofessionalBrian A Stephens20-May-13 11:25 
GeneralMy vote of 5 Pinmembermanoj kumar choubey31-Oct-12 21:54 
Nice
GeneralMy vote of 4 Pinmemberfmsalmeida5-Oct-11 12:04 
GeneralMy vote of 5 Pinmemberkiran dangar4-Oct-11 23:57 
QuestionNot work with MDI form. Pinmembervatit4-Oct-11 0:06 
AnswerRe: Not work with MDI form. PinmemberSSDiver21124-Oct-11 15:08 
GeneralClassic Theme PinmemberMarqW15-Oct-10 0:57 
QuestionGetting an error on CloseThemeData Pinmembermelance4214-Sep-10 10:44 
AnswerRe: Getting an error on CloseThemeData PinmemberSSDiver211214-Sep-10 12:02 
GeneralMy vote of 3 PinmemberGregory.Gadow8-Sep-10 7:35 
GeneralMy vote of 5 PinmemberJamal Alqabandi7-Sep-10 9:50 
GeneralGreat Article as always! PinmemberShane Story7-Sep-10 3:30 
GeneralMy vote of 4 Pinmemberv# guy6-Sep-10 20:48 
GeneralMy vote of 4 Pinmembersam.hill4-Sep-10 7:05 
GeneralRe: My vote of 4 PinmemberSSDiver21124-Sep-10 7:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141216.1 | Last Updated 4 Oct 2011
Article Copyright 2010 by SSDiver2112
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid