WPF Screen Saver Visual Studio Template!

I thought it would be fun to create a WPF screen saver.  I built one with Robert Hogue‘s most excellent Material Group “Background Animation” visuals.  (You can find it in his Sample Pack.)  You can download the screen saver and/or the code.

However, I thought it would be even more fun if everyone else would write screen savers too.  :) To facilitate this, I’ve written a VS template for WPF screen savers.

Screen Saver Template – Installation Instructions

  1. Download the template here.
  2. Move the zip file to <MY DOCUMENTS>Visual Studio 2005TemplatesProjectTemplatesVisual C#”..  (DO NOT unzip it)

When creating a new project, “Screen Saver Application (WPF)” will now be an “Visual C#” project type option.

 

Deploying the Screen Saver

In order to deploy your screen saver, you must rename the RELEASE build of the screen saver executable to have a “.scr” extension:

  1. Go to binRelease folder of your project.
  2. Rename the .exe to .scr.  (e.g.”ScreenSaver.exe” becomes “ScreenSaver.scr”)
  3. If using .NET Application Settings, see important note below.

Installing the Screen Saver

To install on a user’s machine:

  1. Copy the .scr file (& any dependent files) to convenient location on your C: drive.
  2. Right click the .scr file.
  3. Select Install.

To configure on a client machine:

  1. In the Windows Screen Saver Dialog, select the screen saver.
  2. Click Settings button.

To uninstall:

  1. Delete .scr file.

Screen Saver Files

The template creates the following main files for you:

  1. App.xaml/App.xaml.cs – sets up screen saver application.
  2. Window1.xaml/Window1.xaml.cs – main visuals of screen saver
  3. Settings.xaml/Settings.xaml.cs – settings window of screen saver

Screen Saver Modes

The screen saver can be launched with different command line arguments:

  • <no args>: Display screen saver
  • “/s”:  Display screen saver
  • “/c”:  Show settings window

Debugging

In DEBUG configurations, the screen saver window’s “topmost”-ness and “shutdown on key/mouse input” are disabled. To close the window, hit Ctl-F4.

To debug the settings window…

  1. Go to the project properties pane. (Right click the project in the Solution Explorer
    & select “Properties”).
  2. Select Debug on the left tabs.
  3. Find “Command Line Args” textbox under “Start Options”.
  4. Enter: /c

Saving Screen Saver Settings

I highly recommend using the .NET Application Settings feature to save user-configured screen saver settings.

Here’s the quick run down of how to do it.

  1. Determine which settings you need.  The default settings store only supports limited Type, so make sure to use an appropriate format.
  2. Add the settings to the Settings.settings file using the designer.  By default, these are saved to a per user, file system-based .NET settings store.
  3. Access the settings using Properties.Settings.Default.YourSettingName.

For the screen saver above, I defined one setting in the designer:

In my Settings.xaml.cs file, I accessed it:

 //initialize color picker with BackgroundColor application setting
TypeConverter colorTypeConverter = TypeDescriptor.GetConverter(typeof(Color));
MyColorPicker.Color = (Color) colorTypeConverter.ConvertFrom(Properties.Settings.Default.BackgroundColor) ;

As well as saved it:

//set BackgroundColor application setting to color picker's color
TypeConverter colorTypeConverter = TypeDescriptor.GetConverter(typeof(Color));
Properties.Settings.Default.BackgroundColor = colorTypeConverter.ConvertTo(MyColorPicker.Color, typeof(string)) as string;
            
//flush application settings
Properties.Settings.Default.Save();

Finally, I renamed “MaterialGroupScreenSaver.exe” to “Spiral.scr.”

IMPORTANT NOTE:

By default, the .NET Application Settings framework stores user settings based on the executing assembly name.   If you use .NET Application Settings with the default settings store,
YOU MUST GIVE YOUR SCREEN SAVER ASSEMBLY A NAME WITH 8 CHARACTERS OR LESS.

Why? The Windows Screen Saver Dialog launches the screen saver (for settings & preview) using the full assembly name (e.g. MyCoolScreenSaver.scr).  However, when launching the screensaver for real, Windows uses the shortened version (e.g. MYCOOL~1.SCR). Since the name is different, the settings are loaded from a different place.

 

Release Notes

  • Does not support real time preview in the Windows Screen Saver Dialog’s embedded display.
  • The screen saver duplicates the visuals on each monitor by creating an additional instances of Window1.

14 comments

  1. Not alot of respones, but man, this is great! You are the first who managed to do this, and believe me, it will find a good use. It was exactly what I was looking for. Now the purple Aurora XAML file can finally be used as screensaver! thx

  2. Hey, nice job!

    I was wondering if you could help me out here. I am using your screensaver sample for a wpf screensaver I am working on. I am using the application settings as recommended by you, but I was working how would I read the application settings into the XMAL data? I would like to be able to change the speed ratio value.

    - Regards

    Dan

  3. When I try to open a screensaver project in VS 2005, I get the message:

    The project file … cannot be opened. The project type is not supported by this installation.

    Is there something I’m missing? I installed the template already.

  4. Karen,

    I compiled the project using VS2005. However, after rename to clear.scr file, right click did not show install option. What part I did not follow correctly? I am using XP.

    Thanks

  5. Looks like this template works for VS2008 now. Good job! I’m writing a bubbles screensaver for my toddler to play with.

  6. Window1.xaml in Visual Studio 2008 throws an exception
    Cannot convert string ‘pack://application:,,,/Microsoft.Windows.Design.Developer;component/Images/Fit_To_Window.png’ in attribute ‘Source’ to object of type ‘System.Windows.Media.ImageSource’. Bitmap color context is not valid. Error at object ‘ControlTemplate_11′ in markup file ‘Microsoft.Windows.Design.Developer;component/themes/ZoomControl.xaml’.
    at System.Windows.Markup.XamlParseException.ThrowException(String message, Exception innerException, Int32 lineNumber, Int32 linePosition, Uri baseUri, XamlObjectIds currentXamlObjectIds, XamlObjectIds contextXamlObjectIds, Type objectType)
    at System.Windows.Markup.XamlParseException.ThrowException(ParserContext parserContext, Int32 lineNumber, Int32 linePosition, String message, Exception innerException)
    at System.Windows.Markup.XamlTypeMapper.ParseProperty(Object targetObject, Type propType, String propName, Object dpOrPiOrFi, ITypeDescriptorContext typeContext, ParserContext parserContext, String value, Int16 converterTypeId)
    at System.Windows.Markup.OptimizedTemplateContent.ParseDependencyProperty(String attribValue, Int16 attributeId, Int16 converterTypeId, DependencyProperty& dp, Object& propertyValue)
    at System.Windows.Markup.OptimizedTemplateContent.LookForShareableRecord(BamlRecord bamlRecord, DependencyProperty& dp, Object& dpValue)
    at System.Windows.Markup.OptimizedTemplateContent.ReadPotentiallyShareableRecord(BamlRecord bamlRecord)
    at System.Windows.Markup.OptimizedTemplateContent.ReadRecord(BamlRecord bamlRecord)
    at System.Windows.Markup.OptimizedTemplateContent.AddContentRecord(BamlRecord bamlRecord)
    at System.Windows.Markup.TemplateBamlRecordReader.AddContentRecord(BamlRecord bamlRecord)
    at System.Windows.Markup.TemplateBamlRecordReader.ReadRecord(BamlRecord bamlRecord)
    at System.Windows.Markup.BamlRecordReader.Read(Boolean singleRecord)
    at System.Windows.Markup.TemplateTreeBuilderBamlTranslator.ParseFragment()
    at System.Windows.Markup.TreeBuilder.Parse()
    at System.Windows.Markup.XamlTemplateSerializer.ConvertBamlToObject(BamlRecordReader reader, BamlRecord bamlRecord, ParserContext context)
    at System.Windows.Markup.BamlRecordReader.ReadElementStartRecord(BamlElementStartRecord bamlElementRecord)
    at System.Windows.Markup.StyleBamlRecordReader.ReadElementStartRecord(BamlElementStartRecord bamlElementRecord)
    at System.Windows.Markup.BamlRecordReader.ReadRecord(BamlRecord bamlRecord)
    at System.Windows.Markup.StyleBamlRecordReader.ReadRecord(BamlRecord bamlRecord)
    at System.Windows.Markup.BamlRecordReader.Read(Boolean singleRecord)
    at System.Windows.Markup.StyleTreeBuilderBamlTranslator.ParseFragment()
    at System.Windows.Markup.TreeBuilder.Parse()
    at System.Windows.Markup.XamlStyleSerializer.ConvertBamlToObject(BamlRecordReader reader, BamlRecord bamlRecord, ParserContext context)
    at System.Windows.Markup.BamlRecordReader.ReadElementStartRecord(BamlElementStartRecord bamlElementRecord)
    at System.Windows.Markup.BamlRecordReader.ReadRecord(BamlRecord bamlRecord)
    at System.Windows.Markup.BamlRecordReader.ReadElement(Int64 startPosition, XamlObjectIds contextXamlObjectIds, Object dictionaryKey)
    at System.Windows.ResourceDictionary.CreateObject(Int32 valuePosition, Object key)
    at System.Windows.ResourceDictionary.RealizeDeferContent(Object key, Object& value, Boolean& canCache)
    at System.Windows.ResourceDictionary.GetValueWithoutLock(Object key, Boolean& canCache)
    at System.Windows.ResourceDictionary.GetValue(Object key, Boolean& canCache)
    at System.Windows.ResourceDictionary.GetValueWithoutLock(Object key, Boolean& canCache)
    at System.Windows.ResourceDictionary.GetValue(Object key, Boolean& canCache)
    at System.Windows.ResourceDictionary.get_Item(Object key)
    at System.Windows.DeferredResourceReference.GetValue(BaseValueSourceInternal valueSource)
    at System.Windows.DependencyPropertyChangedEventArgs.get_NewValue()
    at System.Windows.FrameworkElement.OnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
    at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, OperationType operationType)
    at System.Windows.StyleHelper.ApplyTemplatedParentValue(DependencyObject container, FrameworkObject child, Int32 childIndex, FrugalStructList`1& childRecordFromChildIndex, DependencyProperty dp, FrameworkElementFactory templateRoot)
    at System.Windows.StyleHelper.InvalidatePropertiesOnTemplateNode(DependencyObject container, FrameworkObject child, Int32 childIndex, FrugalStructList`1& childRecordFromChildIndex, Boolean isDetach, FrameworkElementFactory templateRoot)
    at System.Windows.StyleHelper.LoadOptimizedTemplateContent(DependencyObject container, ParserContext parserContext, OptimizedTemplateContent optimizedTemplateContent, FrameworkTemplate frameworkTemplate, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)
    at System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)
    at System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)
    at System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)
    at System.Windows.FrameworkElement.ApplyTemplate()
    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
    at System.Windows.UIElement.Measure(Size availableSize)
    at MS.Internal.Designer.Viewport.MeasureOverride(Size availableSize)
    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
    at System.Windows.UIElement.Measure(Size availableSize)
    at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
    at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
    at System.Windows.Controls.ScrollContentPresenter.MeasureOverride(Size constraint)
    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
    at System.Windows.UIElement.Measure(Size availableSize)
    at System.Windows.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
    at System.Windows.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)
    at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
    at System.Windows.UIElement.Measure(Size availableSize)
    at System.Windows.Controls.ScrollViewer.MeasureOverride(Size constraint)
    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
    at System.Windows.UIElement.Measure(Size availableSize)
    at System.Windows.Controls.Grid.MeasureOverride(Size constraint)
    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
    at System.Windows.UIElement.Measure(Size availableSize)
    at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
    at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
    at System.Windows.UIElement.Measure(Size availableSize)
    at System.Windows.Controls.Control.MeasureOverride(Size constraint)
    at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
    at System.Windows.UIElement.Measure(Size availableSize)
    at System.Windows.Interop.HwndSource.SetLayoutSize()
    at System.Windows.Interop.HwndSource.set_RootVisualInternal(Visual value)
    at System.Windows.Interop.HwndSource.set_RootVisual(Visual value)
    at MS.Internal.Designer.VSIsolatedDesigner.ViewHolder.Initialize()
    at MS.Internal.Designer.VSIsolatedDesigner.ViewHolder..ctor(UIElement content)
    at MS.Internal.Designer.VSIsolatedDesigner.VSIsolatedView.get_ViewHandle()
    at MS.Internal.Host.Isolation.IsolatedView.get_ViewHandle()
    at MS.Internal.Designer.DesignerPane.LoadDesignerView()

    Bitmap color context is not valid.
    at MS.Internal.HRESULT.Check(Int32 hr)
    at System.Windows.Media.ColorContextHelper.OpenColorProfile(IntPtr pProfile)
    at System.Windows.Media.ColorContext.FromRawBytes(Byte[] data)
    at System.Windows.Media.ColorContext.FromStream(Stream stm, String filename)
    at System.Windows.Media.ColorContext.Initialize(Uri profileUri, Boolean isStandardProfileUriNotFromUser)
    at System.Windows.Media.ColorContext..ctor(PixelFormat pixelFormat)
    at System.Windows.Media.Imaging.BitmapSource.CreateCachedBitmap(BitmapFrame frame, BitmapSourceSafeMILHandle wicSource, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, BitmapPalette palette)
    at System.Windows.Media.Imaging.BitmapFrameDecode.FinalizeCreation()
    at System.Windows.Media.Imaging.BitmapFrameDecode..ctor(Int32 frameNumber, BitmapSourceSafeMILHandle sourceHandle, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, BitmapDecoder decoder)
    at System.Windows.Media.Imaging.BitmapDecoder.SetupFrames(BitmapDecoder decoder, ReadOnlyCollection`1 frames)
    at System.Windows.Media.Imaging.BitmapDecoder.get_Frames()
    at System.Windows.Media.Imaging.BitmapFrame.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy)
    at System.Windows.Media.ImageSourceConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
    at System.ComponentModel.TypeConverter.ConvertFromString(ITypeDescriptorContext context, CultureInfo culture, String text)
    at System.Windows.Markup.XamlTypeMapper.ParseProperty(Object targetObject, Type propType, String propName, Object dpOrPiOrFi, ITypeDescriptorContext typeContext, ParserContext parserContext, String value, Int16 converterTypeId)

  7. Please help!
    Description:
    Stopped working

    Problem signature:
    Problem Event Name: CLR20r3
    Problem Signature 01: wpf_sc~1.scr
    Problem Signature 02: 1.0.3208.17600
    Problem Signature 03: 48f31900
    Problem Signature 04: PresentationCore
    Problem Signature 05: 3.0.0.0
    Problem Signature 06: 470bc96a
    Problem Signature 07: 4be
    Problem Signature 08: 5
    Problem Signature 09: System.IO.FileFormatException
    OS Version: 6.0.6001.2.1.0.768.3
    Locale ID: 1033

    Read our privacy statement:
    http://go.microsoft.com/fwlink/?linkid=50163&clcid=0×0409

  8. This is such a great tutorial. Thanks!

    One question: How can I automatically have the exe file renamed to a .scr and placed in the appropriate directory (or install automatically). I don’t want to force novice users to need to go through manual steps to get the screensaver installed.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>