6

We have an existing windows .net application in which it uses all custom controls.

Things were fine these many days as we were just supporting the default 96 dpi resolution.

Now that we need our application to be dpi-aware. Things are going haywire when we go to next

higher resolution 120 or 144 etc.

Most common issues

  1. Bitmap scaling issues and

  2. Text cut-off

After going through these MSDN docs and existing SO questions I tried to

incorporate such a fix in vain inside my application (since all the controls used are

customized and are not dpi-aware).

Things I tried after modifying my application.manifest to enable the dpi-awareness flag and setting the AutoScaleMode to AutoScaleMode.Dpi on the main form and other forms use the AutoScaleMode as Inherit

  1. Changed the control Font inside the OnLoad event

Graphics g = this.CreateGraphics(); int dpi = int.Parse(g.DpiX.ToString());

    switch (dpi)
    {
        case 125:
            this.Font = new Font(this.Font.FontFamily, this.Font.Size * 125.0f / (float)(g.DpiX));
            Debug.WriteLine("<<---- Selected DPI Resolution :" + " 125 DPI ---->"); 
            break;
    ... so on
  1. Tried overriding the ScaleControl method to make use of different scaling factor based on DPI

But all these never seem to work in my case.

Does anyone know / suggest me a better approach to tackle this issue.

Thanks

VATSAG

Community
  • 1
  • 1
this-Me
  • 2,139
  • 6
  • 43
  • 70
  • 1
    This `int dpi = int.Parse(g.DpiX.ToString());` shakes something inside me. Regarding issue, I think, you shouldn't check for DPI via switch. Use formulas **always**, formulas should work for any dpi. You may need to rework layout, use only auto-sized `Label`s and put them inside `TableLayoutPanel` (to guarantee what controls do not overlap), etc. Bitmaps should be fine if inside `PictureBox`. If you mean your custom controls, then again - formulas+layouting. – Sinatr Nov 26 '13 at 06:56
  • Sorry if i sound stupid here..What do you mean by formulas here ? – this-Me Nov 26 '13 at 07:05
  • To example, for images: `new_width = width * scale * old_dpi / new_dpi;`. – Sinatr Nov 27 '13 at 06:44

1 Answers1

7

So, I just had the same problem in one of my applications. I managed to go around it in a 2 step process, which unfortunately requires a LOT of refactoring, but after the work was done, I managed to get my application to scale on different DPI's automatically. Here's how it goes:

  1. All your forms must be set to scale using AutoScaleMode = AutoScaleMode.Font. When I did some dig-up I found out that AutoScaleMode.Dpi doesn't quite work as one would expect it to. You should also choose what your standard DPI unit will be. Say that it's 96, since that's what your application was originally designed in anyways, then you must set all your forms to use AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F). You can set these configurations from the designer view. Also, if you've created any custom controls, they should all be set to use AutoScaleMode = AsutoScaleMode.Inherit. With these two things in place, you can rest assured that .NET will do all the scaling on all your static components.

  2. Now's when it gets tricky. The thing is that the framework will maintain all your controls about the right size as long as they aren't dynamically created and placed inside the form.

So if you do something like:

var myTextBox = new TextBox();
myTextBox.Location = new System.Drawing.Point(20, 20);
myForm.Controls.Add(myTextBox);

then that text box will be placed at location (20, 20) no matter what your DPI is, hence causing it to look out of place on high DPI monitors. So the solution is to NEVER use hard coded pixel values, instead use values that scale dynamically, depending on your current DPI configuration. So, instead you may want to write something like:

var graphics = Graphics.FromHwnd(IntPtr.Zero); // This gets the graphics configuration of the current screen
var scaleX = graphics.DpiX / 96; // 96 was our standard design DPI, remember?
var scaleY = graphics.DpiY / 96;
var myTextBox = new TextBox();

myTextBox.Location = new System.Drawing.Point((int)Math.Round(20 * scaleX), (int)Math.Round(20 * scaleY));

myForm.Controls.Add(myTextBox);

That way the location at which you'll draw the control on the screen will depend on the actual DPI. And, if I may suggest, I would actually extract the scaling functionality into some sort of helper class, where you define methods to scale a value on x and on y, so you'll have a standard scaling mechanism. Note that it is also important that you scale independently on x and y, since some screens have different pixel densities on each axis.

Anyways, if you follow these two conditions, your application should look just fine independent of the screen it's displayed on.

Parth Sane
  • 587
  • 7
  • 24
cgledezma
  • 612
  • 6
  • 11
  • If I drag the form from my main monitor (175% scale) to 2nd monitor, the Font and Form size are scaled by system, but the hard code in desinger private void InitializeComponent() can't execute any more. Any more update on this solution? – Jonney Jun 20 '22 at 01:49
  • If I use DpiChanged event, when I drag to 2nd monitor, it keeps triggering and freezes my app. private void FrmDemo_DpiChanged(object sender, System.Windows.Forms.DpiChangedEventArgs e) { count++; Debug.Print(count.ToString());} – Jonney Jun 20 '22 at 02:53
  • It's OK. I remove this.Location calculation in DpiChanged. Your solution works! (I maintain AutoScaleDimensions = new System.Drawing.SizeF(96f, 96f); AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;, All re-calculations are hard code.) – Jonney Jun 20 '22 at 03:42