4

I want to be able to load a WPF Resource Dictionary of Paths and output them one by one to files (jpg, bmp, it doesn't matter). This will be in a class library that will be accessed by an MVC application to render to a http stream, so I'm doing this purely within code (no XAML pages).

I've been able to load the dictionary and iterate through the paths, but when I save the images to disk, they are blank. I know I'm missing something trivial, like applying the path to a piece of geometry, or adding it into some containing rectangle or something, but my WPF experience is somewhat limited.

I'm using the following code:

I have a WPF Resource Dictionary containing several paths, such as the following:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Path x:Key="Path1" Data="M 100,200 C 100,25 400,350 400,175 H 280" Fill="White" Margin="10,10,10,10" Stretch="Fill"/>
  <Path x:Key="Path2" Data="M 10,50 L 200,70" Fill="White" Margin="10,10,10,10" Stretch="Fill"/>
</ResourceDictionary>

And the class to read and output the files:

public class XamlRenderer
{
    public void RenderToDisk()
    {
        ResourceDictionary resource = null;

        Thread t = new Thread(delegate()
        {
            var s = new FileStream(@"C:\Temp\myfile.xaml", FileMode.Open);
            resource = (ResourceDictionary)XamlReader.Load(s);
            s.Close();

            foreach (var item in resource)
            {
                var resourceItem = (DictionaryEntry)item;
                var path = (System.Windows.Shapes.Path)resourceItem.Value;

                var panel = new StackPanel();

                var greenBrush = new SolidColorBrush {Color = Colors.Green};

                path.Stroke = Brushes.Blue;
                path.StrokeThickness = 2;
                path.Fill = greenBrush;

                panel.Children.Add(path);

                panel.UpdateLayout();

                string filepath = @"C:\Temp\Images\" + resourceItem.Key + ".jpg";

                SaveImage(panel, 64, 64, filepath);
            }
        });

        t.SetApartmentState(ApartmentState.STA);
        t.Start();
    }

    public void SaveImage(Visual visual, int width, int height, string filePath)
    {
        var bitmap =
            new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
        bitmap.Render(visual);

        var image = new PngBitmapEncoder();
        image.Frames.Add(BitmapFrame.Create(bitmap));
        using (Stream fs = File.Create(filePath))
        {
            image.Save(fs);
        }
    } 
}
Spankachu
  • 167
  • 2
  • 11

1 Answers1

7

After much googling, and trial and error, I seem to have arrived at a solution. This post pointed me in the right direction. There were a few issues:

  • I was now setting the size of the path and the container
  • The stack panel container has some nuances that were causing issues, so I replaced it with a canvas
  • Most importantly, Measure() and Arrange() need to be called on the container element. The UpdateLayout() call is not needed.

Once these issues were fixed, the images rendered to disk (although there's an aspect ratio problem I have yet to fix).

Here is the updated code:

    public void RenderToDisk()
    {
        ResourceDictionary resource = null;

        Thread t = new Thread(delegate()
        {
            var s = new FileStream(@"C:\Temp\myfile.xaml", FileMode.Open);
            resource = (ResourceDictionary)XamlReader.Load(s);
            s.Close();

            foreach (var item in resource)
            {
                var resourceItem = (DictionaryEntry)item;
                var path = (System.Windows.Shapes.Path)resourceItem.Value;

                path.Margin = new Thickness(10);
                path.HorizontalAlignment = HorizontalAlignment.Center;
                path.VerticalAlignment = VerticalAlignment.Center;
                path.Width = 48;
                path.Height = 48;
                path.Stroke = Brushes.White;
                path.Fill = Brushes.Black;

                var canvas = new Canvas();
                canvas.Width = 64;
                canvas.Height = 64;
                canvas.Margin = new Thickness(0);
                canvas.Background = Brushes.Transparent;

                canvas.Children.Add(path);

                canvas.Measure(new Size(canvas.Width, canvas.Height));
                canvas.Arrange(new Rect(new Size(canvas.Width, canvas.Height)));

                string filepath = @"C:\Temp\Images\" + resourceItem.Key + ".png";

                SaveImage(canvas, (int)canvas.Width, (int)canvas.Height, filepath);
            }
        });

        t.SetApartmentState(ApartmentState.STA);
        t.Start();
    }
Community
  • 1
  • 1
Spankachu
  • 167
  • 2
  • 11