Create a screensaver with .NET and WPF

We’ve had 14inches of snow in the last 24 hours. I’m home-bound and having cabin fever. I took the opportunity to learn how to create a screensaver with .NET and WPF. Specifically, I wanted something personal that would show my WBS logo as a marquee across the screen.

Code is available in my GitHub repository: WBSScreenSaver

First create a new WPF application. Then, modify the App.xaml to call a specified method on startup. Here I’m using a method named “ApplicationStartup”

<Application x:Class="WBSScreenSaver.App"
 <Application.Resources />

Create a method in the App.xaml.cs file named ApplicationStartup. This method will be used to parse command-line options and start start the main window of your screensaver.

There are three command-line parameters all screensavers need to handle:

  • /s – Start the screensaver
  • /p – Preview the screensaver
  • /c – Configure the screensaver

For now, let’s just concern ourselves with the /s (Start) option.

if (e.Args.Length == 0 || e.Args[0].ToLower().StartsWith("/s"))
    foreach (Screen s in Screen.AllScreens)
        if (s != Screen.PrimaryScreen)
            Blackout window = new Blackout();
            window.Left = s.WorkingArea.Left;
            window.Top = s.WorkingArea.Top;
            window.Width = s.WorkingArea.Width;
            window.Height = s.WorkingArea.Height;
            MainWindow window = new MainWindow();
            window.Left = s.WorkingArea.Left;
            window.Top = s.WorkingArea.Top;
            window.Width = s.WorkingArea.Width;
            window.Height = s.WorkingArea.Height;

The conditional will run the program is no parameters are passed (for debugging) or if the “/s” is passed. I’m supporting multiple monitors here. For each screen, I set the window size to the size of the monitor. I only want the screensaver to display on the main monitor. All other monitors should go black. This is done using my window named “Blackout” which doesn’t show anything. except a black background.

The MainWindow window is my actual screensaver.

<Window x:Class="WBSScreenSaver.MainWindow"
    <Grid x:Name="MainGrid">
        <Image x:Name="logo"
               Source="WBS_200px-icon.png" />

I set two events on the screensaver which close the windows in the event of a mouse or key event. There’s only a grid with my logo image. It really couldn’t get much simpler. Now for the fun part. Getting it to move across the screen!

I’m using XAML animations for the marquee effect. You’ll need  something on a timer to move elements yourself if you want some random movements. XAML animations don’t support randomization. The screensaver work is done in a Window.Loaded event. The screen maximization and animation start needs to be triggered after the windows has been loaded.

void MainWindow_Loaded(object sender, RoutedEventArgs e)
    TransformGroup group = new TransformGroup();
    double width = this.MainGrid.RenderSize.Width;
    DoubleAnimation animation = new DoubleAnimation((width / 2) * -1, width / 2 + logo.ActualWidth, new Duration(new TimeSpan(0, 0, 0, 10)));
    animation.RepeatBehavior = RepeatBehavior.Forever;
    TranslateTransform tt = new TranslateTransform(-logo.ActualWidth * 2, 0);
    logo.RenderTransform = group;
    logo.Width = 200;
    logo.Height = 200;
    tt.BeginAnimation(TranslateTransform.XProperty, animation);
    WindowState = WindowState.Maximized;
    Mouse.OverrideCursor = Cursors.None;

All UI elements have a RenderTransform property which takes transformation objects such as TranslateTransform. TranslateTransform is used to translate X and Y coordinates of elements. You can make elements spin or scroll easily using these.

RenderTransform will also take a TransformGroup. TransformGroup can contain several transforms, all of which will be applied to your element. You could have a marquee transform and a spin the element at the same time. Look at the MSDN documentation on the different Transforms.

I determine the width of the screen and create a DoubleAnimation object. This applies the Transform to your element over a specified range of values and time period. In my example, I’m moving from left to right in 10 seconds. Then, I set the size of the logo and begin the animation.

The last lines of code maximize the window and hide the mouse cursor.

Now, if you press F5 in VisualStudio you’ll see your screensaver. There are a few remaining housekeeping issues.

All screensavers have an .scr extension. Compile your code and change the .exe to .scr. Windows will recognize this as a screensaver. The last step is installation. To install your new screensaver, copy it somewhere outside of your source tree. The right-click your file and select “Install”.

Install Screensaver

This was surprisingly hard to discover. If you copy the .scr file to the System32 folder with the other screensavers, you’ll receive a “Shim” error when it runs. I tried to work around this issue several different ways until I saw I could just right click the file and select install. Problem solved.

I’m not covering configuration (/c) or preview (/p) here. Maybe if I make a more complicated screensaver or add options I’ll write a follow-up post. This should get you started though!