In WPF, Silverlight and Windows Phone it is possible to render a visual object into a bitmap using the RenderTargetBitmap. This functionality, that I find pretty basic, was not available for Windows Store applications. Fortunately, Windows 8.1 provides that functionality for Windows Store applications too, through the same RenderTargetBitmap class.

There are some limitations though:

  • it should be used in the code behind (not declared in XAML) because you have to call RenderAsync
  • collapsed visual objects are not rendered (only visible ones)
  • in rare circumstances the content can be lost due to the interaction with lower level systems; in this case a specific exception is triggered
  • the rendered target bitmap does not automatically scale when the current DPI settings change
  • the maximum rendered size of a XAML visual tree is restricted by the maximum dimensions of a DirectX texture

Here is a demo Windows Store application that has several controls and a button that when pressed a screenshot of the area shown in red (it’s a grid) is taken. The bitmap is saved on disk, but also displayed as the source for the image control shown in the preview area.

wsas1

The handler for the Click button even looks like this:

SaveScreenshotAsync is an async method that takes the reference to the FrameworkElement to be rendered to a bitmap (in this case the constrolsGrid) and returns a Task<RenderedTargetBitmap> that can be awaited on. As soon as we have the bitmap we set it as the source for the image control (imagePreview).

wsas2

SaveScreenshotAsync is an async method that takes the FrameworkElement to be rendered to a bitmap and returns a Task<RenderedTargetBitmap> that can be awaited on. This method first prompts the user to select a destination file for the rendered bitmap. When the file is available it calls SaveToFileAsync to rendered the bitmap and write it to the file.

SaveToFileAsync is an async method that takes the FrameworkElement to be rendered to a bitmap and the StorageFile when the bitmap is to be saved and returns a Task<RenderedTargetBitmap> that can be awaited on. The file is opened asynchronous for read-write access and the returned IRandomAccessStream is passed further together with the framework element and the bitmap encoder id (that specifies how the bitmap should be encoded, i.e. BMP, JPEG, PNG, GIF, etc.) to CaptureToStreamAsync.

CaptureToStreamAsync creates a new RenderTargetBitmap object and calls RenderAsync to render the visual tree of the framework element to a bitmap. After the bitmap is rendered it retries the image as a buffer of byes in the BGRA8 format. It then asynchronously creates a BitmapEncoder for the IRandomAccessStream stream that it received as an argument, it calls SetPixelData to set the pixels data (notice the BitmapPixelFormat.Bgra8 parameter that matches the pixels format returned by GetPixelsAsync) and later asynchronously flushes all the image data, basically writing it to the file. It then returns that RenderTargetBitmap object that it created, which is used eventually as the source for the image control.

Here is how the saved JPEG image (also seen in the preview screenshot above) looks likes:
wsas3

You can check the source code of the attached WinRT Screenshot demo (422). It requires Visual Studio 2013 and Windows 8.1.

, , , , , , Hits for this post: 10344 .
Trackback

4 comments untill now

  1. Gravatar

    This is not working for me at all.

    Honestly, I don’t know why folks like classes so much. I have yet to see many working examples of any kind that use classes.

    I miss the good old days that did not involve classes.

    I think classes complicated things too much.

    They are great in theory but they make simple tasks take days and days to fix. :(

    I get 114 errors, starting with:

    Cannot implicitly convert type Windows.Storage.StorageFile to Windows.UI.Xaml.Media.Imaging.RenderTargetBitmap on this line:

    return await filePicker.PickSaveFileAsync();

    inside of PickSaveImageAsync()

    Thanks anyway..
    Roark

  2. Gravatar

    Thank you so much for posting this blog.It Really helped me. It works perfectly. :)

  3. Gravatar

    Roark,

    I think the author has a typo in the code. The line:

    async Task PickSaveImageAsync()

    should be:

    async Task PickSaveImageAsync()

    Give that a try. As for the rest of your errors. For each object type that is underlined in red, Use the “Ctrl” + “.” facility to find and add the missing import library.

  4. Gravatar
    Ruwei Liu @ 2014-05-27 05:00

    Hi, I tried to run the app, text are missing, any thoughts?

Add your comment now