【问题标题】:Screen Resolution not matching Screen.Bounds屏幕分辨率与 Screen.Bounds 不匹配
【发布时间】:2015-03-15 05:49:01
【问题描述】:

我发现通过控制面板设置的分辨率与我的宽屏笔记本电脑的 Screen.Bounds 输出之间存在有趣的差异。屏幕为 15.5",通过控制面板设置的分辨率为 1920x1080。但是当我运行一些这样的代码时。

Screen[] screens = Screen.AllScreens;
foreach (Screen scr in screens)
{
    Console.WriteLine("Width: " + scr.Bounds.Width + ", Height: " + scr.Bounds.Width);
}

输出显示我的分辨率为 1536x864。我环顾四周,我认为这可能与 DPI 问题有关,当我查看我的显示设置时,滑块(我在 Windows 8.1 上)位于中间,并且复选框显示“让我为我的所有显示器选择一个缩放级别”未选中。我运行这个小代码来获取当前的 DPI 设置。

float dpiX, dpiY;
Graphics graphics = new System.Windows.Forms.Form().CreateGraphics();
Console.WriteLine("DPI: " + graphics.DpiX);

返回的 DPI 是 96.0,据我了解,它是 100% DPI 设置(因此没有放大或任何名称)。对我来说奇怪的是,Screen 返回的边界正好是我实际分辨率的 80%,这让我认为我的 DPI 设置为 100(或 125%),但事实并非如此。我的笔记本电脑屏幕只有这个问题,因为我的辅助显示器的界限等于通过控制面板的分辨率。这是因为我的 DPI 设置未设置为使显示相互独立(选中该复选框)?对于一点背景知识,我正在编写一个工具,它获取当前屏幕并从 reddit 获取图片并将它们彼此独立地适合屏幕,所以无论我有什么解决方案,它都必须正确获得每个显示器的分辨率。

【问题讨论】:

  • 我认为您的第一个代码示例中的scr.Bounds..Primary 应该是scr.Bounds.Height
  • 如果“让我为所有显示器选择一个缩放级别”未选中,那么您肯定为每个屏幕设置了不同的 DPI 吗?
  • @StevenRands 修复了该示例,我认为是这种情况,但是如果未选中该框,则两个屏幕的 DPI 仍为 96.0。我在获取 DPI 的方式上做错了吗?
  • 本身没有错,只是 Graphics 实例的 DPI 可能默认为主屏幕的 DPI。你在 Windows 8.1 上吗?有一个 GetDpiForMonitor API 可能有用。
  • 有趣,我可以从 .Net 中的 C# 调用它,还是必须在 C++ 中调用它?

标签: c# windows-8.1 resolution dpi


【解决方案1】:

我认为您必须通知操作系统您的应用程序支持 DPI。否则操作系统会假装一切都很好,从而导致您观察到的行为 - 操作系统会处理大小调整。

您可以在此处找到有关编写 DPI 感知应用程序的一些信息 - http://msdn.microsoft.com/cs-cz/library/dd464646.aspx 当然,您应该确保您的应用程序实际上是 DPI 感知的 - 如果不是,您最好坚持使用默认设置。它不是那么好,但至少它会起作用。

Windows 8.1 带来的主要不同是可以在不同的显示器上有不同的 DPI,并且可以查询显示器的 DPI API。默认情况下,.NET(尤其是 WPF)会自动处理 DPI 感知,但仅基于系统 DPI。如果您的显示器具有不同的 DPI 设置,它将表现为非 DPI 感知(更准确地说,系统 DPI 感知,但最终结果是您的应用程序图形将由 Windows 虚拟化)。我希望如果您断开第二个显示器的连接,您的应用程序将在您的唯一显示器上按预期运行(至少在手动设置 DPI 之后,无论值如何)。

【讨论】:

  • 感谢您,这很有帮助。你知道是否有办法同时获得两台显示器的 DPI 吗?给出的示例在显示器之间移动 WPF 应用程序时有效(笔记本电脑上为 120,辅助设备上为 96),但我需要在不将应用程序移动到那里的情况下获取第二台显示器的 DPI。
  • @Issac 要获取每个监视器的 DPI,您需要通过 P/Invoke 调用 GetDpiForMonitor 函数。要获得 Per-Monitor DPI 的一般概述,请查看Writing DPI-Aware Desktop and Win32 Applications
  • @IsaacLevin 您确定需要这样做吗?如果您确保您的应用程序支持监视器 DPI,您将获得正确的屏幕边界。您是否只是想将分辨率乘以 DPI 之类的?正如Screen.Bounds 所报告的那样,该表单实际上在另一个显示器上的空间会更少。
  • @Luaan,我正在尝试获取两个显示器的边界和 DPI。目前,我的流程所做的是获取两个监视器(通过调用获取 AllScreens 成员)然后检索每个监视器的边界并操作图像以适应这些边界。我遇到的问题是我的笔记本电脑显示器的分辨率是错误的,而我的辅助显示器是正确的。我已经设置了 DPI 感知,但两者的 DPI 都是 96.0。我不确定我错过了什么,但我知道我还需要更进一步。
  • @IsaacLevin 听起来您仍然只知道系统 DPI,而不知道监视器 DPI。您是否在任务管理器(或资源管理器,忘记了)中验证了这一点?如果您深入挖掘,它应该显示操作系统兼容性(Windows 8.1)和 DPI 感知(监视器-DPI 感知)。
【解决方案2】:

屏幕截图工具也有同样的问题。 我找到了解决方案,它对我有用。

private enum ProcessDPIAwareness
{
  ProcessDPIUnaware = 0,
  ProcessSystemDPIAware = 1,
  ProcessPerMonitorDPIAware = 2
}

[DllImport("shcore.dll")]
private static extern int SetProcessDpiAwareness(ProcessDPIAwareness value);

private static void SetDpiAwareness()
{
  try
  {
    if (Environment.OSVersion.Version.Major >= 6)
    {
      SetProcessDpiAwareness(ProcessDPIAwareness.ProcessPerMonitorDPIAware);
    }
  }
  catch (EntryPointNotFoundException)//this exception occures if OS does not implement this API, just ignore it.
  {
  }
}

您应该在调用函数之前调用 SetDpiAwareness() 方法以获取系统分辨率等。 此外,如果您的应用程序中现在有一些 UI,则您有责任在高 DPI 的屏幕中缩放 UI。

希望这会有所帮助。

【讨论】:

  • 谢谢!这是在 Windows 8.1 中帮助我进行屏幕共享程序和缩放的唯一解决方案
  • 一件事,在 Windows 7 上,此代码不是缩放分辨率所必需的,但如果此代码在 Windows 7 上运行,try catch 不会捕获抛出的错误,它会崩溃程序。您需要更改它正在检查的错误,或者在我的情况下,我只是将其设置为捕获任何异常。
  • 这实际上不适用于 wpf 应用程序。请参阅第二个答案:stackoverflow.com/questions/23551112/…
猜你喜欢
  • 2016-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
相关资源
最近更新 更多