(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Capturing a Silverlight Screen

(4 votes)
Phil Middlemiss
>
Phil Middlemiss
Joined Mar 02, 2010
Articles:   3
Comments:   10
More Articles
7 comments   /   posted on Jun 21, 2010
Categories:   General , Media

This article is compatible with the latest version of Silverlight.


Introduction

In this article I will show you how to capture a “screenshot” of a Silverlight application. Fortunately, since Silverlight 3, this is straight-forward to do with the WriteableBitmap class. I’m also going to show how to use the screen capture for special effects, or for saving to file as a JPG. This article is aimed at developers who are fairly new to Silverlight.

Here is a Silverlight app that demonstrates the approach we are going to use:

 

You can grab the source here.

The “Swap” button uses a screen capture to create a fun transition between two different views. The “Capture Filmstrip” button starts a timer that captures the screen every second and shows a thumbnail view of it. The “Animate Background” button plays a supporting role for the “Capture Filmstrip” button; it animates the background so the filmstrip captures are different from each other even if you don’t interact with the controls. You can click on one of the captured thumbnails to save it to disk.

Capturing the Screen

Silverlight does not provide any native screen capture capabilities that you would have in a desktop app using GDI routines. Instead Silverlight provides the Render method on the WriteableBitmap class. The Render method takes a UIElement as one of its parameters and will render the passed element to the WriteableBitmap. This is what it looks like in code:

 // create a WriteableBitmap
 WriteableBitmap bitmap = new WriteableBitmap(
     (int)this.LayoutRoot.ActualWidth,
     (int)this.LayoutRoot.ActualHeight);
  
 // render the visual element to the WriteableBitmap
 bitmap.Render(this.LayoutRoot, this.transform);
  
 // request an redraw of the bitmap
 bitmap.Invalidate();

In the code above, the ActualWidth and ActualHeight of the LayoutRoot (the grid at the top of the visual tree) are used as the size for a new WriteableBitmap. The bitmap.Render method is then called to render the element.

The following points should be remembered when using the WriteableBitmap.Render() method:

  • You must follow up a call to Render() with a call to Invalidate() in order to cause a redraw on the WriteableBitmap.
  • If the size of the WriteableBitmap is smaller than the size of the element being rendered then the element will be clipped.
  • You can pass a Transform to the Render method to scale, rotate, skew, and/or translate the element being rendered.
  • The UIElement being rendered must usually be part of the visual tree in order to be rendered correctly – but you can render a UIElement that is not in the visual tree if you call Measure and Arrange on it first, which gives it a chance to be laid out correctly.

If you wanted to create a double sized screen capture you could create a WriteableBitmap that is twice as wide and twice as high as the target UIElement, and pass a ScaleTransform with the ScaleX and ScaleY properties set to 2.

The demo app uses this approach in two different places:

  1. To capture the entire visual tree for the “Swap” transition
  2. To capture a part of the visual tree for the “film strip” thumbnails

So once you have the screen captured to a WriteableBitmap, then what? Here are two possibilities I’ll explore in detail: Saving to disk as an image file, or using the bitmap as the source for an image to create special effects.

Saving the Screen Capture to Disk

Silverlight 4 provides access to the Clipboard, but does not allow anything other than text to be copied to and from the clipboard from within a Silverlight app. This means that we can’t transfer a screen capture to the clipboard, but then there’s always the “PrtScr” button for that.

We can, however, capture the screen and save the image to disk. To do that we need to prompt the user for a location to save it to, and we need to convert it from a WriteableBitmap to an image format such as JPG.

The only way to access the user’s hard drive is via Local Storage or through the SaveDialog and OpenDialog. An Out Of Browser application with elevated privileges has access to a few more areas such as Pictures etc, but in the demo app I use the SaveDialog which lets the user choose anywhere on their hard drive to save a file.

The demo app uses a timer that fires every second (when it is enabled) and captures the screen to one of the thumbnail images. Here is the code for the method that saves the WriteableBitmap as a JPG when you click on one of the captured thumbnails: 

 private void ThumbnailClicked(object sender, MouseButtonEventArgs e)
 {
     // pause the capture timer
     this.timer.Stop();
     try
     {
         // locate the WriteableBitmap source for the clicked image
         WriteableBitmap bitmap = ((Image)sender).Source as WriteableBitmap;
         if (null == bitmap)
         {
             MessageBox.Show("Nothing to save");
             return;
         }
  
         // prompt for a location to save it
         if (this.dialog.ShowDialog() == true)
         {
             // the "using" block ensures the stream is cleaned up when we are finished
             using (Stream stream = this.dialog.OpenFile())
             {
                 // encode the stream
                 JPGUtil.EncodeJpg(bitmap, stream);
             }
         }
     }
     finally
     {
         // restart the capture timer
         this.timer.Start();
     }
 }

In the code above I grab the Source of the Image control that was clicked and extract its Source as a WriteableBitmap. The check for null is there because I only create the WriteableBitmap for each thumbnail the first time it is needed. I stop the timer before showing the SaveDialog so that the thumbnail image hasn’t changed by the time the user has selected a location and clicked Save.

The SaveDialog is very careful about how much of the host computer it exposes to your code – basically it only exposes just enough for you to write something to a location specified by the user. It doesn’t even give you the path information for the location the user selects. The call to SaveDialog.OpenFile() returns a stream that you can use for writing to and that is about all you can do with the SaveDialog.

I’ve used the FJCore library for converting to a JPG (distributed under the MIT License). The code in the sample was adapted from code found in a Stack Overflow Forum by RHLopez.

Using the Screen Capture for Special Effects

The WriteableBitmap can be used to generate special effects or transition effects. In the demo above I use it to bounce the current view into the middle of the screen, revealing the new view. The general approach is as follows:

  1. Take a screenshot of the current view using WriteableBitmap.Render().
  2. Use the WriteableBitmap as the source for an Image sitting on top of all other controls
  3. Change the view behind the Image.
  4. Manipulate the Image or the WriteableBitmap pixels to reveal the new view

In the demo, I have an Image sitting on top of all the other controls with its IsHitTestVisible property set to false. When the “Swap” button is clicked the following code is executed:

 private void SwapScreens(object sender, RoutedEventArgs e)
 {
     // render the current state of the visual root to the WriteableBitmap
     this.bitmap.Render(this.LayoutRoot, this.transform);
             
     // request a redraw of the bitmap
     this.bitmap.Invalidate();
 
     // change the current state of the visual root
     if (this.redShowing)
     {
         this.contentFrame.Source = new Uri("/WriteableBitmapScreenCapture;component/Page1.xaml", UriKind.Relative);
         VisualStateManager.GoToState(this, "BlueBackgroundVisible", false);
     }
     else
     {
         this.contentFrame.Source = new Uri("/WriteableBitmapScreenCapture;component/Page2.xaml", UriKind.Relative);
         VisualStateManager.GoToState(this, "RedBackgroundVisible", false);
     }
   
     // play the transition animation storyboard
     this.redShowing = !this.redShowing;
     this.swapStory1.Begin();
 }

“this.bitmap” refers to a private WriteableBitmap assigned as the source for the Image sitting on top of all the other controls. The WriteableBitmap is regenerated whenever the MainPage UserControl fires a SizeChanged event:

 void MainPageSizeChanged(object sender, SizeChangedEventArgs e)
 {
     if (e.NewSize.Width > 0)
     {
         // create a bitmap for the "swap" transition, using the new size of the layout root
         this.bitmap = new WriteableBitmap(
             (int)this.LayoutRoot.ActualWidth, (int)this.LayoutRoot.ActualHeight);
                 
         // set the source of our transition image to the WriteableBitmap
         this.screenGrab.Source = bitmap;
     }
 }

I can’t use the LoadedEvent because it occurs before the MainPage has been laid out; the ActualHeight and ActualWidth properties are still 0. The SizeChanged event is the first sensible opportunity to create the WriteableBitmap at the right size. If this was a resizable app I would defer the (re)creation of the WriteableBitmap until the screen capture was actually needed, otherwise it could consume a lot of resources if the browser window was resized. But in the demo I have a fixed width and height and I’m simply trying to do all the work possible before the SwapScreens method is called.

Further Reading

  • The WriteableBitmap offers a lot of possibilities for special effects. Check out the excellent WriteableBitmapEx project on CodePlex – it provides a set of extensions to WriteableBitmap to make it even more powerful.
  • Rick Barraza has a fantastic three part series (part 1, part 2, part 3) on advanced render techniques using WriteableBitmap.
  • Joe Stegman has a sample PNG generator in case you prefer PNGs to JPGs.
  • Adam Kinney has a post about Image Blitting with WriteableBitmapEx.

Summary

In this article I’ve shown how to use the WriteableBitmap to capture all or part of the visual tree in a Silverlight application. I’ve then looked in detail at using that captured view to either save a JPG image to disk, or create a transition effect from one view to another.


Subscribe

Comments

  • -_-

    RE: Capturing a Silverlight Screen


    posted by yhali.com on Jun 22, 2010 12:01

    the  source code not working

     

    is there any direct way to do this all silver light page

  • PhilM

    RE: Capturing a Silverlight Screen


    posted by PhilM on Jun 22, 2010 23:49
    The source code download is for Expression Blend 4. You will need to have installed either Expression Blend, or the free Expression Blend 4 SDK to compile and run the demo in Visual Studio 2010.
  • -_-

    RE: Capturing a Silverlight Screen


    posted by Sebastian on Jun 27, 2010 16:09
    Hi, I just want to recommend to use another silverlight library called imagetools: http://imagetools.codeplex.com for saving bitmaps to different formats.
  • AdityaRaghuwanshi

    Re: Capturing a Silverlight Screen


    posted by AdityaRaghuwanshi on Sep 30, 2011 03:18

    is there a way to capture a screenshot from another webpage

  • PhilM

    Re: Capturing a Silverlight Screen


    posted by PhilM on Sep 30, 2011 05:38

    I think so. I have never tried it, but I believe Silverlight's HTML Bridge (see this article) should give you a way to call managed code from JavaScript. I think the two windows would have to be from the same security domain for it to work, but assuming you already have your two pages talking to each other then you should be able to get it working.

  • AnilPerugu

    Re: Capturing a Silverlight Screen


    posted by AnilPerugu on Dec 19, 2011 12:49

    This articles shows a way to encode the JPEG and transfer it to WCF service method

    Saving the captured Silverlight 4 screenshot to database or send it as an email attachment



  • ArielPieiro

    Re: Capturing a Silverlight Screen


    posted by ArielPieiro on Mar 12, 2012 19:08
    as I can do to save an image but complete control not only what you see

Add Comment

Login to comment:
  *      *