Creando una aplicación DPI-Aware

Tengo una aplicación de formulario en C #. Cuando cambio el DPI del monitor, todos los controles se mueven. this.AutoScaleMode = AutoScaleMode.Dpi el código this.AutoScaleMode = AutoScaleMode.Dpi , pero no evitó el problema.

¿Alguien tiene alguna idea?

EDITAR: a partir de .NET 4.7, los formularios de Windows han mejorado el soporte para DPI altos. Obtenga más información al respecto en docs.microsoft.com. Sin embargo , solo funciona para Win 10 Creators Update y versiones posteriores, por lo que podría no ser posible usarlo aún dependiendo de su base de usuarios.


Difícil, pero no imposible. Su mejor opción es pasar a WPF, por supuesto, pero eso podría no ser factible.

He pasado MUCHO tiempo con este problema. Aquí hay algunas reglas / pautas para que funcione correctamente sin un FlowLayoutPanel o TableLayoutPanel:

  • Siempre edite / diseñe sus aplicaciones por defecto 96 DPI (100%). Si diseña en 120DPI (125% f.ex) se pondrá realmente mal cuando vuelva a 96 DPI para trabajar más tarde.
  • He utilizado AutoScaleMode.Font con éxito, no he probado AutoScaleMode.DPI mucho.
  • Asegúrese de utilizar el tamaño de fuente predeterminado en todos sus contenedores (formularios, paneles, tabulaciones, controles de usuario, etc.). 8,25 px. Preferiblemente, no se debe establecer en el archivo .Designer.cs para todos los contenedores, de modo que use la fuente predeterminada de la clase contenedora.
  • Todos los contenedores deben usar el mismo AutoScaleMode
  • Asegúrese de que todos los contenedores tengan la siguiente línea establecida en el archivo Designer.cs:

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

  • Si necesita establecer diferentes tamaños de fuente en tags / cuadros de texto, etc. configúrelos por control en lugar de configurar la fuente en la clase de contenedor porque winforms utiliza la configuración de fuente de contenedores para escalar sus contenidos y tener un panel de f.ex con un tamaño de fuente diferente de lo que contiene la forma, garantiza problemas. Podría funcionar si el formulario y todos los contenedores en el formulario usan el mismo tamaño de fuente, pero no lo he intentado.
  • Utilice otra máquina o una instalación de Windows virtual (VMware, Virtual PC, VirtualBox) con una configuración DPI más alta para probar su diseño de inmediato. Simplemente ejecute el archivo .exe comstackdo de la carpeta / bin / Debug en la máquina DEV.

Te garantizo que si sigues estas pautas estarás bien, incluso cuando hayas colocado controles con anclajes específicos y no uses un panel de flujo. Tenemos una aplicación creada de esta manera implementada en cientos de máquinas con diferentes configuraciones de DPI y ya no tenemos ninguna queja. Todos los formularios / contenedores / grids / buttons / textfield, etc. tienen escalas correctas al igual que la fuente. Las imágenes también funcionan, pero tienden a quedar un poco pixeladas a un alto DPI.

EDITAR: Este enlace tiene mucha información buena, especialmente si eliges usar AutoScaleMode.DPI: enlace a la pregunta relacionada con stackoverflow

Finalmente encontré la solución al problema de la orientación de pantalla y el manejo de DPI.
Microsoft ya ha proporcionado un documento que lo explica, pero con un pequeño error que matará por completo el manejo DPI. Simplemente siga la solución que se proporciona en el documento a continuación en “Creación de un código de diseño independiente para cada orientación” http://msdn.microsoft.com/en-us/library/ms838174.aspx

Entonces parte IMPORTANTE! Dentro del código para los métodos Landscape () y Portrait () al final de cada uno, agregue estas líneas:

 this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; 

Entonces, el código para estos 2 métodos sería como:

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

Funciona como encanto para mí.

Parece que este es un problema con Windows. Sacar estas dos líneas lo solucionó todo.

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

Aquí es donde obtuve la solución:

  • Anchor no funciona durante el cambio del tamaño del texto a 125% @ foros de Telerik

Es realmente difícil diseñar aplicaciones compatibles con DPI en Windows Forms. Tendría que usar contenedores de diseño que cambian de tamaño correctamente cuando se cambia el DPI (como TableLayoutPanel o FlowLayoutPanel). Todos los controles necesitan redimensionamiento también. La configuración de esos contenedores puede ser un desafío.

Para aplicaciones simples, puede hacerse dentro de un tiempo razonable, pero para grandes aplicaciones es realmente mucho trabajo.

  1. Si desea que su aplicación WinForms sea una aplicación DPI-Aware, además de la buena respuesta de Trygve, si tiene un proyecto grande, puede querer escalar sus formularios y su contenido automáticamente. Puede hacerlo creando la función ScaleByDPI:

La función ScaleByDPI recibirá un parámetro de Control que generalmente es un formulario, y que iterará recursivamente a través de todos los controles secundarios (if (control.HasChildren == true)), y escalará la ubicación y los tamaños de los controles y tamaños y tamaños de las fonts a la Sistema operativo configurado. Puede intentar implementarlo también para imágenes, icons y gráficos.

Notas especiales para la función ScaleByDPI:

a. Para todos los controles con tamaños de fuente predeterminados, deberá establecer Font.Size en 8.25.

segundo. Puede obtener los valores de devicePixelRatioX y devicePixelRatioY por (control.CreateGraphics (). DpiX / 96) y (control.CreateGraphics (). DpiY / 96).

do. Necesitará control de escala.Tamaño y control.Localización por algoritmo basado en el control.Dock & control.Anchor values. Tenga en cuenta que control.Dock puede tener 1 de 6 valores posibles y ese control.Anchor puede tener 1 de 16 valores posibles.

re. este algoritmo necesitará valores establecidos para las siguientes variables bool isDoSizeWidth, isDoSizeHeight, isDoLocationX, isDoLocationY, isDoRefactorSizeWidth, isDoRefactorSizeHeight, isDoRefactorLocationX, isDoRefactorLocationY, isDoClacLocationXBasedOnRight, isDoClacLocationYBasedOnBottom.

mi. Si su proyecto utiliza una biblioteca de control distinta de los controles de Microsoft, es posible que estos controles necesiten un tratamiento especial.

Más información sobre las variables de bool anteriores (d.):

* A veces un grupo de controles (puede ser un botones) debe colocarse uno tras otro en la misma línea vertical, y su valor de Anclaje incluye Derecha pero no Izquierda, o deben colocarse uno tras otro en la misma línea horizontal, y su El valor de anclaje incluye Inferior pero no Superior, en este caso necesita recalcular los controles Valores de ubicación.

* En el caso de los controles que Anchor contiene Top & Bottom e \ o Left & Right, necesitará volver a factorizar los valores de tamaño y ubicación de los controles.

Usos de la función ScaleByDPI:

a. Agregue el siguiente comando al final de cualquier constructor de Formulario: ScaleByDPI (this);

segundo. Además, al agregar cualquier control dinámicamente a un Formulario, llame a ScaleByDPI ([ControlName]).

  1. Cuando establece el Tamaño o la Ubicación de cualquier control dinámicamente después de que termina el constructor, cree y use una de las siguientes funciones para obtener los valores escalados de Tamaño o Ubicación: ScaleByDPI_X \ ScaleByDPI_Y \ ScaleByDPI_Size \ ScaleByDPI_Point

  2. Para marcar su aplicación como compatible con DPI, agregue el elemento dpiAware al manifiesto de ensamblaje de su aplicación.

  3. Establecer la unidad de gráficos de todo el control. Fuente a System.Drawing.GraphicsUnit.Point

  4. En los archivos * .Designer.cs de todos los contenedores, establezca el valor de AutoScaleMode en System.Windows.Forms.AutoScaleMode.None

  5. en controles como ComboBox y TextBox, cambiar Control.Size.Hieght no tiene ningún efecto. En este caso, al cambiar Control.Font.Size se fijará la altura del control.

  6. Si el valor de StartPosition es FormStartPosition.CenterScreen, tendrá que volver a calcular la ubicación de la ventana.

Cómo arreglar formas borrosas de Windows en configuraciones de alta dpi

  1. Vaya al diseñador de Formularios, luego seleccione su Formulario (haciendo clic en su barra de título)
  2. Presione F4 para abrir la ventana de Propiedades,
  3. luego ubique la propiedad AutoScaleMode
  4. Cambiarlo de Fuente (predeterminado) a Dpi .

Ahora, vaya a Program.cs (o al archivo donde se encuentra su método Principal) y cámbielo para que se vea como sigue:

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

Guarde y compile. Ahora su forma debe verse crujiente de nuevo.


fuente: http://crsouza.com/2015/04/13/how-to-fix-blurry-windows-forms-windows-in-high-dpi-settings/

Por experiencia:

  • no use el reconocimiento de DPI con formularios de Windows a menos que sea crítico
  • para ello, establezca siempre la propiedad AutoScaleMode en None en todos los formularios y controles de usuario en su aplicación
  • El resultado: tipo de interfaz WYSIWYG cuando cambia la configuración de DPI

Dado que un formulario de solicitud de Winform puede controlar el contenido Y las imágenes, permitiendo que el sistema cambie el tamaño de SU ventana NO es una solución, pero si puede lograr tener un formulario por resolución DPI, con imágenes escaladas correctamente … Y esa no es una buena idea, dado que a medida que crece el tamaño de la pantalla, el tamaño de la fuente disminuye.

Al usar una resolución DPI diferente, el sistema obliga a su formulario a redefinir el tamaño, la ubicación y la fuente del control, PERO NO IMÁGENES, la solución es cambiar el DPI del formulario en tiempo de ejecución, al cargarlo, para que todo vuelva al tamaño original y la ubicación.

Esta es una solución posible, que probé con una aplicación de juegos de cartas donde obtuve unos 80 botones de imagen, TabControls, etc.

En cada formulario del evento form_Load, agregue este fragmento de código:

  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 

Además, un truco rápido para probar varias resoluciones en la misma computadora, sin reiniciar:

Desde el panel de control, cambia la resolución. No reiniciar! En su lugar, cierre su sesión y abra una nueva con el mismo usuario.

Hay otra advertencia: si establece el tamaño y la posición de un control en tiempo de ejecución, debe aplicar el mismo factor DPI (por ejemplo, 125 / Dpi.Dpix) a las nuevas coordenadas. Así que será mejor que configure una variable global DPIFactor desde el evento application.startup.

Por último, si bien no menos importante:

NO abra su aplicación en Visual Studio desde otra resolución que no sea la original, o TODOS SUS CONTROLES se moverán y cambiarán de tamaño a medida que abra cada formulario, y no hay forma de volver atrás …

Espero que esto ayude, progtwigción feliz.