在Windows 7中有一个“显示”设置(控制面板->显示),它允许您更改屏幕上文本和其他项目的大小。我需要获取此设置的值,以便根据设置值在我的C#应用程序中开启/关闭某些功能。这是否可行?
在所有缩放级别下,Surface Pro 上的 graphics.DpiX 和 DeviceCap.LOGPIXELSX 均返回 96。
相反,我通过以下方法计算了缩放因子:
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public enum DeviceCap
{
VERTRES = 10,
DESKTOPVERTRES = 117,
// http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html
}
private float getScalingFactor()
{
Graphics g = Graphics.FromHwnd(IntPtr.Zero);
IntPtr desktop = g.GetHdc();
int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);
float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
return ScreenScalingFactor; // 1.25 = 125%
}
这个设置是屏幕的 DPI,即每英寸点数。
理解方式如下:
float dpiX, dpiY;
Graphics graphics = this.CreateGraphics();
dpiX = graphics.DpiX;
dpiY = graphics.DpiY;
目前我认为X和Y值不可能不同。96代表100%字体缩放(更小),120代表125%缩放(中等),144代表150%缩放(更大)。但是,用户可以设置除这些标准值以外的值。
请注意,除非您的应用程序声明为DPI感知,否则您观察到的值可能会受到DPI虚拟化的影响。
this
只是任何控件。http://msdn.microsoft.com/zh-cn/library/system.windows.forms.control.creategraphics.aspx - David Heffernan以下是在WPF中如何进行操作。返回值以WPF的逻辑单位表示,这与1/96英寸相等。所以如果您的屏幕DPI设置为96,您将得到一个值为1。
Matrix m =
PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice;
double dx = m.M11; // notice it's divided by 96 already
double dy = m.M22; // notice it's divided by 96 already
(来源)
我认为最简单的方法是使用GetDeviceCaps
函数。从pinvoke.net获取:
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);
public enum DeviceCap
{
/// <summary>
/// Logical pixels inch in X
/// </summary>
LOGPIXELSX = 88,
/// <summary>
/// Logical pixels inch in Y
/// </summary>
LOGPIXELSY = 90
// Other constants may be founded on pinvoke.net
}
用法:
Graphics g = Graphics.FromHwnd(IntPtr.Zero);
IntPtr desktop = g.GetHdc();
int Xdpi = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSX);
int Ydpi = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSY);
在这种方法中,您无需将应用程序标记为dpi感知。这里有一个在Windows 10上运作良好的解决方案。不需要DPI意识或其他任何东西。
public static int GetWindowsScaling()
{
return (int)(100 * Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth);
}
Screen.PrimaryScreen.Bounds
有物理像素,而SystemParameters.PrimaryScreenWidth是逻辑像素?很遗憾我们无法确定其他屏幕的情况。 - Garr Godfrey使用Farshid T的答案作为基础,在每个缩放因子中都有效,除了125%。我测试了大约20个不同的缩放因子,DPI始终返回96,除非设置为125%,这将返回120的DPI。 120/96 = 1.25。不确定为什么会出现这种情况,但是这段代码似乎适用于任何比例设置。
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public enum DeviceCap
{
VERTRES = 10,
DESKTOPVERTRES = 117,
LOGPIXELSY = 90,
// http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html
使用方法:
Graphics g = Graphics.FromHwnd(IntPtr.Zero);
IntPtr desktop = g.GetHdc();
int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);
int logpixelsy = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSY);
float screenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
float dpiScalingFactor = (float)logpixelsy / (float)96;
if (screenScalingFactor > 1 ||
dpiScalingFactor > 1)
{
// do something nice for people who can't see very well...
}
这是一个非常古老的问题,但自 Windows 8.1 以来,人们可以使用各种其他功能,比如GetDpiForWindow
在 C# 中:
[DllImport("user32.dll")]
static extern int GetDpiForWindow(IntPtr hWnd);
public float GetDisplayScaleFactor(IntPtr windowHandle)
{
try
{
return GetDpiForWindow(windowHandle) / 96f;
}
catch
{
// Or fallback to GDI solutions above
return 1;
}
}
为了使其在 Windows 10 周年更新版上正常工作,您需要向 C# 项目添加一个 app.manifest
:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect :
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
</windowsSettings>
</application>
</assembly>
我在控制台应用程序中使用这种方式:
float dpiX, dpiY;
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
dpiX = graphics.DpiX;
dpiY = graphics.DpiY;
}
PresentationSource source = PresentationSource.FromVisual(this);
double dpiX, dpiY;
if (source != null)
{
dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
}
以下是Ric Gaudet答案的更完整版本:
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);
public enum DeviceCap
{
VERTRES = 10,
DESKTOPVERTRES = 117
}
static double GetWindowsScreenScalingFactor(bool percentage = true)
{
//Create Graphics object from the current windows handle
Graphics GraphicsObject = Graphics.FromHwnd(IntPtr.Zero);
//Get Handle to the device context associated with this Graphics object
IntPtr DeviceContextHandle = GraphicsObject.GetHdc();
//Call GetDeviceCaps with the Handle to retrieve the Screen Height
int LogicalScreenHeight = GetDeviceCaps(DeviceContextHandle, (int)DeviceCap.VERTRES);
int PhysicalScreenHeight = GetDeviceCaps(DeviceContextHandle, (int)DeviceCap.DESKTOPVERTRES);
//Divide the Screen Heights to get the scaling factor and round it to two decimals
double ScreenScalingFactor = Math.Round((double)PhysicalScreenHeight / (double)LogicalScreenHeight, 2);
//If requested as percentage - convert it
if (percentage)
{
ScreenScalingFactor *= 100.0;
}
//Release the Handle and Dispose of the GraphicsObject object
GraphicsObject.ReleaseHdc(DeviceContextHandle);
GraphicsObject.Dispose();
//Return the Scaling Factor
return ScreenScalingFactor;
}