WPF:截取屏幕截图的方式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20338960/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 10:08:31  来源:igfitidea点击:

WPF: Way to take screenshots

c#wpfscreenshot

提问by Tsukasa

Looking to modify the following to take a screenshot off all monitors. I have tried adapting it but my images are blank. Writing it to test.png is for testing. The byte[]will be sent to the receiving app.

希望修改以下内容以从所有显示器上截取屏幕截图。我试过调整它,但我的图像是空白的。将其写入 test.png 是为了测试。在byte[]将被发送到接收应用程序。

public byte[] Take()
{
    int screenWidth = Convert.ToInt32(SystemParameters.VirtualScreenWidth);
    int screenHeight = Convert.ToInt32(SystemParameters.VirtualScreenHeight);
    int screenLeft = Convert.ToInt32(SystemParameters.VirtualScreenLeft);
    int screenTop = Convert.ToInt32(SystemParameters.VirtualScreenTop);

    RenderTargetBitmap renderTarget = new RenderTargetBitmap(screenWidth, screenHeight, 96, 96, PixelFormats.Pbgra32);
    VisualBrush sourceBrush = new VisualBrush();

    DrawingVisual drawingVisual = new DrawingVisual();
    DrawingContext drawingContext = drawingVisual.RenderOpen();

    using (drawingContext)
    {
        drawingContext.PushTransform(new ScaleTransform(1, 1));
        drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(screenWidth, screenHeight)));
    }
    renderTarget.Render(drawingVisual);

    PngBitmapEncoder pngEncoder = new PngBitmapEncoder();
    pngEncoder.Frames.Add(BitmapFrame.Create(renderTarget));

    Byte[] _imageArray;

    using (MemoryStream outputStream = new MemoryStream())
    {
        pngEncoder.Save(outputStream);
        _imageArray = outputStream.ToArray();
    }

    using (FileStream stream = new FileStream(@"c:\test.png", FileMode.Create, FileAccess.ReadWrite))
    {
        using (BinaryWriter writer = new BinaryWriter(stream))
        {
            writer.Write(_imageArray);
        }
    }

    return _imageArray;
}

回答by Mark Feldman

First you'll need to add references for the following namespaces:

首先,您需要为以下命名空间添加引用:

using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

Then enumerate your monitors to get the bounding rectangle for all display surfaces and pass that in to the Graphics.CopyFromScreen()method call:

然后枚举您的监视器以获取所有显示表面的边界矩形并将其传递给Graphics.CopyFromScreen()方法调用:

private static BitmapSource CopyScreen()
{
    var left = Screen.AllScreens.Min(screen => screen.Bounds.X);
    var top = Screen.AllScreens.Min(screen => screen.Bounds.Y);
    var right = Screen.AllScreens.Max(screen => screen.Bounds.X + screen.Bounds.Width);
    var bottom = Screen.AllScreens.Max(screen => screen.Bounds.Y + screen.Bounds.Height);
    var width = right - left;
    var height = bottom - top;

    using (var screenBmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
    {
        using (var bmpGraphics = Graphics.FromImage(screenBmp))
        {
            bmpGraphics.CopyFromScreen(left, top, 0, 0, new System.Drawing.Size(width, height));
            return Imaging.CreateBitmapSourceFromHBitmap(
                screenBmp.GetHbitmap(),
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());
        }
    }
}

Bear in mind though that monitors often don't fit neatly into a single rectangle, particularly if they have different resolution etc, so you might be better snap-shotting the individual screens. Either way, the solution to your problem is to change the coordinates that you were passing in to the Graphics.CopyFromScreen()method call.

请记住,尽管显示器通常不能整齐地放入单个矩形中,特别是如果它们具有不同的分辨率等,因此您最好对各个屏幕进行快照。无论哪种方式,您的问题的解决方案是更改您传递给Graphics.CopyFromScreen()方法调用的坐标。

EDIT: see Demetris Leptos's comment below, the code I've posted in this answer should be calling DeleteObject on the bitmap returned by screenBmp.GetHbitmap()so as to avoid a memory leak, as specified in the MSDN documentation.

编辑:请参阅下面的 Demetris Leptos 评论,我在此答案中发布的代码应该在返回的位图上调用 DeleteObject screenBmp.GetHbitmap(),以避免内存泄漏,如 MSDN 文档中所述。