Search code examples
c#wpf.net-4.5rendertargetbitmap

RenderTargetBitmap position issues


Objective

Take a screenshot of a control (or a set of controls) using RenderTargetBitmap.

Source:

<Grid Height="200" Width="500">
    <!-- Here goes any content, in my case, a Label or a Shape-->
    <Label VerticalAligment="Top" HorizontalAligment="Left" Content="Text">
</Grid>

Expected result:

Expected

Method 1

This one basically uses the UIElement as the source of the RenderTargetBitmap.

public static ImageSource GetRender(this UIElement source)
{
     double actualHeight = source.RenderSize.Height;
     double actualWidth = source.RenderSize.Width;

     var renderTarget = new RenderTargetBitmap((int)Math.Round(actualWidth), 
         (int)Math.Round(actualHeight), 96, 96, PixelFormats.Pbgra32);

     renderTarget.Render(source);
     return renderTarget;
}

Result:

Method 1 Result

Method 2:

Instead of directly setting the UIElement as the source of the RenderTargetBitmap, I'll use a VisualBrush.

//Same RenderTargetBitmap...

DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
    VisualBrush vb = new VisualBrush(target);
    ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);

Result:

This one ignores the position and size of the Grid and the Label inside:

enter image description here

What's happening here?


Solution

  • Modified Method 2

    I just needed to get the bounds of the descendant of the Grid and render only the needed part.

    public static ImageSource GetRender(this UIElement source, double dpi)
    {
        Rect bounds = VisualTreeHelper.GetDescendantBounds(source);
    
        var scale = dpi / 96.0;
        var width = (bounds.Width + bounds.X)*scale;
        var height = (bounds.Height + bounds.Y)*scale;
    
        RenderTargetBitmap rtb = 
            new RenderTargetBitmap((int)Math.Round(width, MidpointRounding.AwayFromZero), 
            (int)Math.Round(height, MidpointRounding.AwayFromZero), 
            dpi, dpi, PixelFormats.Pbgra32);        
    
        DrawingVisual dv = new DrawingVisual();
        using (DrawingContext ctx = dv.RenderOpen())
        {
            VisualBrush vb = new VisualBrush(source);
            ctx.DrawRectangle(vb, null, 
                new Rect(new Point(bounds.X, bounds.Y), new Point(width, height)));
        }
    
        rtb.Render(dv);
        return (ImageSource)rtb.GetAsFrozen();
    }
    

    Result:

    The rendered Label/Shape:

    Rendered

    Merged with another Picture:

    Merged