如何使用 CopyFromScreen 在 WPF 窗口中创建 win32 控件的图像

问题描述

我想从 WPF 窗口中显示的 win32 控件(即 windowsformshost、Hwndhost、webview2)创建一个图像。

  1. rendertargetBitmap 不适用于这些类型的控件,我是否正确?

假设我是对的,我正在查看 this GitHub project 中用于制作图像的方法。该项目将 webbrowser 控件包装在 ContenControl 中。然后获取内容控件在屏幕上的位置,并使用 Graphics 类方法 copyFromScreen。

private void CreateScreenshotFromContent()
{
    Point upperLeftPoint = _airspaceContent.PointToScreen(new Point(0,0));
    var bounds = new System.Drawing.Rectangle((int)(upperLeftPoint.X * _scalingFactor),(int)(upperLeftPoint.Y * _scalingFactor),(int)(_airspaceContent.RenderSize.Width * _scalingFactor),(int)(_airspaceContent.RenderSize.Height * _scalingFactor));

    using (var bitmap = new System.Drawing.Bitmap((int)bounds.Width,(int)bounds.Height))
    {
        using (var g = system.drawing.graphics.FromImage(bitmap))
        {
            g.copyFromScreen(new System.Drawing.Point(bounds.Left,bounds.Top),System.Drawing.Point.Empty,new System.Drawing.Size((int)bounds.Width,(int)bounds.Height));
        }

        _airspaceScreenshot.source = GetimageSourceFromBitmap(bitmap);
    }
}

// https://stackoverflow.com/questions/5977445/how-to-get-windows-display-settings
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc,int nIndex);
private void GetScalingFactor()
{
    var g = system.drawing.graphics.FromHwnd(IntPtr.Zero);
    IntPtr desktop = g.GetHdc();
    int LogicalScreenHeight = GetDeviceCaps(desktop,10);
    int PhysicalScreenHeight = GetDeviceCaps(desktop,117);

    float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;

    _scalingFactor = ScreenScalingFactor; // 1.25 = 125%
}

public ImageSource GetimageSourceFromBitmap(System.Drawing.Bitmap bitmap)
{
    using (var memory = new MemoryStream())
    {
        bitmap.Save(memory,ImageFormat.Png);
        memory.Position = 0;
        BitmapImage bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = memory;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();
        return bitmapImage;
    }
}

GitHub 项目使用 Webbrowser 控件。一个小问题是我将其更新为 WebView2 控件,该控件较新,但也只是 WPF 中的一个 win32 控件。

如果 PC 的缩放比例为 100%,则一切正常。生成的图像几乎完全匹配控件显示内容。不幸的是,如果缩放比例发生变化,则不会发生这种情况。

这是带有 200% 缩放比例的 Webbrowser 控件的窗口的外观。

The WebBrowser control.

然而,当缩放比例为 200% 时,图像被放大了很多。

The Image at 200%

我的项目现在面向 4.7.2 .Net Framework,如果我理解正确,这意味着它现在可以识别 DPI,因此我知道我不需要对应用程序配置或清单做任何额外的事情。我是否正确 不再需要像下面这样的清单更改?

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
  </windowsSettings>
</application>

解决方法

假设 _scalingFactor 有问题,我建议使用 VisualTreeHelper.GetDpi 方法。在这种情况下,GetScalingFactor 方法将如下所示:

public void GetScalingFactor()
{
    _scalingFactor = (float)VisualTreeHelper.GetDpi(_airspaceContent).DpiScaleX;
}

我认为这个类有很大的重构空间。