而不是捕获WM_DPICHANGED
事件,只要在需要时询问当前的 DPI 设置(在Paint
事件或其他)?
但这也并不明显。如果你搜索 StackOverflow,通常可以找到以下答案:
using (Graphics screen = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hdc = screen.GetHdc();
int dpiX = GetDeviceCaps(hdc, DeviceCaps.LOGPIXELSX);
screen.ReleaseHdc(hdc);
}
但是,无论实际 DPI 设置如何,它都将始终返回 96,除非...
- 您使用 Windows XP 或在 DPI 设置中签入兼容模式。Problem:您不能对用户强制执行它。
- DWM 已关闭(您使用基本或经典主题)。Problem: 和上面一样。
- 你打电话设置进程DPIAware https://msdn.microsoft.com/en-us/library/ms633543%28v=vs.85%29.aspx使用 GetDeviceCaps 之前的函数。Problem:该函数应该在所有其他渲染之前调用一次。如果您现有不支持 DPI 的应用程序,则更改感知会破坏整个外观。一旦调用该函数,它就无法关闭。
- 你打电话设置进程Dpi感知 https://msdn.microsoft.com/en-us/library/dn302216%28v=vs.85%29.aspx使用 GetDeviceCaps 之前和之后。Problem:此功能至少需要Windows 8.1
真正可行的解决方案
看来GetDeviceCaps 函数 https://msdn.microsoft.com/en-us/library/windows/desktop/dd144877%28v=vs.85%29.aspxMSDN 上没有完整记录。至少我发现pinvoke.net http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html提到了该函数可以获得的一些其他选项。最后我得出了以下解决方案:
public static int GetSystemDpi()
{
using (Graphics screen = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hdc = screen.GetHdc();
int virtualWidth = GetDeviceCaps(hdc, DeviceCaps.HORZRES);
int physicalWidth = GetDeviceCaps(hdc, DeviceCaps.DESKTOPHORZRES);
screen.ReleaseHdc(hdc);
return (int)(96f * physicalWidth / virtualWidth);
}
}
以及上面示例中所需的附加代码:
private enum DeviceCaps
{
/// <summary>
/// Logical pixels inch in X
/// </summary>
LOGPIXELSX = 88,
/// <summary>
/// Horizontal width in pixels
/// </summary>
HORZRES = 8,
/// <summary>
/// Horizontal width of entire desktop in pixels
/// </summary>
DESKTOPHORZRES = 118
}
/// <summary>
/// Retrieves device-specific information for the specified device.
/// </summary>
/// <param name="hdc">A handle to the DC.</param>
/// <param name="nIndex">The item to be returned.</param>
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, DeviceCaps nIndex);