创建DPI感知应用程序

时间:2010-11-02 07:56:27

标签: c# dpi dpi-aware

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

有没有人有想法?

9 个答案:

答案 0 :(得分:102)

编辑:从.NET 4.7开始,Windows窗体改进了对高DPI的支持。 Read more about it on 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:link to related stackoverflow question

答案 1 :(得分:14)

我终于找到了屏幕方向和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);
}

对我来说就像魅力一样。

答案 2 :(得分:5)

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

  1. 转到“表单”设计器,然后选择您的“表单”(通过单击 其标题栏)
  2. 按F4打开“属性”窗口,
  3. 然后找到 AutoScaleMode 属性
  4. 将其从字体(默认)更改为 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/

答案 3 :(得分:4)

看起来这是Windows的一个问题。取出这两条线修好了一切。

ng build --base-href=/path/to/your/application/dist/

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

答案 4 :(得分:3)

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

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

答案 5 :(得分:2)

根据经验:

  • 除非关键
  • ,否则不要对Windows窗体使用DPI感知
  • 为此,请始终在应用中的所有表单和用户控件上将AutoScaleMode属性设置为None
  • 结果:DPI设置更改时的WYSIWYG类型界面

答案 6 :(得分:1)

  1. 如果你想让你的WinForms应用程序成为DPI-Aware应用程序,除了Trygve一个很好的答案,如果你有大项目你可能想要自动扩展你的表单和他们的内容,你可以通过创建ScaleByDPI函数来做到这一点: / LI>

    ScaleByDPI函数将接收一个通常是表单的Control参数,然后递归遍历所有子控件(if(control.HasChildren == true)),并缩放应用程序控件的大小和位置。 OS配置DPI的字体大小和大小。您也可以尝试将其用于图像,图标和图像。图形。

    ScaleByDPI功能的特别说明:

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

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

    ℃。你需要规模Control.Size& Control.Location基于控制的算法.Dock& control.Anchor值。请注意,control.Dock可能有6个可能值中的1个和该控件.Anchor可能有16个可能值中的1个。

    d。此算法需要设置值为下一个bool变量isDoSizeWidth,isDoSizeHeight,isDoLocationX,isDoLocationY,isDoRefactorSizeWidth,isDoRefactorSizeHeight,isDoRefactorLocationX,isDoRefactorLocationY,isDoClacLocationXBasedOnRight,isDoClacLocationYBasedOnBottom。

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

    以上(d。)bool变量的更多信息:

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

    *如果控件中Anchor包含Top&底部和\或左边&是的,你需要重新控制尺寸和尺寸。位置值。

    ScaleByDPI功能的使用:

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

    湾此外,当向FormB调用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,则需要重新计算窗口的位置。

答案 7 :(得分:1)

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

在您的 WinForms App.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 结果。

查看 thisthis 了解更多信息。

答案 8 :(得分:-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中使用除原始分辨率之外的其他分辨率打开您的应用程序,否则当您打开每个表单时,所有控件都会移动并调整大小,并且无法返回......

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