What's New
Q & A
Tip Jar
C# Helper...
Follow VBHelper on Twitter
MSDN Visual Basic Community
TitleMake a bouncing ball screen saver with VB .NET
KeywordsVB.NET, screen saver, commandline, command line, parameters
CategoriesWindows, Multimedia, Graphics
The program starts from a Main subroutine. That routine examines the command line parameters for the flags /p, /c, and /s.

The screen saver should display as a preview. The second command line argument gives the hWnd (window handle) of the preview window in which the program should draw the preview.

The screen saver should display a configuration screen. This program displays the frmConfig form as a modal dialog and then exits.

The screen saver should run as a normal screen saver. The program also runs in this mode if it receives no command line flag or some other flag. To display as a normal screen saver, the program displays the frmCanvas form as a modal dialog.
Private Enum ActionType
End Enum

Public m_Action As ActionType
Public Sub Main(ByVal args As String())
    ' See what we should do.
    If args.Length = 0 Then
        m_Action = ActionType.actRun
        Select Case args(0).ToLower().Substring(0, 2)
            Case "/p"
                m_Action = ActionType.actPreview
            Case "/c"
                m_Action = ActionType.actConfigure
            Case "/s"
                m_Action = ActionType.actRun
            Case Else
                m_Action = ActionType.actRun
        End Select
    End If

    ' Do it.
    Select Case m_Action
        Case ActionType.actRun
            ' Normal screen saver.
            Dim canvas As New frmCanvas
        Case ActionType.actConfigure
            ' Configure.
            Dim dlg_config As New frmConfig
        Case ActionType.actPreview
            ' Preview.
            Dim canvas As New frmCanvas
            SetForm(canvas, args(1))
    End Select
End Sub
The frmConfig form loads the number of balls from the registry using GetSetting. If the user changes the value and clicks OK, the program saves the new value using SaveSetting.
' Display the current settings.
Private Sub Config_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
    txtNumBalls.Text = GetSetting("Bouncer", "Settings", _
        "NumBalls", "20")
End Sub

' Save the new settings.
Private Sub cmdOk_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles cmdOk.Click
        SaveSetting("Bouncer", "Settings", "NumBalls", _
    Catch exc As Exception
    End Try

End Sub
If the program is running as a preview, it calls subroutine SetForm to reparent the frmCanvas form into the preview window.
' Reparent the form into the preview window.
Private Sub SetForm(ByRef frm As Form, ByRef arg As String)
    Dim style As Integer
    Dim preview_hwnd As Integer = Integer.Parse(CType(arg, _
    Dim r As New RECT

    ' Get the preview window's size.
    GetClientRect(preview_hwnd, r)

    With frm
        .WindowState = FormWindowState.Normal
        .FormBorderStyle = FormBorderStyle.None
        .Width = r.right
        .Height = r.bottom
    End With

    ' Add the WS_CHILD style to the form.
    style = GetWindowLong(frm.Handle.ToInt32, GWL_STYLE)
    style = style Or WS_CHILD
    SetWindowLong(frm.Handle.ToInt32, GWL_STYLE, style)

    ' Reparent the form into the preview window.
    SetParent(frm.Handle.ToInt32, preview_hwnd)

    ' Set the form's GWL_PARENT value to the preview window.
    SetWindowLong(frm.Handle.ToInt32, GWL_HWNDPARENT, _

    ' Position the form in the preview window.
    SetWindowPos(frm.Handle.ToInt32, 0, r.left, 0, r.right, _
        r.bottom, _
End Sub
The frmCanvas form displays the normal screen saver. At design time, the form gets these property values:


When the form loads, it determines whether it is running as a preview or a screen saver. If it is running as a screen saver, it hides the cursor. If it is running as a preview, it removes the KeyDown, MouseMove, and MouseDown event handlers so the user doens't stop the preview.

The program then reads the number of balls it should animate using GetSetting. It generates values (radius, location, speed, and color) to define that many random balls. It also creates a memory bitmap to represent the form and makes a Graphics object attached to it.

When the form's Timer event handler fires, the program clears the memory bitmap. Then for each ball, it adds the X and Y components of the ball's velocity to the ball's position, bouncing back any ball that hits an edge of the screen. The event handler then draws the ball on the memory bitmap.

After it has drawn all of the balls in memory, the program copies the memory bitmap onto the screen.

If the user moves the mouse, clicks a mouse button, or presses a key, the form closes itself to end the program.

Private m_Xmax As Integer
Private m_Ymax As Integer

' (X, Y) is the ball's upper left corner.
' D is the ball's diameter.
Private m_NumBalls As Long
Private m_Color() As Color
Private m_D() As Single
Private m_X() As Single
Private m_Y() As Single
Private m_Vx() As Single
Private m_Vy() As Single

Private m_BufferBitmap As Bitmap
Private m_BufferGraphics As Graphics
Private m_Graphics As Graphics
Private m_Random As New Random

' Hide the cursor, get ready, and enable
' the timer.
Private Sub frmCanvas_Load(ByVal sender As Object, ByVal e _
    As System.EventArgs) Handles MyBase.Load
    ' See if we are running as a preview or screen saver.
    If m_Action = ActionType.actRun Then
        ' Screen saver. Hide the cursor.
        ' Preview. Remove the KeyDown, MouseMove, and
        ' MouseDown event handlers.
        RemoveHandler Me.KeyDown, AddressOf _
        RemoveHandler Me.MouseMove, AddressOf _
        RemoveHandler Me.MouseDown, AddressOf _
    End If

    ' Save the screen width and height.
    m_Xmax = Width
    m_Ymax = Height

    ' Get the number of balls.
    m_NumBalls = CLng(GetSetting("Bouncer", "Settings", _
        "NumBalls", "20"))

    ' Create the balls.
    ReDim m_Color(m_NumBalls)
    ReDim m_D(m_NumBalls)
    ReDim m_X(m_NumBalls)
    ReDim m_Y(m_NumBalls)
    ReDim m_Vx(m_NumBalls)
    ReDim m_Vy(m_NumBalls)

    Const MAX_VEL As Single = 0.025
    Const MIN_D_FACTOR As Single = 0.025
    Const MAX_D_FACTOR As Single = 0.1
    For i As Integer = 0 To m_NumBalls - 1
        m_Color(i) = Color.FromArgb(&HFF000000 Or _
            QBColor(m_Random.Next(1, 15)))
        m_D(i) = RandomSingle(m_Ymax * MIN_D_FACTOR, m_Ymax _
            * MAX_D_FACTOR)
        m_X(i) = RandomSingle(1, m_Xmax - m_D(i))
        m_Y(i) = RandomSingle(1, m_Ymax - m_D(i))
        m_Vx(i) = RandomSingle(-m_Ymax * MAX_VEL, m_Ymax * _
        m_Vy(i) = RandomSingle(-m_Ymax * MAX_VEL, m_Ymax * _
    Next i

    ' Make a buffer Bitmap and Graphics.
    m_BufferBitmap = New Bitmap(m_Xmax, m_Ymax)
    m_BufferGraphics = Graphics.FromImage(m_BufferBitmap)

    ' Create a Graphics object for the form.
    m_Graphics = CreateGraphics()

    ' Enable the timer.
    tmrMove.Enabled = True
End Sub

' Return a random Single between min_value and max_value.
Private Function RandomSingle(ByVal min_value As Single, _
    ByVal max_value As Single) As Single
    Return min_value + (max_value - min_value) * _
End Function

' Move the balls.
Private Sub tmrMove_Tick(ByVal sender As Object, ByVal e As _
    System.EventArgs) Handles tmrMove.Tick
    Dim i As Long

    ' Draw on the memory bitmap.
    For i = 0 To m_NumBalls - 1
        ' Update the ball's X coordinate.
        m_X(i) += m_Vx(i)
        If m_X(i) < 1 Then
            m_X(i) = 1 - m_X(i)
            m_Vx(i) *= -1
        ElseIf m_X(i) > m_Xmax - m_D(i) Then
            m_X(i) = (m_Xmax - m_D(i)) - (m_X(i) - (m_Xmax _
                - m_D(i)))
            m_Vx(i) *= -1
        End If

        ' Update the ball's Y coordinate.
        m_Y(i) += m_Vy(i)
        If m_Y(i) < 1 Then
            m_Y(i) = 1 - m_Y(i)
            m_Vy(i) *= -1
        ElseIf m_Y(i) > m_Ymax - m_D(i) Then
            m_Y(i) = (m_Ymax - m_D(i)) - (m_Y(i) - (m_Ymax _
                - m_D(i)))
            m_Vy(i) *= -1
        End If

        ' Draw the ball.
        Dim the_brush As New SolidBrush(m_Color(i))
        m_BufferGraphics.FillEllipse(the_brush, _
            m_X(i), m_Y(i), m_D(i), m_D(i))
        Dim the_pen As New Pen(m_Color(i))
        m_BufferGraphics.DrawEllipse(the_pen, _
            m_X(i), m_Y(i), m_D(i), m_D(i))
    Next i

    ' Copy the image onto the screen.
    m_Graphics.DrawImage(m_BufferBitmap, 0, 0)
End Sub

' Stop if the mouse moves.
Private Sub frmCanvas_MouseMove(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) Handles _
    Static done_before As Boolean
    Static mouse_x As Long
    Static mouse_y As Long

    ' If this is the first time, 
    ' save the mouse position.
    If Not done_before Then
        done_before = True
        mouse_x = e.X
        mouse_y = e.Y
        ' See if the mouse has moved.
        If Math.Abs(e.X - mouse_x) > 10 Or _
           Math.Abs(e.Y - mouse_y) > 10 _
            ' Close the form.
        End If
    End If
End Sub

' Stop if the mouse button is pressed.
Private Sub frmCanvas_MouseDown(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) Handles _
End Sub

' Stop if the user presses a key.
Private Sub frmCanvas_KeyDown(ByVal sender As Object, ByVal _
    e As System.Windows.Forms.KeyEventArgs) Handles _
End Sub


In a program such as this one that reads command line parameters, it is handy to test the program with different parameters. In this example, you can test the program with the /c parameter.

It's a real hassle, however, to have to compile the program and then launch it from a shortcut or DOS window so you can pass it different parameters. Fortunately you can make the development environment start the program with command line parameters.

In Project Explorer, select the project. Then select the View menu's Property Pages command.

Click the Configuration Properties category and select Debugging. Enter the parameters you want to use in the "Command line arguments" text box.


To install the screen saver, compile the program into an executable file. Change the file's extension from .exe to .scr and copy it into the system directory. That's all you need to do.

Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.