创build一个DPI感知应用程序

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

有没有人有一个想法?

困难,但并非不可能。 你最好的select是转移到WPF当然,但这可能是不可行的。

这个问题我花了很多时间。 下面是一些规则/指导方针,使其在没有FlowLayoutPanel或TableLayoutPanel的情况下正常工作:

  • 始终以默认的96 DPI(100%)编辑/devise您的应用程序。 如果你devise的是120DPI(125%f.ex),那么当你回到96 DPI后面的时候会变得很糟糕。
  • 我用AutoScaleMode.Font成功,我还没有尝试过AutoScaleMode.DPI。
  • 确保在所有容器(表单,面板,tabpage,usercontrols等)上使用默认的字体大小。 8,25 px。 最好不要在所有容器的.Designer.cs文件中设置,以便它使用容器类中的默认字体。
  • 所有容器必须使用相同的AutoScaleMode
  • 确保所有容器都在Designer.cs文件中设置了下面的行:

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

  • 如果你需要在标签/文本框等设置不同的字体大小,而不是设置容器类的字体,而不是设置容器类的字体,因为winforms使用容器字体设置来缩放它的内容,并有f.ex一个不同的字体大小的面板比它包含的forms保证会造成问题。 它可能工作,如果窗体和窗体上的所有容器使用相同的字体大小,但我没有尝试过。
  • 使用另一台机器或具有更高DPI设置的虚拟Windows安装(VMware,Virtual PC,VirtualBox)立即testing您的devise。 只需从DEV机器上的/ bin / Debug文件夹运行编译后的.exe文件即可。

我保证,如果你遵循这些指导原则,即使你已经放置了具有特定锚点的控件,但不使用stream动面板,也可以。 我们有一个这样的应用程序,部署在数百台具有不同DPI设置的机器上,我们不再有任何抱怨。 所有表单/容器/网格/button/文本字段等大小正如字体一样正确缩放。 图像也可以工作,但是在高DPI的情况下,它们往往会有一点像素化。

编辑:这个链接有很多很好的信息,特别是如果你select使用AutoScaleMode.DPI: 链接到相关的stackoverflow问题

我终于find了解决屏幕方向和DPI处理问题的办法。
微软已经提供了一个解释它的文件,但有一点将会完全消除DPI处理的缺陷。 只需按照下面“为每个方向创build单独的布局代码”中的文档中提供的解决schemehttp://msdn.microsoft.com/en-us/library/ms838174.aspx

那么重要的一部分! 在每个最后的横向()和纵向()方法的代码里面添加这些行:

 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); } 

像我的魅力。

看起来这是Windows的一个问题。 拿出这两条线固定一切。

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

这是我得到的解决scheme:

  • 在将文本大小更改为125%@ Telerik论坛时,锚点不工作

在Windows窗体中devise支持DPI的应用程序是非常困难的。 您将不得不使用布局容器在DPI更改时正确resize(如TableLayoutPanel或FlowLayoutPanel)。 所有的控件都需要resize。 这些容器的configuration可能是一个挑战。

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

  1. 如果你想让你的WinForms应用程序成为DPI-Aware应用程序,除了Trygve很好的答案之外,如果你有很大的项目,你可能想自动调整你的表单和内容,你可以通过创buildScaleByDPI函数来做到这一点:

ScaleByDPI函数将接收通常为表单的Control参数,并recursion遍历所有子控件(if(control.HasChildren == true)),并将应用程序控件的大小和位置和大小以及字体的大小和大小缩放到操作系统configurationDPI。 你可以尝试实现它也为图像,图标和graphics。

ScaleByDPIfunction的特别注意事项:

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

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

C。 您将需要通过基于control.Dock&control.Anchor值的algorithm来调整Control.Size和Control.Location。 注意control.Dock可能有6个可能的值中的1个和控制。锚点可能有16个值中的1个。

d。 这个algorithm需要设置下一个boolvariables的值isDoSizeWidth,isDoSizeHeight,isDoLocationX,isDoLocationY,isDoRefactorSizeWidth,isDoRefactorSizeHeight,isDoRefactorLocationX,isDoRefactorLocationY,isDoClacLocationXBasedOnRight,isDoClacLocationYBasedOnBottom。

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

以上(d。)布尔variables的更多信息:

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

*如果控件的Anchor包含Top&Bottom和\或Left&Right,则需要重新设置控件大小和位置值。

ScaleByDPI函数的使用:

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

湾 同时dynamic添加任何控件到FormByDPI([ControlName])的窗体调用。

  1. 在构造函数结束之后dynamic设置任何控件的大小或位置时,请创build并使用下一个函数之一以获取大小或位置的缩放值: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,则需要重新计算窗口的位置。

从经验:

  • 除非严重,否则不要使用Windows窗体的DPI意识
  • 为此,始终将AutoScaleMode属性设置为None应用程序中的所有窗体和用户控件
  • 结果是:当DPI设置改变时所见即所得的接口types

由于Winform应用程序表单可能内容控制和图像,允许系统调整您的窗口大小不是一个解决scheme,但如果你可以设法每个DPI的决议有一个forms,适当的缩放图像…这不是一个好主意,因为随着屏幕尺寸的增大,字体大小会减小。

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

这是可能的解决scheme,我已经testing了一个纸牌游戏应用程序,其中我有80个图像button,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 

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

从控制面板,更改分辨率。 不要重新启动! 而是closures你的会议,并用同一个用户打开一个新的。

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

最后但并非最不重要的:

不要在Visual Studio中使用另一个分辨率打开应用程序,否则所有控件都会在您打开每个表单时移动和resize,并且没有办法返回…

希望这有助于,快乐的编程。