You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF中如何确定RenderTargetBitmap的DPI值以正确渲染位图?

问题

我正尝试在WPF应用中将800×800的位图绘制到Image控件中。具体操作是创建DrawingVisual,通过RenderOpen()生成DrawingContext,在其中绘制红蓝色交替的98×98矩形,接着创建指定尺寸和手动输入DPI的RenderTargetBitmap,调用Render()后将其设为Image控件的源。

问题在于,若不明确正确的DPI值,位图无法渲染到正确尺寸,目前发现实际DPI约为91。当前代码中我将DPI硬编码为96:

DrawingVisual D = new DrawingVisual();
DrawingContext DC = D.RenderOpen();
DC.DrawRectangle(Brushes.White, null, new Rect(0, 0, 800, 800));

int R, C;
for (R = 0; R < 8; R++)
{
    for (C = 0; C < 8; C++)
    {
        Brush TheColor = Brushes.Red;
        if ((R + C) % 2 == 0)
        {
            TheColor = Brushes.Blue;
        }
        DC.DrawRectangle(TheColor, null, new Rect(C * 100 + 1, R * 100 + 1, 98, 98));
    }
}
DC.Close();

double DPI_X = 96.0;
double DPI_Y = 96.0;

RenderTargetBitmap B = new RenderTargetBitmap(800, 800, DPI_X, DPI_Y, PixelFormats.Pbgra32);
B.Render(D);
theImage.Source = B;

Image控件的XAML代码如下:

<Image x:Name="theImage" HorizontalAlignment="Left" Height="800" Width="800"
                         Margin="0,0,0,0" VerticalAlignment="Top" />

主要疑问:

  1. 我的实现方式是否正确?当前位图显示正常但尺寸不符。
  2. 若此方式可行,如何获取屏幕DPI?网上找到的方法要么返回null要么返回96。

解决方案

一、实现方式的正确性

核心实现思路是对的,尺寸不符的根源在于WPF的设备无关单位(1/96英寸)与实际像素的映射依赖真实屏幕DPI,硬编码96DPI会在非标准DPI屏幕上出现缩放偏差。

需要注意:

  • DrawingVisual中的绘制坐标是设备无关单位,而RenderTargetBitmap的尺寸参数是实际像素数。如果目标是让绘制内容刚好填满800x800像素的Image控件,必须保证RenderTargetBitmap的DPI与屏幕实际DPI匹配。
  • 你当前绘制的800x800设备无关单位,在96DPI下对应800x800像素,但如果屏幕实际DPI是91,800设备无关单位对应的像素数为 800 * (91/96) ≈ 758,这就导致显示尺寸不符。

二、获取正确的屏幕DPI

以下是WPF中可靠的屏幕DPI获取方式,避开旧方法的96DPI偏差问题:

方法1:通过Visual的DpiProperty获取(推荐,.NET 4.6.2+支持)

直接读取Image控件所在屏幕的真实DPI:

var dpi = VisualTreeHelper.GetDpi(theImage);
double dpiX = dpi.DpiScaleX * 96;
double dpiY = dpi.DpiScaleY * 96;

方法2:通过Window的CompositionTarget获取

如果Image在Window中,可从窗口的设备转换矩阵计算DPI:

var dpiScale = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
double dpiX = 96 * dpiScale.M11;
double dpiY = 96 * dpiScale.M22;

方法3:启用高DPI感知(必要配置)

在项目的app.manifest中添加以下配置,避免系统自动缩放导致的DPI偏差:

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

三、修正后的代码示例

替换硬编码DPI为真实屏幕DPI,同时用using自动释放DrawingContext:

DrawingVisual D = new DrawingVisual();
using (DrawingContext DC = D.RenderOpen())
{
    DC.DrawRectangle(Brushes.White, null, new Rect(0, 0, 800, 800));

    for (int R = 0; R < 8; R++)
    {
        for (int C = 0; C < 8; C++)
        {
            Brush TheColor = (R + C) % 2 == 0 ? Brushes.Blue : Brushes.Red;
            DC.DrawRectangle(TheColor, null, new Rect(C * 100 + 1, R * 100 + 1, 98, 98));
        }
    }
}

// 获取真实DPI
var dpi = VisualTreeHelper.GetDpi(theImage);
double DPI_X = dpi.DpiScaleX * 96;
double DPI_Y = dpi.DpiScaleY * 96;

RenderTargetBitmap B = new RenderTargetBitmap(800, 800, DPI_X, DPI_Y, PixelFormats.Pbgra32);
B.Render(D);
theImage.Source = B;

内容的提问来源于stack exchange,提问作者Don Del Grande

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 14:12:04