How to render a WPF Hwnd with Graphics Object

一曲冷凌霜 提交于 2019-12-22 22:29:07

问题


After the RenderTargetBitmap is much too slow. I tried a different approach. Unfortunately my attempt was not successful. Hopefully some of you guys can figure out why this code is not working.

  var myPopup = new Popup();
            var child = new Grid() { Background = new VisualBrush(myVisual)};
            myPopup.StaysOpen = false;
            myPopup.Child = child;
            myPopup.IsOpen = false;

            myPopup.Opened += (sender, args) =>
            {
                var source = ((HwndSource)PresentationSource.FromVisual(myPopup.Child));
                var image = new Bitmap(1000,1000);
                using (Graphics gr = Graphics.FromHwnd(source.Handle))
                {
                    var ptr = gr.GetHdc();

                    using (Graphics g = Graphics.FromHdc(ptr))
                    {
                        g.DrawImage(image, new System.Drawing.Point(0, 0));
                    }
                    gr.ReleaseHdc(ptr);
                }
                //The image is just black...
                image.Save("test.png");

                myPopup.IsOpen = false;
            };
            myPopup.IsOpen = true;

回答1:


I am afraid that your code cannot work. Indeed you are copying an empty image to a Graphics and then you are saving that image to a file. So the file is empty.

If you do not want to use RenderTargetBitmap, then you need to use BitBlt function.

Let's see how to do it. First of all let's create an helper class which manages the conversion from Visual to Bitmap:

public static class VisualToBitmapConverter
{
    private enum TernaryRasterOperations : uint
    {
        SRCCOPY = 0x00CC0020,
        SRCPAINT = 0x00EE0086,
        SRCAND = 0x008800C6,
        SRCINVERT = 0x00660046,
        SRCERASE = 0x00440328,
        NOTSRCCOPY = 0x00330008,
        NOTSRCERASE = 0x001100A6,
        MERGECOPY = 0x00C000CA,
        MERGEPAINT = 0x00BB0226,
        PATCOPY = 0x00F00021,
        PATPAINT = 0x00FB0A09,
        PATINVERT = 0x005A0049,
        DSTINVERT = 0x00550009,
        BLACKNESS = 0x00000042,
        WHITENESS = 0x00FF0062,
        CAPTUREBLT = 0x40000000
    }

    [DllImport("gdi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

    public static Bitmap GetBitmap(Visual visual, int width, int height)
    {
        IntPtr source;
        IntPtr destination;

        Bitmap bitmap = new Bitmap(width, height);
        HwndSource hwndSource = (HwndSource)PresentationSource.FromVisual(visual);
        using (Graphics graphicsFromVisual = Graphics.FromHwnd(hwndSource.Handle))
        {
            using (Graphics graphicsFromImage = Graphics.FromImage(bitmap))
            {
                source = graphicsFromVisual.GetHdc();
                destination = graphicsFromImage.GetHdc();

                BitBlt(destination, 0, 0, bitmap.Width, bitmap.Height, source, 0, 0, TernaryRasterOperations.SRCCOPY);

                graphicsFromVisual.ReleaseHdc(source);
                graphicsFromImage.ReleaseHdc(destination);
            }
        }

        return bitmap;
    }
}

Now we can write a simple XAML just for testing the helper class:

<Window x:Class="WpfApplication1.MainWindow" Name="win"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="400">

    <StackPanel>
        <Border BorderBrush="DarkGray" BorderThickness="4" CornerRadius="4"
                Background="LightGray" Padding="6" Name="border">
            <Label Content="Copy me to a bitmap file, please" FontSize="20" Foreground="Green"
                   FontStyle="Italic" />
        </Border>
        <Button Content="Save to file" Margin="20" HorizontalAlignment="Center"
                Click="Button_Click" />
    </StackPanel>

</Window>

With its code-behind:

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Bitmap image = VisualToBitmapConverter.GetBitmap(border,
                (int)border.ActualWidth, (int)border.ActualHeight);

            image.Save(@"C:\YourPath\test.png");
        }
    }
}

If you click the button, you will find the Border and its content copied into the Bitmap file. I hope the speed of this method is suitable for your pourpose.



来源:https://stackoverflow.com/questions/32505712/how-to-render-a-wpf-hwnd-with-graphics-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!