2014-11-12 11 views
5

Non riesco a trovare un modo per ridimensionare l'immagine in un pulsante di Windows Form. Vedi sotto come appare con la finestra di progettazione di Windows Form visualizzata su DPI 200% (sono consapevole che la finestra di progettazione di Windows Form dovrebbe essere utilizzata solo su DPI 100%/96, questa schermata illustra correttamente il mio punto).Come ridimensionare su DPI alto l'immagine di un pulsante Windows Form?

Mentre le dimensioni del pulsante vengono ridimensionate correttamente (34x33), l'immagine nella dimensione del pulsante non viene ridimensionata/allungata/ingrandita (rimane 16x16). Ho fatto molti tentativi per risolvere questo:

  • Il controllo padre AutoScaleMode è impostato su Font, impostandolo a Dpi non fa questo lavoro.
  • Il pulsante di impostazione AutoSize a true o false non funziona.
  • Il pulsante di impostazione o il controllo genitore AutoSizeMode a qualsiasi valore non lo fa funzionare.
  • non c'è Button.ImageLayout che potrebbe essere impostato su Stretch o Zoom.
  • L'utilizzo della nuova impostazione App.Config<add key="EnableWindowsFormsHighDpiAutoResizing" value="true" /> non funziona.
  • Il pulsante di cambio FlatStyle o ImageAlign non funziona.

Come hai risolto questo nella tua app?

Windows Form Button Image doesn't scale

+0

si sta chiedendo un pony . Puoi facilmente aggiungere il codice per ridimensionare le bitmap, l'unica garanzia che otterrai è che odierai assolutamente il suo aspetto. –

+0

Creare una nuova bitmap e ridimensionarla è la soluzione di hacky/piano B, ovviamente. Questa è la responsabilità del controllo di allungare la sua immagine e qui, voglio davvero assicurarmi di non aver perso una soluzione pulita. –

+2

Questa è la parte misteriosa di questa domanda. Se sai che il ridimensionamento della bitmap è un brutto attacco, perché sulla Terra ti aspetteresti che una classe .NET standard lo usi? Quella "responsabilità" che ci si aspetta semplicemente non è lì. La gestione delle risorse per un programma dpiAware è compito di un programmatore. L'implementazione è piuttosto banale, ma non avrai mai alcun aiuto finché fai la domanda in questo modo. –

risposta

4

Così, nonostante la filosofia MS è to go toward out-of-the-box stretched images for Windows Form Controls when high DPI, a quanto pare le immagini sul pulsante devono essere allungato manualmente. Naturalmente una soluzione ancora migliore sarebbe quella, per ogni bitmap mostrata all'utente (sul pulsante e ovunque) per definire diverse bitmap adattate al 250% 200% 150% e 125% DPI.

Ecco il codice:

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

Si noti che un enumerabile di bitmap creati monouso viene restituito. Se non ti interessa disporre la bitmap sui pulsanti, non dovrai preoccuparti di eliminare la bitmap tesa.

Avviso disponiamo bitmap di pulsanti originali.

Avviso i nostri membri per trattare con DPI: MultipliedByDPIRatio(this int), DPIRatioIsOne:bool, s_DPIRatio. Puoi scrivere il tuo, il punto difficile è ottenere il rapporto DPI effettivo. Per raccogliere il rapporto DPI il modo migliore che ho trovato è this one.

Notare il riferimento al post del blog Improving High-DPI support for Visual Studio 2013 dove il team VS spiega che per il loro stile di icona, determinano che l'immagine viene allungata tra] 200%, 100% [si ottiene il migliore con l'algoritmo bicubico e superiore o uguale al 200% , si ottiene al meglio con l'algoritmo naive più vicino. Il codice presentato riflette queste scelte.


Edit: sotto schermata della modalità di interpolazione various a 200% DPI, IMHO InterpolationMode.HighQualityBicubic è meglio di InterpolationMode.NearestNeighbor.

Interpolation mode

1

Ecco un pronto per l'uso classe di supporto in base alla risposta accettata che comprende il recupero della scala DPI, e aggiunge il supporto di una scalatura delle immagini PictureBox:

public static class HighDpiHelper 
{ 
    public static void AdjustControlImagesDpiScale(Control container) 
    { 
     var dpiScale = GetDpiScale(container).Value; 
     if (CloseToOne(dpiScale)) 
      return; 

     AdjustControlImagesDpiScale(container.Controls, dpiScale); 
    } 

    private static void AdjustButtonImageDpiScale(ButtonBase button, float dpiScale) 
    { 
     var image = button.Image; 
     if (image == null) 
      return; 

     button.Image = ScaleImage(image, dpiScale); 
    } 

    private static void AdjustControlImagesDpiScale(Control.ControlCollection controls, float dpiScale) 
    { 
     foreach (Control control in controls) 
     { 
      var button = control as ButtonBase; 
      if (button != null) 
       AdjustButtonImageDpiScale(button, dpiScale); 
      else 
      { 
       var pictureBox = control as PictureBox; 
       if (pictureBox != null) 
        AdjustPictureBoxDpiScale(pictureBox, dpiScale); 
      } 

      AdjustControlImagesDpiScale(control.Controls, dpiScale); 
     } 
    } 

    private static void AdjustPictureBoxDpiScale(PictureBox pictureBox, float dpiScale) 
    { 
     var image = pictureBox.Image; 
     if (image == null) 
      return; 

     if (pictureBox.SizeMode == PictureBoxSizeMode.CenterImage) 
      pictureBox.Image = ScaleImage(pictureBox.Image, dpiScale); 
    } 

    private static bool CloseToOne(float dpiScale) 
    { 
     return Math.Abs(dpiScale - 1) < 0.001; 
    } 

    private static Lazy<float> GetDpiScale(Control control) 
    { 
     return new Lazy<float>(() => 
     { 
      using (var graphics = control.CreateGraphics()) 
       return graphics.DpiX/96.0f; 
     }); 
    } 

    private static Image ScaleImage(Image image, float dpiScale) 
    { 
     var newSize = ScaleSize(image.Size, dpiScale); 
     var newBitmap = new Bitmap(newSize.Width, newSize.Height); 

     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 (dpiScale >= 2.0f) 
       interpolationMode = InterpolationMode.NearestNeighbor; 

      g.InterpolationMode = interpolationMode; 
      g.DrawImage(image, new Rectangle(new Point(), newSize)); 
     } 

     return newBitmap; 
    } 

    private static Size ScaleSize(Size size, float scale) 
    { 
     return new Size((int)(size.Width * scale), (int)(size.Height * scale)); 
    } 
} 
Problemi correlati