Converting and customizing XAML to PNG with server-side WPF

One Tweet by Oliver Sturm (http://twitter.com/olivers) the other day got me to thinking. He was asking about the possibility to host graphics for a web site in XAML format, and the best way to render it.

Currently, there are 3 possible ways that I know of to render XAML in a web browser

  1. Loading the XAML file directly in an IFRAME will trigger Windows Presentation Foundation on the client (if available). The IFRAME can be positioned in the page so the it "blends" with the HTML page. That's only for Windows (IE and Firefox) and requires .NET 3.0 (or preferably 3.5/3.5 SP1) installed on the client. I wouldn't recommend that solution.
  2. Silverlight can load loose XAML files. However, Silverlight XAML is less powerful than WPF XAML. Not all controls and features are supported. Also, it requires a plug-in to be installed on the client. The plug-in is available for a number of platforms, but still. More lightweight than solution 1, but still not ideal to render static images.
  3. Rendering the XAML on the web server. This solution has the advantage to require installation of the .NET framework only on the web server (a controlled environment). WPF can render in memory, which is very convenient. No need for a visual, you can just load the XAML in a stream, and render as a bitmap image it in a memory stream, which in turn can be passed to the Response. Multiple bitmap formats are supported. In this example, we will use PNG.

I really want to write a more in-depth article about that, but for now I already post source code demonstrating 3 possible uses for this technique. To install the demo, follow the steps below.

Note: I am having some issues installing this on my web server, and trying to clear things up now. It works fine locally however.

  1. Download the source code (Zip)
  2. Unzip to a folder on your PC.
  3. Open the Solution file in Visual Studio 2008. Note: This is targetting .NET 3.0.
  4. Make sure that the web application "GalaSoft.Web.ConvertXamlToPng" is set as startup project.
  5. Make sure that the file "index.html" is set as StartUp page.
  6. Run the application by pressing Ctrl-F5.

After 350-001 and VCP-310, many professionals do not register immediately for 646-204 or even 70-649 and wait before they attempt 70-620 and that too after registering with testking.

The demo has 3 parts:

  • Localizing an image: In this sample, some strings are fetched from the resources (using a standard .NET resource manager) and set in the XAML scene after it has been loaded in memory. This solves a recurrent problem for web site managers: How can we design pictures in various languages without having to create (and store) one picture per supported language. In this demo, the same XAML file is localized in 3 different languages, and rendered to PNG.
Localized image, en-US
Localized image, fr-CH
  • Scaling an image: WPF and XAML are great for flow layout and element composition. Using tools like Expression Design and Expression Blend, it is easy to create compelling graphics that resize neatly. The second sample demonstrates this and allows the use to specify any size for an image. The XAML is loaded and rendered to that size directly on the server. Note that if you choose a very large size (thousands of pixels), the image appears pixellated. I am not sure yet what is the reason for it.
Resized image
  • Customizing an image. In this last sample, you can parameterize multiple elements of the XAML scene. It's all a matter of finding the right element after the XAML has been loaded (FindName helps), finding the right DependencyPropertyand reflection are needed) and then setting the right object.
Customized image
Just a word on the technology

One thing that WPF requires when it is being rendered (visually or in memory) is a STA (single threaded apartment). However, ASP.NET pages are typically rendered in MTA (multi threaded appatment). To solve this, you can either force an ASPX web page to run in STA (by using the <%@ ASPCOMPAT="true" %>directive in the Page) but that will degrade the performances. A much better way is to create a worker thread and to render the XAML in this thread.

One problem with that is that WPF is very particular with threads: To put it simply, a visual element created in a thread may not be used in another thread. We run into that problem when we parameterize the SolidColorBrush used by the last sample. In that sample, the replacement brush is created on the main thread, but used in the worker thread which creates an Exception. To avoid this issue, we "freeze" the SolidColorBrush after it is created. Frozen objects cannot be modified anymore, but they can be shared between threads.

What's missing?

Some things are still missing to this demo. I really would like to get it to work on my web server, but I am getting some issues. My understanding so far is that some assemblies are missing on the web server, which creates exceptions.

Another thing I would love to support is zoomability. Since XAML elements are vector graphics, and since ScaleTransform is supported, it is rather easy to add zoomability to an image. I experimented with that already and with good results, but it's not ripe for distribution yet. The problem here is that dimensions get all kind of wacky when you zoom an image (a 500 pixels wide image zoomed with a factor of 200% is suddenly 1000 pixels wide) and I am still looking for the best way to implement this in a generic way.

The XamlToPngConverter class

This is the main component used for the conversion. I won't write too much about it now, but it's implemented in a generic way, so that XAML can be loaded from any stream, and rendered to any stream. This allows for saving static images to a file, for example, rendering to a web response like here, etc... More details will follow.

I hope you like this demo and that it is useful. While it requires .NET 3.0 on the web server (and while it's not as straightforward on remote web servers as I was hoping for), it's a useful technique that I will be playing with more in the close future.

Print | posted on Friday, October 10, 2008 12:48 AM

Feedback

# re: Converting and customizing XAML to PNG with server-side WPF

left by Jeremiah Morrill at 10/10/2008 3:21 AM Gravatar
This is really cool Laurent! You said that it won't work in IIS right now. Do you think if you set the IIS service option "Allow to interact with the desktop" option will work? I had issues in creating hWnds under IIS in the past and I *think* this fixed it (It's been a while).

-Jer

# re: Converting and customizing XAML to PNG with server-side WPF

left by russ at 10/10/2008 6:07 AM Gravatar
hi Laurent, cool article. have you had a look @ the memory consumption on your IIS box? I've been working on a project doing something v. similar: We ended up abandoning this approach and displaying everything in Silverlight - because memory would quickly run out on the server. our WPF did refer to JPG files, so I'm pretty sure that's were the memory leak was.. when I have time I'll grab your source code and see how the memory looks with our XAML - would be VERY nice if I can end up using it.

# re: Converting and customizing XAML to PNG with server-side WPF

left by Laurent at 10/10/2008 4:30 PM Gravatar
@Jer: I am having issues with IIS in general on this box. Our IT department apparently restricted my usage of it without notice. Investigating. On my ISP (discountASP.NET), I get other errors indicating that some codecs DLLs cannot be found. I will check that when I have more time.

@Russ: As you can see this is still pretty rough and need some honing. I didn't run the code in IIS yet, as explained just above. I will investigate more when I can and I'll make sure to post updates. In the mean time, feel free to use the code, I would love it if you can use it. Cheers!

# re: Converting and customizing XAML to PNG with server-side WPF

left by Chris Cavanagh at 10/16/2008 7:58 PM Gravatar
Here's something that might help (or save some typing): http://chriscavanagh.wordpress.com/2008/02/15/wpf-skinning-your-websites/

There's an assembly in there called "XamlCapture". I've not tried using it under IIS, but in theory it should work (unless there are partial/full trust issues). As far as threading goes, I'd say the simplest solution is just maintain a static dictionary of images. Use a ReaderWriterLockSlim around any access to the dictionary; if images don't currently exist, upgrade to a writer lock and generate them. Unless I'm missing something, it shouldn't matter which thread you create and use your WPF elements on (as long as you don't share them between threads). The end result is just a dictionary of byte[] content you can share for all requests...

# re: Converting and customizing XAML to PNG with server-side WPF

left by Laurent at 10/16/2008 8:27 PM Gravatar
Hey Chris,

Thanks for the feedback. In fact I had seen your blog entry about XamlCapture. I wrote my own stuff because I like to play with code :) As you can read here, I am having issues with it on IIS at the moment, so I will definitely test with your library. I am just a bit too busy right now but it's high on my list.

Thanks!
Laurent

# re: Converting and customizing XAML to PNG with server-side WPF

left by Josh Smith at 10/21/2008 10:05 PM Gravatar
Nice solution! Thanks for sharing it.

Josh

# re: Converting and customizing XAML to PNG with server-side WPF

left by Charles at 10/23/2008 1:02 AM Gravatar
Rendering out vector to PNG was something we desperately wanted for our Flash files when we developed www.dudefactory.com (now messdudes.com). In the end we just saved out all the SWFs as PNGs from Flash and composited them all using .NET.

If Silverlight had been around then we'd have done it all in XAML for sure.

# Issue with IIS7 and .net framework 3.5 sp1

left by Bryan Livingston at 11/10/2008 6:49 PM Gravatar
This will break with .net framework 3.5 sp1.

See: http://forums.asp.net/t/1299963.aspx

# re: Converting and customizing XAML to PNG with server-side WPF

left by Laurent at 11/10/2008 7:13 PM Gravatar
Hey Bryan,

Thanks for pointing the thread to me. I am also having issues running this on IIS, but I never had time to look further into this (with PDC and my book being just published, my plate was quite full). I will contact David Teitlebaum and follow up. We met at PDC and he seems a very knowledgeable (and nice) fellow.

I'll follow up on my blog.

Cheers,
Laurent

# re: Converting and customizing XAML to PNG with server-side WPF

left by slyi at 1/12/2009 6:42 PM Gravatar
Fix avaialble on
http://www.microsoft.com/downloads/details.aspx?familyid=b9de7937-2c12-4f16-ad66-a31b83931953&displaylang=en

Could this be used in silverlight screenshoting but i was think a little different approach, would this be more performent?

1. Use http://silverlightcontrib.org/ xamlwriter or VisualTreeHelper (since the xaml is not directly serializable) to send the client xaml back to the server
2. On the server create a winform WebBrowser object and add a silverlight object control and inject posted xaml
3. Take a screenshot of the result and send back to client

Of course you would need have the correct <base href=””> on the server side based silverlight page etc…

# re: Converting and customizing XAML to PNG with server-side WPF

left by Johnson at 2/5/2009 5:06 PM Gravatar
Hi:

How about in the xaml string you embed a rotated image, how you get the image also shows up when converting xaml to png.

# re: Converting and customizing XAML to PNG with server-side WPF

left by Bruce Pierson at 3/17/2009 11:04 PM Gravatar
Here's a much simpler way to get a System.Windows.Media.Color from a string (in your WpfUtility class, MakeColor method):

TypeConverter tc = TypeDescriptor.GetConverter(typeof(System.Windows.Media.Color));
return tc.ConvertFromString(colorNameOrCode);


# re: Converting and customizing XAML to PNG with server-side WPF

left by Laurent at 3/23/2009 2:08 PM Gravatar
Very nice trick, thanks Bruce.

Every day we learn new things :) You are my new thing for the day LOL

# re: Converting and customizing XAML to PNG with server-side WPF

left by Sasha at 4/30/2009 1:12 AM Gravatar
Thanks for good article!

I used your ideas for Silverlight -> XAML -> WPF -> PNG printing.

# re: Converting and customizing XAML to PNG with server-side WPF

left by Tatiana at 5/19/2010 9:54 AM Gravatar
I have a valid XAML call tarea.xaml but It doesnt work only shows XXXX description here is the code

---------------------------------------------------
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Canvas Width="auto" Height="auto" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<TextBlock Text="XXXXXXX" Width="125" Height="37.5" Padding="38,16,1,0">
<TextBlock.Background>
<VisualBrush>
<VisualBrush.Visual>
<Rectangle Width="125" Height="37.5" Stroke="#FF000000" RadiusX="20" RadiusY="20" Canvas.Left="262.5" Canvas.Top="525">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFFFFFFF" Offset="0" />
<GradientStop Color="DodgerBlue" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</VisualBrush.Visual>
</VisualBrush>
</TextBlock.Background>
</TextBlock>

</Canvas>
-----------------------------------------------------
I don´t know why
I would apreciate your help

# re: Converting and customizing XAML to PNG with server-side WPF

left by Damian Powell at 9/20/2011 3:05 PM Gravatar
This is a really neat piece of code. Nice one, Laurent.

# re: Converting and customizing XAML to PNG with server-side WPF

left by Neil Barnwell at 9/22/2011 2:28 PM Gravatar
Hey Laurent - this is excellent stuff and I'm in the process of trying it out at the moment. The only thing is, binding expressions in the xaml don't seem to be invoked. If I put the FrameworkElement I get back from XamlReader.Load() into a container on a WPF window it all works fine, but using your code above to render straight to a png does not seem to invoke the bindings properly. Is there something I can do to force this to happen?

# re: Converting and customizing XAML to PNG with server-side WPF

left by Neil Barnwell at 9/22/2011 3:53 PM Gravatar
Hi again!

I just wanted to post an update to mention that I think I've worked out a solution. I posted a question on stackoverflow and then posted my own answer when I worked it out for myself shortly thereafter. Here's the link in case it's of interest:

http://stackoverflow.com/questions/7515360/wpf-xaml-bindings-seemingly-not-applied-to-dynamically-loaded-xaml

# re: Converting and customizing XAML to PNG with server-side WPF

left by Mark Foreman at 11/16/2011 11:35 PM Gravatar
Laurent, your code is fantastic, though we've been plagued by massive unmanaged memory leaks. Turns out that the rendering needs to run on a Dispatcher thread. Check out Josh Twist's post about it here:

http://www.thejoyofcode.com/Generating_images_using_WPF_on_the_Server.aspx

The also include a call to UpdateLayout(), which enables binding support (thanks Neil!), and they have slightly simplified the implementation of RenderToBitmap (no image brush needed).

# re: Converting and customizing XAML to PNG with server-side WPF

left by Laurent at 11/20/2011 7:34 PM Gravatar
Hey Mark,

Thanks! Happy you found a use for it (and a solution to the leaks). It was pretty much experimental code for me, so I didn't investigate much deeper :)

Cheers,
Laurent

# re: Converting and customizing XAML to PNG with server-side WPF

left by Dirkster99 at 7/19/2012 10:23 AM Gravatar
I took your code and used it in a command line tool that solves some of the to dos you mentioned above. Please leave a comment on the code project web-site site.

# re: Converting and customizing XAML to PNG with server-side WPF

left by David Egli at 2/9/2013 8:39 PM Gravatar
We've implemented a tool for VisualStudio that implements all this and more, initially based on the code posted by Chris Cavanagh above.
The tool can create images, animated gifs, PDF's, html image maps from xaml, svg & photoshop, upon project build, or on the fly in ASP.NET through an HttpHandler. You can even include xaml markup in an aspx page, and render it as an image in the page.

Greetings & Blessings,

David Egli

# re: Converting and customizing XAML to PNG with server-side WPF

left by David Egli at 2/9/2013 8:40 PM Gravatar
Uups, forgto to say, the tool is at xamlimageconverter.codeplex.com
Comments have been closed on this topic.