Search code examples
c#wpfuser-controlspngscrollviewer

Export WPF ScrollViewer content as separate PNGs


In my WPF application I have a ScrollViewer which is dynamically populated by DataBinding and the use of a UserControl. Imagine that my UserControl is something simple that only contains a label, and the value displayed in the label comes from a list. So when I run the application it looks like below:

enter image description here

As you can see there are multiple instances of the UserControl each with a different value that is dynamically populated. My goal is to export each of the UserControls as a separate PNG. My EXPORT PNG button click should do this.

So I looked around and found this example which works fine for exporting the entire content of the ScorllViewer.

So I tried modifying it to achieve my goal, and I do get it to work to a certain extent, but not quite what I want.

Here's my code:

private void ExportPNG()
{
    var dir = Directory.GetCurrentDirectory();
    var file = "ITEM_{0}.PNG";
    var height = 100.0; // Height of the UserControl
    var width = mainSV.ActualWidth;

    for (int i = 1; i <= ItemList.Count; i++)
    {
        var path = System.IO.Path.Combine(dir, string.Format(file, i));

        Size size = new Size(width, height);
        UIElement element = mainSV.Content as UIElement;
        element.Measure(size);
        element.Arrange(new Rect(new Point(0, 0), size));

        RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
        VisualBrush sourceBrush = new VisualBrush(element);

        DrawingVisual drawingVisual = new DrawingVisual();
        DrawingContext drawingContext = drawingVisual.RenderOpen();

        using (drawingContext)
        {
            drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(width, height)));
        }
        renderTarget.Render(drawingVisual);

        PngBitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(renderTarget));
        using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
        {
            encoder.Save(stream);
        }
    }
}

Now this exports 5 PNGs (when my list has 5 items), bu they all contain the image of the first element:

enter image description here

And I figure the problem is likely where I do the drawingContext.DrawRectangle() because my rectanble coordinates are always the same. So I tried changing it to the following, which I thought should work, but for some reason it just generates one PNG with the first UserControl and 4 empty PNGs.

using (drawingContext)
{
    drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, (i-1) * height), new Point(width, height + ((i-1) * height))));
}

And the result:

enter image description here

What am I doing wrong here?

If you would like to run the code please find it here.


Solution

  • Please check below two methods. I tested it and it is working very well. 1. First, find children from you Items controls 2. Convert them to PNGs.

    private void ExportPNG()
                {
                    var dir = Directory.GetCurrentDirectory();
                    var file = "ITEM_{0}.PNG";
                    var height = 100.0;
                    var width = 100.0;
    
                    var children = GetChildrenOfType<UCDisplayItem>(itC);
    
    
                    foreach (var item in children)
                    {
                        var path = System.IO.Path.Combine(dir, string.Format(file, DateTime.Now.Ticks));
    
                        Size size = new Size(width, height);
                        UIElement element = item as UIElement;
                        element.Measure(size);
                        element.Arrange(new Rect(new Point(0, 0), size));
    
                        RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);
                        VisualBrush sourceBrush = new VisualBrush(element);
    
                        DrawingVisual drawingVisual = new DrawingVisual();
                        DrawingContext drawingContext = drawingVisual.RenderOpen();
    
                        using (drawingContext)
                        {
                            drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(width, height)));
                            //drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, (i - 1) * height), new Point(width, height + ((i - 1) * height))));
                        }
                        renderTarget.Render(drawingVisual);
    
                        PngBitmapEncoder encoder = new PngBitmapEncoder();
                        encoder.Frames.Add(BitmapFrame.Create(renderTarget));
                        using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
                        {
                            encoder.Save(stream);
                        }
    
                    }
                }
    
    public List<T> GetChildrenOfType<T>( DependencyObject depObj)
       where T : DependencyObject
            {
                var result = new List<T>();
                if (depObj == null) return null;
                var queue = new Queue<DependencyObject>();
                queue.Enqueue(depObj);
                while (queue.Count > 0)
                {
                    var currentElement = queue.Dequeue();
                    var childrenCount = VisualTreeHelper.GetChildrenCount(currentElement);
                    for (var i = 0; i < childrenCount; i++)
                    {
                        var child = VisualTreeHelper.GetChild(currentElement, i);
                        if (child is T)
                            result.Add(child as T);
                        queue.Enqueue(child);
                    }
                }
    
                return result;
            }
    

    RESULT enter image description here