如何使用图形对象渲染WPF Hwnd

4

在使用RenderTargetBitmap时速度太慢了。我尝试了不同的方法,但很遗憾我的尝试没有成功。希望你们中的一些人能够找出这段代码为什么不起作用。

  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;

什么不起作用?你是否遇到了错误,得到了错误的结果,对性能还不满意... - sous2817
没有错误。它只是看起来图形对象是空的并绘制一张空白图片。 - Andreas
为什么这个问题被踩了?^^ - Andreas
1个回答

5
很抱歉,您的代码无法正常工作。实际上,您正在将空图像复制到Graphics中,然后将该图像保存到文件中。因此,该文件是空的。
如果您不想使用RenderTargetBitmap,则需要使用BitBlt函数。
让我们看看如何做到这一点。首先,让我们创建一个帮助类来管理从Visual到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;
    }
}

现在,我们可以编写一个简单的XAML来测试助手类:
<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>

使用其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");
        }
    }
}

如果您点击按钮,您将发现边框及其内容已复制到位图文件中。我希望这种方法的速度适合您的目的。

不幸的是,它也捕获了控件的覆盖层,但我们可以将边框放在空间弹出窗口中以捕获其hwnd。 - Andreas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接