从控件视图中获取位图图像

27
我想要将我WPF应用程序中的Control绘制内容“复制到剪贴板”,因此需要从当前显示的Control构建一个位图图像。有没有简便的方法可以实现?谢谢。

这个问题/答案对我也很有帮助: https://dev59.com/rkzSa4cB1Zd3GeqPqutK - Aaron Hoffman
3个回答

48

我不会说它很容易,但关键组件是RenderTargetBitmap,在使用时可以按如下方式:

RenderTargetBitmap rtb = new RenderTargetBitmap((int)control.ActualWidth, (int)control.ActualHeight, 96, 96, PixelFormats.Pbgra32);
rtb.Render(control);

那部分很简单,现在RTB已经在内部存储像素...但你的下一步是将其放入有用的格式中以放置在剪贴板上,弄清楚这一点可能会很麻烦...有许多与图像相关的类都互相交互。

以下是我们用来创建System.Drawing.Image的代码,我认为你应该能够将其放入剪贴板。

PngBitmapEncoder png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream stream = new MemoryStream();
png.Save(stream);
Image image = Image.FromStream(stream);

System.Drawing.Image(一个Windows Forms图片)无法直接与RenderTargetBitmap(一个WPF类)进行交互,因此我们使用MemoryStream将其转换。


谢谢,这是我的答案。 好的一点是Clipboard.SetImage()需要一个BitmapSource,所以我可以直接给它RenderTargetBitmap。非常好用。你可以轻松调用它。再次感谢!现在我需要想办法让用户在将控件可视化(基本上是缩放)导出到固定分辨率位图之前对其进行操作。我认为我会将当前显示复制为Viewbox中的VisualBrush,并允许用户在导出之前进行操作。 - Aurelien Ribon
5
值得注意的是,尽管这适用于普通的WPF组件,但是任何仅包装来自不同空间的组件(例如Webbrowser控件)的特殊控件都与RenderTargetBitmap不兼容。 - nextgentech
7
谢谢以前的自己!8年过去了,我又遇到了一个需要将WPF控件保存到流中的情况。但是我不记得它的确切工作方式。所以我搜索了一下,惊讶地发现我正在阅读自己的答案。:D - Bubblewrap
1
你如何捕获整个控件,即使它超出了屏幕? - JeremyK

17

如果您正在尝试从其中一个 StackPanel 中创建位图的控件,则不起作用,您将只得到一张空白图片。

Jaime Rodriguez在他的博客中有一段很好的代码来解决这个问题:

private static BitmapSource CaptureScreen(Visual target, double dpiX, double dpiY)
{
    if (target == null)
    {
        return null;
    }
    Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
    RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
                                                    (int)(bounds.Height * dpiY / 96.0),
                                                    dpiX,
                                                    dpiY,
                                                    PixelFormats.Pbgra32);
    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);
    return rtb;
}

我已经为这个问题努力思考了几个小时了。一个在网格中的画布可行,而另一个却不行。即使过了多年,WPF仍然能给我带来惊喜。 - CiucaS

2

以下内容需添加到您的代码后台(或传入窗口):

这不是我的代码,而是我自己和通过谷歌搜索混合使用的。适用于 .Net 5 和 WPF。

    public void RenderThisWindow(string fullPngPath)
    {
        Image myImage = new Image();
        
        DrawingVisual drawingVisual = new DrawingVisual();

        RenderTargetBitmap bmp = new RenderTargetBitmap((int)Width, (int)Height, 120, 96, PixelFormats.Pbgra32);
        bmp.Render(this);
        myImage.Source = bmp;

        SaveToPng(this, fullPngPath);
    }
    
    private void SaveToPng(FrameworkElement visual, string fileName)
    {
        var encoder = new PngBitmapEncoder();
        SaveUsingEncoder(visual, fileName, encoder);
    }
    
    private void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
    {
        RenderTargetBitmap bitmap = new RenderTargetBitmap((int)visual.Width, (int)visual.Width, 96, 96, PixelFormats.Pbgra32);
        bitmap.Render(visual);
        BitmapFrame frame = BitmapFrame.Create(bitmap);
        encoder.Frames.Add(frame);

        using (var stream = File.Create(fileName))
        {
            encoder.Save(stream);
        }
    }

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