【问题标题】:Using RenderTargetBitmap on WindowsFormsHost在 WindowsFormsHost 上使用 RenderTargetBitmap
【发布时间】:2009-11-18 15:51:13
【问题描述】:

我在 WindowsFormsHost 中有一组控件,我想捕获当前视图并将其保存为图像,但是我只能在图像中看到一些 Panel

是否可以将 WindowsFormsHost 用作“视觉对象”并捕获包装的控件?

看我的例子:

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

如果我要向basePanel 添加按钮或其他任何内容,则在使用以下代码导出到 PNG 时将不可见:

 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();

关于为什么这可能不起作用以及可能的解决方法的建议?我想这不是真的应该以这种方式工作,但人们真的希望!

【问题讨论】:

    标签: c# .net wpf winforms


    【解决方案1】:

    WindowsFormsHost 的内容由 GDI+ 呈现,就像在 Windows 窗体应用程序中一样,因此您不能为此使用 RenderTargetBitmap,因为它不是由 WPF 呈现的。相反,您应该使用 GDI+ BitBlt 函数,该函数允许您捕获屏幕上的某个区域。

    请参阅this post 了解示例


    更新:这是代码的另一个版本,已更新以与 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;
    }
    

    只需调用将您的WindowsFormsHost 控件作为参数传递给Capture 方法,然后对生成的ImageSource 做任何您想做的事情。对于BitBltGetDC 的定义,请查看this website(我是在家用电脑上写的,现在无法访问)

    【讨论】:

    • 您能否提供一个关于如何使用该函数的示例:public static Bitmap Capture(IntPtr hwnd), in WPF?
    • 好吧,忘掉这个技巧... nobugz 指出了一个更好的解决方案 ;)
    • 谢谢,不过只是一个想法,GetWindowRectangle 是否也被外部定义了?
    • 它只是一个包装器,使 GetWindowRect API 更易于使用
    • Hans Passant 的解决方案对我不起作用(winform 内容仍然不可见),但这有效。谢谢。
    【解决方案2】:

    Windows 窗体控件也知道如何呈现自己,您不必跳过屏幕捕获圈。让它看起来像这样:

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

    【讨论】:

    • 好点,我完全忘记了那个方法!这确实比使用 BitBlt 容易得多,并且不需要任何 P/Invoke 调用...
    • 太棒了..但是!这“仅”捕获您所看到的内容,它不会捕获所有内容(如果您有滚动条)。是否可以说“渲染整个控件并跳过滚动条”?
    • 没有。您可以先调整控件的大小,但很快就会耗尽气体。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 2012-06-30
    相关资源
    最近更新 更多