WPF Custom Screen Saver Art

Summary

Creating custom
wallpaper is easy. But what about custom screen savers? This post will detail how to build a custom saver using Windows Presentation Framework (WPF). The posted code also provides some insight on how to create interesting particle effects using WPF's animation
engine, and how to implement multiple monitor support in .Net.

Download

Introduction

Microsoft Unified Communications (UC) is a new technology that has been getting a lot of hype in the past couple of months. UC is essentially the integration of messaging, voice, and video across the
applications and devices that people use every day. More information about UC can be found
here. One of the coolest things about UC is the concept of “presence” (see below). The Presence indicator effectively allows the user to reflect his/her status in other Office Communicator Server friendly
applications (i.e. Outlook, Office communicator, Live Meeting, etc.)

While building out some communicator functionality on another project I found myself recreating the presence “bubbles” in xaml so that they could scale without losing quality (see below). I used Expression Design to re-create the presence bubbles and export
then to xaml.

In doing so it occurred to me that the presence “bubbles” would make for some excellent art sprites.

So after a hop-skip-and a jump to Photoshop I found myself with a shiny new “presence art” wallpaper.

Immediately after creating the wallpaper I thought to myself, “this would also make a great screen saver”. This article will attempt to show how to build a custom WPF screen saver that implements a simple physics particle effect using the WPF animation engine.

Particle Animation

With my graphic designer hat on I knew that I wanted to create a floating particle effect. Particle effects can be used to simulate anything from rain, snow, fire to bubbles and clouds. Then by varying the presence “bubbles” in size and opacity the illusion
of depth and space can be created. After some research I found the CompositionTarget.Rendering event handler in WPF. Essentially, this event is a timer that ticks all the time and fires once each time WPF decides to render a frame. Inside the event handler
you have complete freedom to do whatever you want to any object. In other words, hooking CompositionTarget.Rendering is very much a "do it yourself" animation system. This event is especially good for physics-based animations and is very similar to the way
Flash's rendering engine works. The MSDN entry for this event handler can be found
here.

Once I had found the appropriate animation hook I created the particle user control. This control holds the various particle attributes that will be animated. The code below shows the various properties that can be used to create very interesting physics-based
effects. These properties can be used to calculate collision detection, gravitational pull and other kinetics-related effects.

Once I had the particle class created, I decided to use a variation of a spring physics equation to get the desired float animation. The spring equation forces the particles toward each other and then springs them past each other similar to how a ball on
the end of a rubber band would act when pulling it and letting it spring back and forth. Then by tweaking the force and velocity applied to each particle I successfully simulated a floating effect (I am by no means a physicist and I am sure there is a better
way of implementing this equation). By combining the CompositionTarget.Rendering event and the positioning logic we can effectively scatter and animate the particles on our Canvas. The math involved looks something like this:

Supporting Multiple Monitors

After the animation was rendering correctly the next step was getting it to run on multiple monitors. To make this happen I wrote some code in the application entry point to get a handle to the active screens. The System.Windows.Forms namespace has a Screen
class which contains a collection of screens and their attributes including working area, width, and height. By looping through the screen collection I measure the screen boundaries and set the window size dynamically based on the available screen real estate.

C#

1:privatevoid Application_Startup(object sender, StartupEventArgs e)

2: {

3:

4: Window1 _window = null;

5:

6: System.Windows.Forms.Cursor.Hide();

7:

8:foreach (Screen screen inScreen.AllScreens)

9: {

10:if (screen.Primary)

11: {

12: Rectangle location = screen.Bounds;

13: _window = newWindow1(location.Height, location.Width);

14: _window.Width = location.Width;

15: _window.Height = location.Height;

16: _window.Left = 0;

17: _window.Top = 0;

18: _window.WindowState = WindowState.Maximized;

19: _window.Show();

20: }

21:

22: elseif (!screen.Primary)

23: {

24:

25: Rectangle location = screen.Bounds;

26: _window = newWindow1(location.Height, location.Width);

27: _window.Left = screen.WorkingArea.Left;

28: _window.Top = screen.WorkingArea.Top;

29: _window.Width = location.Width;

30: _window.Height = location.Height;

31: _window.Show();

32: }

33:

34: }

35:

36: }

VB

1:private void Application_Startup(object sender, StartupEventArgs e)

2: {

3:

4: Window1 _window = null;

5:

6: System.Windows.Forms.Cursor.Hide();

7:

8: foreach (Screen screen inScreen.AllScreens)

9: {

10:if (screen.Primary)

11: {

12: Rectangle location = screen.Bounds;

13: _window = newWindow1(location.Height, location.Width);

14: _window.Width = location.Width;

15: _window.Height = location.Height;

16: _window.Left = 0;

17: _window.Top = 0;

18: _window.WindowState = WindowState.Maximized;

19: _window.Show();

20: }

21:

22:elseif (!screen.Primary)

23: {

24:

25: Rectangle location = screen.Bounds;

26: _window = newWindow1(location.Height, location.Width);

27: _window.Left = screen.WorkingArea.Left;

28: _window.Top = screen.WorkingArea.Top;

29: _window.Width = location.Width;

30: _window.Height = location.Height;

31: _window.Show();

32: }

33:

34: }

35:

36: }

Making it a Screen Saver

The last step to this application is making it a screen saver. By implementing mouse and keyboard event handlers to shut down the application we essentially can mimic the interaction of a screen saver.

Lastly, simply set the build configuration to Release mode and build it. In the output bin->Release directory find the built executable. Rename the file extension from .exe to .scr then right click and choose
Install from the context menu.

That's it!

Challenges to You

One of the interesting things about Office Communicator Server is that it comes with an SDK to build your own custom Unified Communications applications.

I would love to see the presence bubbles actually hooked up to real users' status, and maybe a cool animation to indicate when a users' status is changed.

Create a config file to let the user specify his/her own images and tweak other settings like speed and velocity.

Conclusion

This was a great opportunity to brush up on my physics skills and play with some of the animation possibilities in WPF. This application is also a great example of alternative data visualization. Sometimes I get tired of seeing everything in tabular form
and I think that “presence art” is a great example of Art + Code (aesthetically pleasing and functional). I hope you enjoy it.

Remove this comment

Remove this thread

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation,
please create a new thread in our Forums, or
Contact Us and let us know.