Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to scale on high-DPI the image of a Windows Form Button?

I cannot find a way to make scale the image in a Windows Form Button. See below what it looks like with the Windows Form designer shown on DPI 200% (I am aware that the Windows Form designer should be only used on DPI 100% / 96, this screenshot just illustrates properly my point).

While the button size get scaled properly (34x33), the image in the button size doesn't get scaled/stretched/zoomed (it remains 16x16). I did many attempt to solve this:

  • The parent control AutoScaleMode is set to Font, setting it to Dpi doesn't make this work.
  • Setting button AutoSize to true or false doesn't make it work.
  • Setting button or parent controlAutoSizeMode to any value doesn't make it work.
  • there is no Button.ImageLayout that could be set to Stretch or Zoom.
  • Using the new App.Config setting <add key="EnableWindowsFormsHighDpiAutoResizing" value="true" /> doesn't make it work.
  • Changing button FlatStyle or ImageAlign doesn't make it work.

How did you get solved this in your app?

Windows Form Button Image doesn't scale

like image 235
Patrick from NDepend team Avatar asked Nov 12 '14 10:11

Patrick from NDepend team


People also ask

How do I resize a Windows Form?

By dragging either the right edge, bottom edge, or the corner, you can resize the form. The second way you can resize the form while the designer is open, is through the properties pane. Select the form, then find the Properties pane in Visual Studio. Scroll down to size and expand it.

How do I change my DPI scaling in Windows?

Select Display > Change the size of text, apps, and other items, and then adjust the slider for each monitor. Right-click the application, select Properties, select the Compatibility tab, and then select the Disable display scaling on high DPI settings check box.

How do I fit windows form to any resolution?

simply set Autoscroll = true for ur windows form..


1 Answers

So despite the MS philosophy is to go toward out-of-the-box stretched images for Windows Form Controls when high DPI, it seems images on Button need to be stretched manually. Of course an even better solution would be that, for each bitmap shown to user (on button and everywhere else) to define several bitmaps adapted to 250% 200% 150% and 125% DPI.

Here is the code:

  public static IEnumerable<IDisposable> AdjustControlsThroughDPI(this Control.ControlCollection controls) {
     Debug.Assert(controls != null);
     if (DPIRatioIsOne) {
        return new IDisposable[0]; // No need to adjust on DPI One
     }

     var list = new List<IDisposable>();
     foreach (Control control in controls) {
        if (control == null) { continue; }

        var button = control as ButtonBase;
        if (button != null) {
           button.AdjustControlsThroughDPI(list);
           continue;
        }

        // Here more controls tahn button can be adjusted if needed...

        // Recursive
        var nestedControls = control.Controls;
        Debug.Assert(nestedControls != null);
        if (nestedControls.Count == 0) { continue; }
        var disposables = nestedControls.AdjustControlsThroughDPI();
        list.AddRange(disposables);
     }
     return list;
  }

  private static void AdjustControlsThroughDPI(this ButtonBase button, IList<IDisposable> list) {
     Debug.Assert(button != null);
     Debug.Assert(list != null);
     var image = button.Image;
     if (image == null) { return; }

     var imageStretched = image.GetImageStretchedDPI();
     button.Image = imageStretched;
     list.Add(imageStretched);
  }


  private static Image GetImageStretchedDPI(this Image imageIn) {
     Debug.Assert(imageIn != null);

     var newWidth = imageIn.Width.MultipliedByDPIRatio();
     var newHeight = imageIn.Height.MultipliedByDPIRatio();
     var newBitmap = new Bitmap(newWidth, newHeight);

     using (var g = Graphics.FromImage(newBitmap)) {
        // According to this blog post http://blogs.msdn.com/b/visualstudio/archive/2014/03/19/improving-high-dpi-support-for-visual-studio-2013.aspx
        // NearestNeighbor is more adapted for 200% and 200%+ DPI
        var interpolationMode = InterpolationMode.HighQualityBicubic;
        if (s_DPIRatio >= 2.0f) {
           interpolationMode = InterpolationMode.NearestNeighbor;
        }
        g.InterpolationMode = interpolationMode;
        g.DrawImage(imageIn, new Rectangle(0, 0, newWidth, newHeight));
     }

     imageIn.Dispose();
     return newBitmap;
  }

Notice that an enumerable of disposable bitmaps created is returned. If you don't care disposing bitmap on buttons, you won't have to care for disposing stretched bitmap.

Notice we dispose original buttons bitmaps.

Notice our own members to deal with DPI: MultipliedByDPIRatio(this int) , DPIRatioIsOne:bool , s_DPIRatio. You can write your own, the tricky point is to obtain the actual DPI ratio. To gather DPI ratio the best way I found is this one.

Notice the reference to the blog post Improving High-DPI support for Visual Studio 2013 where the VS team explains that for their icon style, they determine that image stretched between ] 200%, 100% [ is best achieved with Bicubic algorithm, and above or equal to 200%, is best achieved with naive nearest neighbor algorithm. The code presented reflects these choices.


Edit: below screenshot of various interpolation mode at 200% DPI, IMHO InterpolationMode.HighQualityBicubic is better than InterpolationMode.NearestNeighbor.

Interpolation mode

like image 158
Patrick from NDepend team Avatar answered Oct 25 '22 13:10

Patrick from NDepend team