Desactivar el conocimiento de DPI para la aplicación WPF

¡Buen día!

He estado trabajando en una aplicación de WPF desde hace un tiempo (como una experiencia de aprendizaje y oh chico, fue una experiencia de aprendizaje) y finalmente está lista para su lanzamiento. Versión significa instalarlo en mi HTPC, donde se utilizará para explorar mi colección de películas.

Lo diseñé en mi PC, que funciona a 1920 * 1080 pero a la configuración normal de PPP, mientras que el HTPC / TV funciona con la misma resolución pero con una configuración de DPI más alta por razones obvias.

El problema es que mi aplicación se vuelve loca en el HTPC, arruinando casi todo en lo que se refiere a los efectos visuales. Sé que esto se debe a un mal diseño (mea culpa), pero como es una aplicación que solo usaré, estoy buscando una solución rápida, no un rediseño completo. Leí que sería posible evitar que la aplicación tenga conocimiento de DPI agregando lo siguiente a AssemblyInfo.cs:

[assembly: System.Windows.Media.DisableDpiAwareness] 

Sin embargo, no parece tener ningún efecto y el comportamiento de la aplicación sigue siendo el mismo.

¿Alguien podría señalarme en la dirección correcta?

Gracias, Johan

DPIAwareness

Solo algunas ideas (no probadas):

¿Estás corriendo en XP? Esa opción podría no funcionar en esa plataforma.

Lo que sigue probablemente sean solo diferentes formas de establecer la misma opción de DpiAwareness:

  • mira la configuración de “modo de compatibilidad” en tu EXE … haz clic derecho en sus propiedades … y activa “Desactivar escalado de pantalla”

  • crea un manifiesto, y di que no estás enterado

      ...   false   ...  
  • llama a SetProcessDPIAware (piensa que tienes que llamar esto antes, es decir, antes de que se cree Window)

    http://msdn.microsoft.com/en-gb/library/windows/desktop/ms633543(v=vs.85).aspx

Puede llamar a IsProcessDPIAware para verificar si su proceso de aplicaciones ha tenido la configuración aplicada o no.

Descubriendo el DPI

A continuación, le mostramos cómo averigua qué DPI utiliza actualmente:

Acceda a CompositionTarget través de PresentationSource de su Window para averiguar qué escala de PPP está usando … luego puede usar los valores para hacer algunos ajustes de escala, es decir, escalar sus “cosas” (cuyos tamaños / longitud / etc. se especifican en el Dispositivo Unidades independientes), de modo que cuando se amplíe debido a que esté en funcionamiento un DPI más alto, no explote el uso del píxel físico (… esto se puede hacer de varias maneras, por ejemplo, ViewBox , o cálculos de anchos, etc. en los elementos) .

 double dpiX, double dpiY; PresentationSource presentationsource = PresentationSource.FromVisual(mywindow); if (presentationsource != null) // make sure it's connected { dpiX = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M11; dpiY = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M22; } 

Hacer ajustes de escala

  • usa el truco ViewBox

    Ver esta respuesta que hice antes permitió que Canvas utilizara posiciones que se interpretaron como píxeles “nativos” independientemente de la escala de DPI.

    WPF para pantalla LCD Full HD

  • acceda a la matriz de escalamiento de TransFormToDevice en CompositionTarget , y a partir de eso calcule una nueva matriz que simplemente deshaga esa escala y la use en LayoutTransform o RenderTransform .

  • utilice un método (tal vez incluso lo coloque en un convertidor) que modifique una posición / longitud DIP (posición independiente del dispositivo) de forma explícita … puede hacerlo si desea que su tamaño de ventana coincida con un tamaño de píxel particular.

Este artículo de blog explica cómo usar una subclase Decorator para hacer precisamente eso.

En resumen, crea una clase como esta:

 namespace MyNamespace { public class DpiDecorator : Decorator { public DpiDecorator() { this.Loaded += (s, e) => { Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice; ScaleTransform dpiTransform = new ScaleTransform(1 / m.M11, 1 / m.M22); if (dpiTransform.CanFreeze) dpiTransform.Freeze(); this.LayoutTransform = dpiTransform; }; } }; }; 

A continuación, agregue algo como xmlns:custom="clr-namespace:MyNamespace" a su definición de ventana de nivel superior, luego encierre su interfaz de usuario independiente de DPI con .

Una solución rápida sería simplemente envolver los contenidos de su Ventana en una Viewbox . Si el hijo inmediato de Viewbox tiene un Ancho y una Altura explícitos, y si la relación de aspecto es la misma en ambas máquinas, entonces esto debería ponerlo en funcionamiento prácticamente de inmediato.

DpiAwareness solo afecta la virtualización DPI. En Windows se muestra como “Usar escala de estilo de Windows XP” en el cuadro de resolución personalizada de Windows 7 & 8, donde marcado significa deshabilitado y sin marcar significa habilitado. (¡Recuerde aplicar en la pantalla de tamaño de fuente principal si la cambia! Aceptar la casilla no es suficiente).

Si tiene desactivada la virtualización DPI, entonces no hay diferencia entre lo que su aplicación le dice al sistema operativo que admite, siempre usará escalado estándar. Cuando está activado, hace la diferencia entre la escala real (consciente) y simplemente aplicando un tamaño bilineal en toda la aplicación (sin darse cuenta).

Acabo de recordar otra advertencia, DPI virt no funcionará en absoluto sin Aero Glass funcionando.

Ninguno de los anteriores deshabilita realmente la escala de dpi en WPF.

Así es como se calcula el escalado de dpi en .net 4.6 por WPF: 1) HwndTarget que es el CompositionTarget utilizado por todos los efectos visuales. 2) UIElement, que es la clase base para visuales y el lugar donde se almacenan los resultados de cálculo de escalado de ppp.

WPF se asegura de que la escala global se calcule una vez cuando se prepara el primer visual. Esto está controlado por un booleano en HwndTarget {private static bool _setDpi = true}. Después de calcular los ppp, el campo _setDpi se establece en falso y los métodos que admiten ppp son accesos directos usando código como this {if (! HwndTarget._setDpi) return;}

Algo similar sucede con cada elemento UIElement, que tiene el mismo patrón con {private static bool _setDpi = true} para permitir el cálculo por primera vez.

La siguiente comprobación proviene de ProcessDpiAwareness, que puede ser None, Process o Monitor. Para evitar que WPF considere monitores individuales, debe configurar ProcessDpiAwareness para procesar (int 1).

Finalmente, cuando se calcula el dpi, el resultado se almacena en 2 listas denominadas DpiScaleXValues ​​y DpiScaleYValues ​​en UIElement. Por lo tanto, debe inicializar estos para corregir los valores.

Aquí hay un código de muestra que utilizo para mí (solo funciona para .net 4.6):

  var setDpiHwnd = typeof(HwndTarget).GetField("_setDpi", BindingFlags.Static | BindingFlags.NonPublic); setDpiHwnd?.SetValue(null, false); var setProcessDpiAwareness = typeof(HwndTarget).GetProperty("ProcessDpiAwareness", BindingFlags.Static | BindingFlags.NonPublic); setProcessDpiAwareness?.SetValue(null, 1, null); var setDpi = typeof(UIElement).GetField("_setDpi", BindingFlags.Static | BindingFlags.NonPublic); setDpi?.SetValue(null, false); var setDpiXValues = (List)typeof(UIElement).GetField("DpiScaleXValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); setDpiXValues?.Insert(0, 1); var setDpiYValues = (List)typeof(UIElement).GetField("DpiScaleYValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); setDpiYValues?.Insert(0, 1); 

Para quienes usan VB.NET, la forma de acceder a la aplicación .manifest es:

enter image description here

y luego descomentar esta parte y establecerla en “falso”

enter image description here

La respuesta de Calin Pirtea realmente deshabilita la escala DPI.

Para aquellos que desean estirar la aplicación para una escala de visualización de más del 100% y aceptar borrosa, la respuesta de Carlos Borau funciona. Aquí hay una forma para .NET 4.6 (C #):

1 Agregue un archivo de manifiesto a su proyecto de aplicación

  1. haga clic derecho en proyecto, Agregar-> Nuevo elemento
  2. Elija General-> Archivo de Manifiesto de Aplicación

2 Descomentar siguiendo y establecer dpiAware = falso:

    false