一、问题描述
Windows中缺省的DPI值为96。在Vista中,我们把DPI设为150%,也就是144。可如果此时我们去获取属性System.Drawing.Graphics.DpiX或者System.Drawing.Graphics.DpiY的值,我们发现得到的仍然是96,而不是144。
二、原因分析
修改Windows的DPI值,所有窗口的字体会变大,因此窗口的布局(Layout)也有可能不同。Windows程序员可以根据Windows的DPI设置去定义窗口的布局,但我们发现很多程序员在根据DPI定义窗口布局的时候遇到了很多问题。所以Windows的解决办法就是不建议程序员根据DPI设置调整窗口布局,Windows会根据系统设置自动按比例放大窗口的所有元素(包括文字和控件)。这样虽然降低了程序员的灵活性和自主性,但至少保证了窗口布局在所有DPI设置时都是正确的。
于是从Vista开始,Windows添加了一个叫SetProcessDPIAware的API。缺省的情况下,该函数不被调用,因此我们的进程不知道DPI已经修改,那我们得到的DPI也总是其缺省值96了。
三、建议
如前所述,我们不建议程序员试图去获取DPI设置值,并根据该值去调整窗口布局。如果确实需要得到系统的DPI的设置值,我们必须调用API SetProcessDPIAware。下面是一个例子:
public class Utility
{
private const int LOGPIXELSX = 88;
private const int LOGPIXELSY = 90;
public static int DpiX
{
get
{
if (Environment.OSVersion.Version.Major >= 6)
SetProcessDPIAware();
IntPtr hDC = GetDC(new HandleRef(null, IntPtr.Zero));
return GetDeviceCaps(hDC, LOGPIXELSX);
}
}
public static int DpiY
{
get
{
if (Environment.OSVersion.Version.Major >= 6)
SetProcessDPIAware();
IntPtr hDC = GetDC(new HandleRef(null, IntPtr.Zero));
return GetDeviceCaps(hDC, LOGPIXELSY);
}
}
[DllImport("user32.dll")]
private extern static bool SetProcessDPIAware();
[DllImport("user32.dll")]
private extern static IntPtr GetDC(HandleRef hWnd);
[DllImport("gdi32.dll")]
private extern static int GetDeviceCaps(IntPtr hdc, int nIndex);
}
上述代码先得到显示器的DC,然后得到该DC在X方向和Y方向的DPI值。另外值得注意的是,由于API SetProcessDPIAware在Vista才被引入,所以在调用该API之前,我们需要判断Windows的版本号。Vista的主版本号是6。