views:

1901

answers:

1

I'm taking Silverlight XAML and sending it to a web service that renders the XAML in a STA thread and generates a PNG image. All the XAML renders correctly except the <image> entries which 'appear' to load correctly when their Source property is set in WPF but the PNG does not show the referenced image - what am I doing wrong ?

The core of the code that I am using is as below. The value object is a DTO from Silverlight that contains the XAML in the string that ends up as the sXAML local property and the Image URI is in the value.ImageURL property.

  var canvas = (FrameworkElement)XamlReader.Load(new XmlTextReader(new StringReader(sXAML)));

  var obj = canvas.FindName("BGImage");
  Image img = null;
  if (obj != null)
  {
    img = obj as Image;
    img.ImageFailed += img_ImageFailed;
    img.Source = new BitmapImage(new Uri(value.ImageURL, UriKind.Absolute));
  }

  canvas.Arrange(new Rect(new Size(463d, 381d)));
  canvas.UpdateLayout();

  var mem = new MemoryStream();
  var bmp = new RenderTargetBitmap(463, 381, 96d, 96d, PixelFormats.Pbgra32);
  bmp.Render(canvas);

  var encoder = new PngBitmapEncoder();
  encoder.Frames.Add(BitmapFrame.Create(bmp));
  encoder.Save(mem);

  FileStream fs = new FileStream("D:\\out.png", FileMode.Create);
  mem.WriteTo(fs);
  fs.Close();

NB: The img_ImageFailed event handler is never invoked indicating that the img Source assignment was successful in some way.

+2  A: 

Things I would try:

1) In your WPF app, if you 'render' your dynamically loaded Canvas to display in a Window, does it all work (images included)?

2) Have you tried attaching a Loaded handler to the img object to see when the image is actually loaded?

3) Assuming #1 works - where are the images located (are they on the internet/local web server)? If you put a breakpoint in the code on

bmp.Render(canvas);

and wait a while before stepping on - does the image then appear in the rendered output?

I suspect the image is being 'downloaded' asynchronously and you are rendering the Canvas too early, before the Image object has resolved its source.

[UPDATE 29-Jan-09] possible solution

I copied you code down exactly and gave it a try. When I used a 'local' image location (eg. "c:\images\splash.jpg") the image was rendered fine in the output jpeg. When I used a URL (eg. "http://localhost/images/splash.jpg") it did NOT appear (as you describe).

So I modified your code as follows (to try and FORCE the image to be downloaded - will only work for http: image references) --

if (obj != null)
{
    img = obj as Image;
    // new stuff
    BitmapImage bi = new BitmapImage();
    bi.BeginInit();
    bi.StreamSource = getCachedURLStream(new Uri(ImageURL));
    bi.EndInit();
    img.BeginInit();
    img.Source = bi;
    img.EndInit();
    //img.ImageFailed += img_ImageFailed;
    //img.Loaded += new RoutedEventHandler(img_Loaded);
    //img.Source = new BitmapImage(new Uri(ImageURL, UriKind.Absolute));
}
public static Stream getCachedURLStream(Uri url)
{
    HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(url);
    WebResponse response;
    webrequest.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Default);
    response = webrequest.GetResponse();
    Stream s = response.GetResponseStream();
    BufferedStream bs = new BufferedStream(s, 8192);
    return bs;
}

(the getCachedURLStream method is from synchronous image loading - it's kinda extraneous but I just wanted a quick chunk of code to test) and it seemed to then work (image visible in JPEG). Maybe that will work in your scenario?

CraigD
If you don't cast the return of WebRequest.Create(url) to a HttpWebRequest it will work for local addresses ("file://c:/foo.png") or apparently new Uri("@"C:\temp\foo.png"). Also of note: the webrequest.CachePolicy line is required, at least in the context I need this fix.
sphereinabox