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

A Draggable Shaped Form In VB.net

0.00/5 (No votes)
9 Apr 2010 1  
Shows how to create a draggable shaped form in VB.net

Download ShapedForm.zip - 13.47 KB

Introduction

It's fairly easy to produce a shaped form in VB.net. In fact there are already several articles on the subject. But what most existing articles do not address is how to enable the user to drag the form while it's displaying its shaped appearance. If you're working on a project which requires you to produce a shaped form, then I hope this article will be of use to you.

What Is A Shaped Form?

A shaped form is essentially a window that doesn't look like a window normally would. It doesn't conform to the standard way a window should look, but instead uses graphics to render those areas of the window that would usually be handled by the operating system. The screenshot below illustrates this in more detail. Figure 1 shows a form with a familiar border and title bar (shall we say 'normal mode'), whereas figure 2 shows the same window but with the normal window frame switched off (let's call that 'compact mode'). I used GIMP to produce a simple custom shape to use as the basis for my window, but you can use whichever graphics package you normally use. When the user has the form displayed in either normal or compact mode, it can be dragged around using any area on the form that is not covered by a control. In normal mode though, the user can also drag the form around by the title bar as they would expect to be able to do.

windows.png

Some General Advice On This Subject

Don't use custom forms in your application without good reason. Most software users don't care what an application looks like as long as it does the job that they installed it to do. Many software developers fall into the trap of thinking that users are constantly looking at, and admiring their applications while using them. Most often this is just not the case. The average user is often looking somewhere else, or talking to someone, while interacting with your application. By adopting a custom form, your application departs from the standard Windows behaviour model, and this forces your user to enter a learning curve concerning how to perform basic tasks with your application. An example of when not to use a custom form is a straight forward data entry application. There are however, many situations where a custom form brings that special something to an application. One example would be if you're writing a media player of some kind. In that example, the application taking on a cool car stereo look may be just what the user wants.

Using the code

Download the project files and examine the contents. You'll see that the form is a standard form (I named it 'ShapedForm'). I added two controls to the form. One is a link label that allows the user to close the form. The other is a checkbox that allows the user to toggle between compact and normal mode. The form start up in normal mode. It's constructor look like this:

    Public Sub New()
        'This call is required by the Windows Form Designer.
        InitializeComponent()
        'You can set the following properties at design-time or
        'you can set them here like this...
        With Me
            'Set the background image to be used at the shape of the form.
            .BackgroundImage = My.Resources.frame
            'Specify that the image should not be tiled or zoomed etc.
            .BackgroundImageLayout = ImageLayout.None
            'Set the transparency colour for the form. This is the colour
            'that will render as transparent while the window is visible.
            .TransparencyKey = Color.Lime 'Pure green is the colour I used in
            'my image to represent transparency.
        End With
        'In the VS designer I left the form border as normal, so the form will
        'start up in normal mode.
    End Sub

The image used for the form background has transparent regions. So in normal mode, Windows will draw those regions of the form that lay under those transparent regions. In other words, the form's surface will show through the transparent parts of the image. Note that although I've told Windows what colour to use for transparency, I haven't yet told it that transparency applies to the form surface. The user will cause that to happen next. When the user selects compact mode the following code is called:

    Private Sub DoCompactMode()
        'Set transparency on the form
        Me.BackColor = Me.TransparencyKey
        'Switch off my border
        Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
    End Sub

There is a similar routine (not shown here) which puts the form back into normal mode. The code above is all that's required to display your custom shaped form because setting the form background colour to the same colour used for transparency, tells Windows that transparency now applies to the form surface too. So Windows doesn't draw any part of the form that lays beneath a transparent regions of the image. Instead it draws what's beneath that region of the form, thereby causing the form to look as though it consists only of the graphic. Switching off the form's border completes the effect.

Staying Draggable

If the above code was all that was contained in the form, the user would be stuck with a problem when entering compact mode. Because the window title bar is gone, their primary means of moving the form around the screen has gone with it. So too has the ability to to minimise, maximise, and close the window. All gone. As the developer of a shaped window, it's your responsibility to provide these facilities by other means. For minimise, maximise, and close you could supply controls that when clicked, perform those functions simply enough. But giving the user the ability to drag the window is a little more tricky. Firstly you have to intercept the mouse down event for the form:

    Private Sub ShapedForm_MouseDown(ByVal sender As Object, _
                                     ByVal e As MouseEventArgs) _
                                     Handles Me.MouseDown
        'If the left mouse button is depressed
        If e.Button = MouseButtons.Left Then
            With Cursor.Position
                'Calculate the location of the mouse point relative to the form
                mptMouseOffset = New Point(-(.X - Me.Left), -(.Y - Me.Top))
            End With
            'Flag that dragging is enabled
            mblnDragEnabled = True
        End If
    End Sub

In the MouseDown event handler, detect that it was the left mouse button that was depressed. Don't worry about left handed users who have switched their mouse buttons. In such a case, Windows tells you the left button was clicked even though it was the right one, because the swapping of mouse buttons is an agreement between the user and Windows and is not your concern. Windows informs your code it was the left button because the concept of 'right' to a left-handed person is equivalent to the concept of 'left' to a right-handed one. You then have to calculate where the mouse is in relation to the top left corner of the form. This information is not used now, but stored at module level scope for when the user starts the mouse moving. Finally set a flag (also defined at module level) to indicate that the mouse is down (dragging is enabled).

    Private Sub ShapedForm_MouseMove(ByVal sender As Object, _
                                     ByVal e As MouseEventArgs) _
                                     Handles Me.MouseMove
        'If dragging is currently enabled
        If mblnDragEnabled Then
            'Get the mouse location relative to the screen
            Dim mousePos As Point = Cursor.Position
            'Offset it by the amount that was calculated on mouse down
            mousePos.Offset(mptMouseOffset)
            'Place the window at that location
            Me.Location = mousePos
        End If
    End Sub

In the MouseMove event handler, if the mouse button is down, then the user must be dragging. This is when you need the offset value you calculated on MouseDown. Take the current mouse position and offset it by that amount, before causing the window to redraw at the new location. Remember that this code will be executed many times per second, as the user drags, so keep your code efficient, or the user may experience lag.

    Private Sub ShapedForm_MouseUp(ByVal sender As Object, _
                                   ByVal e As MouseEventArgs) _
                                   Handles Me.MouseUp
        'If the left mouse button is released
        If e.Button = MouseButtons.Left Then
            'Flag that draging should stop
            mblnDragEnabled = False
        End If
    End Sub

Finally on the MouseUp event, confirm it was the left mouse that was released and reset your drag flag.

Points of Interest

You should be aware of, and consider, the following issues when implementing windows with custom shapes:

  • The user may not appreciate the cool looking window frame you've supplied. So consider giving the user an option to use a standard look as demonstrated by the sample code provided with this article.
  • You cannot know how often Windows will invoke your MouseMove event handler. On a fast computer with little else to do, it could be many time per second, but on an older system, or earlier version of the operating system, or a busy computer, it could be just a few. So make sure your code is as quick and efficient as possible.
  • Pay careful attention to what functions the user looses as a result of your application displaying a custom form. The system box menu for example, which appears when a user clicks on the icon inside a normal Windows title bar.
  • It may surprise you to learn that some users don't use a mouse at all, so as with any other application, make sure your form still works Ok when using just the keyboard.
  • The sample code allows the user to drag the form using the form's surface (and not just the title bar) when in normal mode. You may decide that the user shouldn't be able to do that in normal mode. If so, your code will have to add and remove the event handlers where appropriate. The DoCompactMode and DoNormalMode routines are the best place for this.
  • Consider how to let the user (who may be looking at your application for the first time) know how to move the window. The most obvious way is to change the mouse pointer to something appropriate (such as a hand) as it moves into the form (MouseEnter event), and reset it to default as it moves out (MouseLeave event).

History

None.

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