Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using RenderTargetBitmap on WindowsFormsHost

I have a set of controls inside a WindowsFormsHost and I would like to capture the current view and just save it as an image, I however only get some Panel visible in the Image.

Is it possible to use the WindowsFormsHost as a "Visual" and capture the wrapped controls?

See my example:

<WindowsFormsHost x:Name="windowHost">
    <wf:Panel Dock="Fill" x:Name="basePanel"/>
</WindowsFormsHost>

If i were to add a Button or whatever to the basePanel this wouldn't be visible when exporting to a PNG with the following code:

 RenderTargetBitmap rtb = new RenderTargetBitmap(basePanel.Width, 
                                 basePanel.Height, 96, 96, PixelFormats.Pbgra32);
 rtb.Render(windowHost);

 PngBitmapEncoder pnge = new PngBitmapEncoder();
 pnge.Frames.Add(BitmapFrame.Create(rtb));
 Stream stream = File.Create("test.jpg");

 pnge.Save(stream);

 stream.Close();

Suggestions on why this might not work and maybe a possible work-around? I guess it's not really suppose to work this way, but one could really hope!

like image 985
Filip Ekberg Avatar asked Dec 30 '22 11:12

Filip Ekberg


2 Answers

The content of the WindowsFormsHost is rendered by GDI+, like in a Windows Forms application, so you can't use RenderTargetBitmap for that since it is not rendered by WPF. Instead, you should use the GDI+ BitBlt function, which allows you to make a capture of an area on the screen.

See this post for an example


UPDATE: here's another version of the code, updated for use with WPF :

using System.Drawing;
...

public static ImageSource Capture(IWin32Window w)
{
    IntPtr hwnd = new WindowInteropHelper(w).Handle;
    IntPtr hDC = GetDC(hwnd);
    if (hDC != IntPtr.Zero)
    {
        Rectangle rect = GetWindowRectangle(hwnd);
        Bitmap bmp = new Bitmap(rect.Width, rect.Height);
        using (Graphics destGraphics = Graphics.FromImage(bmp))
        {
            BitBlt(
                destGraphics.GetHdc(),
                0,
                0,
                rect.Width,
                rect.Height,
                hDC,
                0,
                0,
                TernaryRasterOperations.SRCCOPY);
        }
        ImageSource img = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
            bmp.GetHBitmap(),
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());
        return img;
    }
    return null;
}

Just call pass your WindowsFormsHost control as a parameter to the Capture method, and do whatever you like with the resulting ImageSource. For the definitions of BitBlt and GetDC, have a look at this website (I wrote that on my home computer, which I can't access from where I am now)

like image 164
Thomas Levesque Avatar answered Jan 12 '23 01:01

Thomas Levesque


A Windows Forms control also knows how to render itself, you don't have to jump through the screen capture hoop. Make it look like this:

    using (var bmp = new System.Drawing.Bitmap(basePanel.Width, basePanel.Height)) {
        basePanel.DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height));
        bmp.Save(@"c:\temp\test.png");
    }
like image 26
Hans Passant Avatar answered Jan 12 '23 00:01

Hans Passant