Create a Screen Saver with GDI+
Create a Screen Saver with GDI+
You might think there’s something mysterious about a Microsoft Windows screen saver, but it’s really nothing more than a Windows application in disguise. You can easily build your own screen saver by creating a regular Windows application with a form that’s displayed in dialog mode, maximized to fill the screen, and waiting for a cue to be dismissed. The cue can be a mouse click or a movement of the mouse. This sample shows you how to create and deploy a screen saver with Visual Basic .NET.
FormBorderStyle = None
ControlBox = False
MaximizeBox = False
MinimizeBox = False
ShowInTaskbar = False
SizeGripStyle = Hide
TopMost = True
WindowState = Maximized
TIP
While you’re developing the screen saver, set the form’s size to a fraction of the screen’s size, set the ControlBox property to True, and set the WindowState property to Normal. That way the form won’t cover the entire screen, you can see your code for debugging purposes, and the form will have a title bar so that you can move it around.
Code Walkthrough
The sample solution has two projects: Create a Screensaver with GDI+.vbproj and GDI+ Screen Saver.vbproj. The second one is the screen-saver project, while the first is a small installation program to install your screen saver once you’ve completed it. The procedures we’ll describe next are in the screen-saver project, except for those in the “Deploying the Screen Saver” section. Figure 10-5 shows the screen saver form during development, small and sizeable.
Figure 10-5. While you’re developing your screen saver, keep the form small and sizeable so that you can test and debug it easily.
Application Entry Point
Sub Main is the entry point into our application, the first procedure that executes when the screen-saver program is run. The STAThread attribute means this application will run in a single-threaded apartment, and it’s needed for COM interoperability reasons. Windows will pass parameters to this program whenever a user is setting up the screen saver using the Display Properties │ Screen Saver property screen. The parameters will be passed in an array named args, and Windows will pass a /p, /c, or /s argument, depending on how the screen saver should behave. We’ll explain each argument in the following paragraphs.
First we need to determine whether an argument was passed and, if so, which one. A /p argument means we’re being asked to show a preview of the screen saver. We haven’t implemented the preview functionality here because it involves creating and joining threads and is beyond the scope of this sample, so we’ll simply exit the application.
<STAThread()> Shared Sub Main(ByVal args As String())
If args.Length > 0 Then
If args(0).ToLower = "/p" Then
Application.Exit()
End If
If the screen saver should offer a form for user options, Windows passes a /c, so we’ll create and display a frmOptions form, and then exit when the form is closed. If the screen saver should simply execute normally, Windows passes a /s, so we’ll create and display a screenSaverForm and then exit when the form is closed.
If args(0).ToLower.Trim().Substring(0, 2) = "/c" Then
Dim userOptionsForm As New frmOptions()
userOptionsForm.ShowDialog() Application.Exit()
End If
If args(0).ToLower = "/s" Then
Dim screenSaverForm As New frmSceenSaver()
screenSaverForm.ShowDialog() Application.Exit()
End If
If there are no arguments, we’ll simply execute the screen saver normally, because it means that the user double-clicked the .scr or .exe file or ran it from within Visual Studio. We know this because otherwise Windows would have passed a parameter to the application.
Else
Dim screenSaverForm As New frmSceenSaver()
screenSaverForm.ShowDialog() Application.Exit()
End If
End Sub
Normal Operation
When the screen-saver form is loaded, we initialize it by creating the Graphics object we’ll use for drawing, loading the user’s saved options (creating an options file if one does not exist), setting the speed based on the user-defined options, and enabling the timer. The speed setting dictates how quickly shapes will be displayed when the screen saver is active.
Private Sub frmSceenSaver_Load(...
m_Graphics = Me.CreateGraphics()
m_Options.LoadOptions() Select Case m_Options.Speed
Case "Slow" Me.tmrUpdateScreen.Interval = 500
Case "Fast" Me.tmrUpdateScreen.Interval = 100
Case Else
Me.tmrUpdateScreen.Interval = 200
End Select
Me.tmrUpdateScreen.Enabled = True
End Sub
All subsequent operations are in response to events. When the timer ticks, a new shape is drawn on the screen, and this continues until a mouse button is clicked or the mouse is moved over the form.
Private Sub tmrUpdateScreen_Tick(...
DrawShape()End Sub
The DrawShape subroutine draws a randomly colored, randomly sized shape to the screen, based on some user-defined parameters. It starts by computing the largest possible values for the screen, as indicated by the form width and height. (Remember that the form is maximized.) Note that x1, x2, y1, and y2 are coordinates for random points to be generated later, myRect is the rectangle within which the shapes will be drawn, and myColor is the color to be used to draw the shapes.
Private Sub DrawShape()
Dim maxX As Integer = Me.Width Dim maxY As Integer = Me.Height Dim x1, x2, y1, y2 As Integer
Dim myRect As Rectangle
Dim myColor As Color
Next we generate some random numbers ranging between zero and the maximums we determined earlier, and we create a rectangle based on the generated coordinates.
x1 = m_Random.Next(0, maxX)
x2 = m_Random.Next(0, maxX)
y1 = m_Random.Next(0, maxY)
y2 = m_Random.Next(0, maxY)
myRect = New Rectangle(Math.Min(x1, x2), Math.Min(y1, y2), _
Math.Abs(x1 - x2), Math.Abs(y1 - y2))
We’ll select a color at random for the shape we’re about to draw. If the user wants transparent shapes, we’ll allow the transparency to be randomly generated as well. If not, we’ll set the Alpha to 255 (the maximum). Alpha, which is the first argument to the Color.FromArgb function, determines how opaque the shape will be.
If m_Options.IsTransparent Then
myColor = Color.FromArgb(m_Random.Next(255), m_Random.Next(255), _
m_Random.Next(255), m_Random.Next(255))
Else
myColor = Color.FromArgb(255, m_Random.Next(255), _
m_Random.Next(255), m_Random.Next(255))
End If
Finally, we draw an ellipse or rectangle based on user-defined options.
If m_Options.Shape = "Ellipses" Then
m_Graphics.FillEllipse(New SolidBrush(myColor), myRect)
Else
m_Graphics.FillRectangle(New SolidBrush(myColor), myRect)
End If
End Sub
Ending the Application
When the user clicks a button or moves the mouse, we want the screen saver to quit. The following routines accomplish that. The first one simply exits if any mouse button is clicked on the screen saver form.
Private Sub frmSceenSaver_MouseDown(...
Application.Exit()End Sub
The second one responds to a mouse movement. Because the MouseMove event can sometimes be fired by very trivial moves of the mouse, we’ll verify that the mouse has actually been moved by at least a few pixels before exiting. To do that, we store the current location of the mouse, turn on a switch that shows we’re tracking the mouse movements, and exit only if the mouse has been moved at least 10 pixels.
Private Sub frmSceenSaver_MouseMove(...
If Not m_IsActive Then
Me.m_MouseLocation = New Point(e.X, e.Y)
m_IsActive = True
Else
If Math.Abs(e.X - Me.m_MouseLocation.X) > 10 Or _
Math.Abs(e.Y - Me.m_MouseLocation.Y) > 10 Then
Application.Exit()
End If
End If
End Sub
Setting Options
We want the user to be able to choose screen-saver options such as how fast shapes should be drawn on the screen, whether they should be rectangles or ellipses, and whether the shapes should be transparent. So we provide an Options form, which gets called when the screen-saver program is invoked with the /c argument.
We’ve defined a class named Options (which you can see in Options.vb) to hold the preferences as properties and provide methods for loading and saving the preferences. Creating a class makes it easy to save the options to disk in XML format by serializing the class and then later retrieving them by deserialization. (See “Application #82: Serialize Objects” for more details.)
The btnOK_Click procedure in frmOptions creates an Options object and sets its values to the user-selected values on the form. It then saves the choices to disk.
Private Sub btnOK_Click(...
Dim myOptions As New Options() If Me.optEllipses.Checked Then
myOptions.Shape = "Ellipses"
Else
myOptions.Shape = "Rectangles"
End If
myOptions.IsTransparent = Me.chkTransparent.Checked
myOptions.Speed = Me.cboSpeed.Text
myOptions.SaveOptions() Me.Close()
End Sub
The Form_Load event procedure of frmOptions loads the current user-defined options and sets the controls on the form accordingly. The Load method of the Options class always returns values, even if the options file doesn’t currently exist.
Private Sub frmOptions_Load(...
Dim myOptions As New Options() myOptions.LoadOptions()
Me.cboSpeed.Text = myOptions.Speed Me.chkTransparent.Checked = myOptions.IsTransparent If myOptions.Shape = "Ellipses" Then
Me.optEllipses.Checked = True
Else
Me.optRectangles.Checked = True
End If
End Sub
Deploying the Screen Saver
Screen savers live in the Windows System directory, which by default is C:\Windows\System32. To install your new screen saver, you simply have to copy the .exe application file to the System directory, changing the extension from .exe to .scr. In the sample solution, we’ve provided a project that copies the file to its correct location. It assumes you have a copy of “101 VB.NET Sample Applications Screensaver.scr” in the root directory of the 89 Create a Screensaver with GDI+ project.
If you don’t, copy “101 VB.NET Sample Applications Screensaver.exe” from GDI+ Screen Saver\bin to the root directory of the 89 Create a Screensaver with GDI+ project and change the extension to .scr.
Once you’re sure the screen-saver file is in place, the btnInstall_Click procedure of frmMain handles the installation for you. Notice the use of Environment.CurrentDirectory (the folder where the executable is running) and Environment.SystemDirectory (the Windows System directory).
Private Sub btnInstall_Click(...
Dim fileName As String = _
"101 VB.NET Sample Applications Screensaver.scr"
Dim sourceFile As String = _
Environment.CurrentDirectory & "\..\" & fileName
Dim destFile As String = Environment.SystemDirectory & "\" & fileName
Try
File.Copy(sourceFile, destFile, True)
Catch ex As Exception
MsgBox(ex.ToString(), MsgBoxStyle.Exclamation, Me.Text)
End Try
End Sub
Conclusion
In this sample application, you’ve seen that a screen saver is simply a Windows Application in disguise. Once you change its file extension from .exe to .scr and put it in the Windows System directory, it will show up on the list of screen savers in Control Panel │ Display. You’ve seen that Windows passes /p, /c, or /s arguments to the screen saver to indicate how it should behave. They indicate Preview, Set Options, and Normal, respectively. We’ve also shown you that generating random colors and even random levels of transparency is easy with the Random class and the FromArgb function of the Color class.
About The Author
Saahil Khan has been a Team Lead with
Offshore Programmers Ltd and freelance write with
Megasolutions Ltd. He has over 5 years of experience in industrial automation and technologies like Java, .NET, and Flash. Saahil has extensive experience in strategizing/architecting solutions in .NET. Today, Sahil is completely focused on Microsoft .NET Technologies like C#, ASP.NET, and emerging technologies like FlashRemoting.