3

I'm trying to render a WPF based object as a PNG inside an ASP.NET image handler. We've created a designer in WPF that creates XAML based templates. Using the XAML serializer to store the templates on disk is simple. Pulling those same serialized XAML based objects and reconstituting them in an ASP.NET HTTP handler has proved difficult.

The XAML deserialization process needs to run on a STA thread. Creating a thread and setting it to STA and doing the deserialization and image composition worked, the first time. I get the reconstituted XAML based image created, passed back and sent to the browser. Subsequent calls to the HTTP handler crash the web server with "The calling thread cannot access this object because a different thread owns it."

Are there any libraries out there that will generate an image from a simple XAML based object?

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
KenBobPDX
  • 31
  • 5

3 Answers3

2

I had answered this a while ago on the MSDN forums, but I'll copy that and tweak here for the prosterity of StackOverflow. :)

The simplest approach is spinning up a new STA thread everytime, letting that process one image and then the thread terminates and everything is cleaned up. That won't really scale so great though as the spin-up costs for the thread as well as all WPF Dispatcher infrastructure setup will add obvious overhead. You would want to look into having a pool of render ready threads that you farm the jobs out to. You should be able to basically spin up threads that just call Dispatcher::Run, and leave them sitting there. When a job comes in, you basically pull a thread out of the pool and call Invoke on the associated Dispatcher instance (you can get this by calling Dispatcher::FromThread) passing a delegate that contains all the rendering logic you want to execute in the context of that thread. When that finishes, the thread will remain running, because of Disptacher::Run call you made earlier, and you can return it to the pool for the next job to use. To clean up threads, just go to them and call Dispatcher::InvokeShutdown.

Drew Marsh
  • 33,111
  • 3
  • 82
  • 100
  • Thanks Drew. I am using a STA thread in a simple configuration as you described. I'll look into having a pool of render ready threads to farm the jobs out to. – KenBobPDX Oct 19 '09 at 22:59
  • Yes, if you hope to scale in any way, it's an absolute must. Good luck! :) – Drew Marsh Oct 19 '09 at 23:17
  • 1
    Thanks again Drew. Can you point me to any examples of such a thread pool? I'm a bit of a neophyte when it comes to managing thread pools. I really appreciate your help. – KenBobPDX Oct 19 '09 at 23:23
  • Let's recurse into Stack Overflow[1]. :) Just keep in mind you need to set your pool's threads to be STA and initialize the Dispatcher on each of them. [1] http://stackoverflow.com/questions/435668/code-for-a-simple-thread-pool-in-c – Drew Marsh Oct 19 '09 at 23:54
  • I've found you don't need to initialize the Dispatcher on the created threads; they get set up automatically. You do need to do manual cleanup on them however. – RandomEngy Oct 20 '09 at 15:44
  • Ah, yeah that's right, I forgot that they are automatically create the Dispatcher for you the first time you call Dispatcher::CurrentDispatcher. – Drew Marsh Oct 20 '09 at 16:02
2

I have some example code in an answer to a similar question. Though as Drew pointed out, you would probably be better off re-using your threads by passing delegates to them. Also I think you could use XamlReader.Parse() instead of programmatic creation.

Community
  • 1
  • 1
RandomEngy
  • 14,931
  • 5
  • 70
  • 113
  • I'm using XamlReader.Parse() for pieces of the template in an observable collection. Excellent example. Thank you very much! – KenBobPDX Oct 19 '09 at 23:14
0

Have you considered creating a WPF shell application that just loads the templates you've created, screen shots them, and then saves the screenshot to disk? Then allow the application to accept command line args and pass in the template you want it to render and the name of the file you want it to generate.

Your web app could then just spin it off as a separate process, wait for it to complete, and grab the file. That way you wouldn't have to worry about running multiple STA threads.

Joseph
  • 25,330
  • 8
  • 76
  • 125