75

我在 C# 中有一个表单应用程序。当我更改显示器的 DPI 时,所有控件都会移动。我使用了代码this.AutoScaleMode = AutoScaleMode.Dpi,但它并没有避免这个问题。

有人有想法吗?

4

9 回答 9

117

编辑:从 .NET 4.7 开始,Windows 窗体改进了对高 DPI 的支持。在 docs.microsoft.com 上阅读有关它的更多信息。它仅适用于 Win 10 Creators Update 及更高版本,因此根据您的用户群,使用它可能还不可行。


困难,但并非不可能。当然,您最好的选择是迁移到 WPF,但这可能不可行。

我花了很多时间来解决这个问题。以下是一些规则/指南,可以在没有 FlowLayoutPanel 或 TableLayoutPanel 的情况下使其正常工作:

  • 始终以默认 96 DPI (100%) 编辑/设计您的应用程序。如果您以 120DPI (125% f.ex) 进行设计,当您稍后回到 96 DPI 使用它时,它会变得非常糟糕。
  • 我已经成功使用了 AutoScaleMode.Font,但我还没有尝试过 AutoScaleMode.DPI。
  • 确保在所有容器(表单、面板、标签页、用户控件等)上使用默认字体大小。8,25 像素。最好不要在所有容器的 .Designer.cs 文件中设置它,以便它使用容器类的默认字体。
  • 所有容器必须使用相同的AutoScaleMode
  • 确保所有容器都在 Designer.cs 文件中设置了以下行:

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); // for design in 96 DPI

  • 如果您需要在标签/文本框等上设置不同的字体大小,请为每个控件设置它们,而不是在容器类上设置字体,因为 winforms 使用容器字体设置来缩放其内容并让 f.ex 面板具有不同的字体大小比它包含的形式保证会产生问题。如果表单和表单上的所有容器使用相同的字体大小,它可能会起作用,但我还没有尝试过。
  • 使用具有更高 DPI 设置的另一台机器或虚拟 Windows 安装(VMware、Virtual PC、VirtualBox)立即测试您的设计。只需从 DEV 机器上的 /bin/Debug 文件夹运行编译的 .exe 文件。

我保证,如果您遵循这些准则,您会没事的,即使您已经放置了带有特定锚点的控件并且不使用流程面板。我们有一个以这种方式构建的应用程序部署在数百台具有不同 DPI 设置的机器上,我们不再有任何抱怨。所有表单/容器/网格/按钮/文本字段等大小都像字体一样正确缩放。图像也可以,但在高 DPI 下它们往往会有点像素化。

编辑:此链接有很多很好的信息,特别是如果您选择使用 AutoScaleMode.DPI:链接到相关的 stackoverflow 问题

于 2010-11-02T09:14:28.023 回答
33

注意:当 dpi 更改时,这不会修复控件移动。这只会修复模糊的文字!!


如何在高 dpi 设置中修复模糊的 Windows 窗体:

  1. 转到表单设计器,然后选择您的表单(通过单击其标题栏)
  2. 按 F4 打开属性窗口,
  3. 然后找到AutoScaleMode属性
  4. 将其从Font (默认)更改为Dpi

现在,转到 Program.cs(或 Main 方法所在的文件)并将其更改为:

namespace myApplication
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            // ***this line is added***
            if (Environment.OSVersion.Version.Major >= 6)
                SetProcessDPIAware();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }

        // ***also dllimport of that function***
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool SetProcessDPIAware();
    }
}

保存并编译。现在你的表格应该看起来又脆了。


来源: http ://crsouza.com/2015/04/13/how-to-fix-blurry-windows-forms-windows-in-high-dpi-settings/

于 2018-08-29T09:57:40.420 回答
16

我终于找到了解决屏幕方向和 DPI 处理问题的方法。
微软已经提供了一份解释它的文档,但有一个小缺陷会完全破坏 DPI 处理。只需按照下面“为每个方向创建单独的布局代码”下文档中提供的解决方案 http://msdn.microsoft.com/en-us/library/ms838174.aspx

然后是重要的部分!在 Landscape() 和 Portrait() 方法的代码中,在每个方法的最后添加以下几行:

this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;

因此,这两种方法的代码如下:

protected void Portrait()
{
   this.SuspendLayout();
   this.crawlTime.Location = new System.Drawing.Point(88, 216);
   this.crawlTime.Size = new System.Drawing.Size(136, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(10, 216);
   this.crawlTimeLabel.Size = new System.Drawing.Size(64, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(88, 200);
   this.crawlStartTime.Size = new System.Drawing.Size(136, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(10, 200);
   this.crawlStartedLabel.Size = new System.Drawing.Size(64, 16);
   this.light1.Location = new System.Drawing.Point(208, 66);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(192, 66);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(88, 182);
   this.linkCount.Size = new System.Drawing.Size(136, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(10, 182);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 84);
   this.currentPageBox.Size = new System.Drawing.Size(214, 90);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 68);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(214, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(10, 48);
   this.noProxyCheck.Size = new System.Drawing.Size(214, 20);
   this.startButton.Location = new System.Drawing.Point(8, 240);
   this.startButton.Size = new System.Drawing.Size(216, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 24);
   this.addressBox.Size = new System.Drawing.Size(214, 22);

   //note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
   this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;   //IMPORTANT
   this.ResumeLayout(false);
}

protected void Landscape()
{
   this.SuspendLayout();
   this.crawlTime.Location = new System.Drawing.Point(216, 136);
   this.crawlTime.Size = new System.Drawing.Size(96, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(160, 136);
   this.crawlTimeLabel.Size = new System.Drawing.Size(48, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(64, 120);
   this.crawlStartTime.Size = new System.Drawing.Size(248, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(8, 120);
   this.crawlStartedLabel.Size = new System.Drawing.Size(48, 16);
   this.light1.Location = new System.Drawing.Point(296, 48);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(280, 48);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(80, 136);
   this.linkCount.Size = new System.Drawing.Size(72, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(8, 136);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 64);
   this.currentPageBox.Size = new System.Drawing.Size(302, 48);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 48);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(50, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(168, 16);
   this.noProxyCheck.Size = new System.Drawing.Size(152, 24);
   this.startButton.Location = new System.Drawing.Point(8, 160);
   this.startButton.Size = new System.Drawing.Size(304, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 20);
   this.addressBox.Size = new System.Drawing.Size(150, 22);

   //note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
   this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;   //IMPORTANT
   this.ResumeLayout(false);
}

对我来说就像魅力一样。

于 2012-04-12T22:59:43.540 回答
5

看起来这是 Windows 的问题。取出这两条线可以解决所有问题。

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

这是我得到解决方案的地方:

于 2017-03-30T19:31:20.590 回答
4

在 Windows 窗体中设计 DPI 感知应用程序真的很困难。您必须使用在 DPI 更改时正确调整大小的布局容器(例如 TableLayoutPanel 或 FlowLayoutPanel)。所有控件也需要调整大小。这些容器的配置可能是一个挑战。

对于简单的应用程序,它可以在合理的时间内完成,但对于大型应用程序,这确实是很多工作。

于 2010-11-02T08:08:30.860 回答
3

根据经验:

  • 除非关键,否则不要对 Windows 窗体使用 DPI 感知
  • 为此,始终将AutoScaleMode属性设置为None应用程序中的所有表单和用户控件
  • 结果:DPI 设置更改时的所见即所得界面类型
于 2010-11-02T08:53:44.987 回答
2

我为此苦苦挣扎了一段时间,最终我找到了适用于 Windows 10 和可能的其他系统的超级简单的解决方案。

在你的 WinFormsApp.config文件中粘贴这个:

<System.Windows.Forms.ApplicationConfigurationSection>
    <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>

然后创建一个app.manifest文件并在此行中粘贴或注释:

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

完成上述操作后,我能够在 4k 屏幕上获得出色的 DPI 结果。

查看以获取更多信息。

于 2021-02-17T22:47:24.360 回答
1
  1. 如果您希望您的 WinForms 应用程序是 DPI-Aware 应用程序,除了 Trygve 好的答案之外,如果您有大项目,您可能希望自动缩放表单及其内容,您可以通过创建 ScaleByDPI 函数来做到这一点:

ScaleByDPI 函数将接收一个通常是表单的 Control 参数,然后递归地遍历所有子控件(如果 (control.HasChildren == true)),并将应用程序控件的位置和大小以及字体的大小和大小缩放到操作系统配置的 DPI。您也可以尝试为图像、图标和图形实现它。

ScaleByDPI 函数特别说明:

一种。对于所有具有默认字体大小的控件,您需要将其 Font.Size 设置为 8.25。

湾。您可以通过 (control.CreateGraphics().DpiX / 96) 和 (control.CreateGraphics().DpiY / 96) 获取 devicePixelRatioX 和 devicePixelRatioY 值。

C。您将需要通过基于 control.Dock 和 control.Anchor 值的算法来缩放 Control.Size 和 Control.Location。请注意,control.Dock 可能有 6 个可能值中的 1 个,而 control.Anchor 可能有 16 个可能值中的 1 个。

d。该算法需要为下一个布尔变量 isDoSizeWidth、isDoSizeHeight、isDoLocationX、isDoLocationY、isDoRefactorSizeWidth、isDoRefactorSizeHeight、isDoRefactorLocationX、isDoRefactorLocationY、isDoClacLocationXBasedOnRight、isDoClacLocationYBasedOnBottom 设置值。

e. 如果您的项目使用 Microsoft 控件以外的控件库,则此控件可能需要特殊处理。

有关上述 (d.) bool 变量的更多信息:

*有时一组控件(可能是一个按钮)需要一个接一个的放置在同一垂直线上,并且它们的Anchor值包含Right但不包含Left,或者需要一个接一个的放置在同一水平线上,并且它们的锚点值包括底部但不包括顶部,在这种情况下您需要重新计算控件位置值。

*如果 Anchor 包含 Top & Bottom 和/或 Left & Right 的控件,您将需要重新考虑控件的大小和位置值。

ScaleByDPI 函数的使用:

一种。将下一个命令添加到任何 Form 构造函数的末尾:ScaleByDPI(this);

湾。此外,当动态地将任何控件添加到对 ScaleByDPI([ControlName]) 的表单调用时。

  1. 当您在构造函数结束后动态设置任何控件的大小或位置时,创建并使用下一个函数之一以获得大小或位置的缩放值:ScaleByDPI_X \ ScaleByDPI_Y \ ScaleByDPI_Size \ ScaleByDPI_Point

  2. 为了将您的应用程序标记为可识别 DPI,请将 dpiAware 元素添加到应用程序的程序集清单中。

  3. 将所有 Control.Font 的 GraphicsUnit 设置为 System.Drawing.GraphicsUnit.Point

  4. 在所有容器的 *.Designer.cs 文件中,将 AutoScaleMode 值设置为 System.Windows.Forms.AutoScaleMode.None

  5. 在 ComboBox 和 TextBox 等控件中,更改 Control.Size.Hieght 没有任何影响。在这种情况下,更改 Control.Font.Size 将修复控件的高度。

  6. 如果窗体 StartPosition 的值为 FormStartPosition.CenterScreen,则需要重新计算窗口的位置。

于 2015-03-09T09:17:57.583 回答
-1

由于 Winform 应用程序表单可能包含控件和图像,因此允许系统调整您的窗口大小不是一个解决方案,但如果您可以设法让每个 DPI 分辨率的表单具有适当缩放的图像......这不是一个好主意,因为随着屏幕大小的增加,字体大小会减小。

当使用不同的 DPI 分辨率时,系统会强制您的表单重新定义其控件的大小、位置和字体,但不是图像,解决方案是在加载时在运行时更改表单的 DPI,以便一切恢复到原始大小和位置。

这是可能的解决方案,我已经使用纸牌游戏应用程序对其进行了测试,其中我有大约 80 个图像按钮、TabControls 等。

在每个表单 form_Load 事件中,添加以下代码片段:

  Dim dpi As Graphics = Me.CreateGraphics
    Select Case dpi.DpiX
        Case 120
            '-- Do nothing if your app has been desigbned with 120 dpi
        Case Else
    '-- I use 125 AND NOT 120 because 120 is 25% more than 96
            Me.Font = New Font(Me.Font.FontFamily, Me.Font.Size * 125 / dpi.DpiX)
    End Select

此外,在同一台计算机上测试各种分辨率的快速技巧,无需重新启动:

从控制面板,更改分辨率。不要重启!而是关闭您的会话并使用同一用户打开一个新会话。

还有一个警告:如果您在运行时设置控件的大小和位置,那么您应该将相同的 DPI 因子(例如 125 / Dpi.Dpix)应用于新坐标。所以你最好从 application.startup 事件中设置一个 DPIFactor 全局变量。

最后但并非最不重要的:

不要在 Visual Studio 中从原始分辨率以外的其他分辨率打开您的应用程序,否则您的所有控件将在您打开每个表单时移动和调整大小,并且无法返回...

希望这会有所帮助,快乐的编程。

于 2010-12-30T16:33:18.220 回答