visual studio que puede programacion presentacion hacer ejemplos desventajas con animaciones wpf dpi elementhost

studio - ¿Cómo convierto un tamaño de WPF en píxeles físicos?



visual studio wpf (4)

¿Cuál es la mejor manera de convertir un ancho y alto WPF (independiente de la resolución) a píxeles de pantalla físicos?

Estoy mostrando el contenido de WPF en un Formulario de WinForms (a través de ElementHost) y tratando de resolver cierta lógica de tamaño. Lo tengo funcionando bien cuando el sistema operativo se ejecuta con los 96 ppp predeterminados. Pero no funcionará cuando el sistema operativo esté configurado a 120 ppp o a alguna otra resolución, porque entonces un elemento de WPF que informe su ancho como 96 tendrá 120 píxeles de ancho en lo que respecta a WinForms.

No pude encontrar ninguna configuración de "píxeles por pulgada" en System.Windows.SystemParameters. Estoy seguro de que podría usar el equivalente de WinForms (System.Windows.Forms.SystemInformation), pero ¿hay una forma mejor de hacerlo (léase: una forma de utilizar las API de WPF, en lugar de utilizar las API de WinForms y hacer los cálculos manualmente)? ¿Cuál es la "mejor manera" de convertir "píxeles" de WPF en píxeles de pantalla real?

EDITAR: También estoy buscando hacer esto antes de que se muestre el control WPF en la pantalla. Parece que Visual.PointToScreen podría estar hecho para darme la respuesta correcta, pero no puedo usarlo, porque el control aún no está pareado y obtengo InvalidOperationException "Esta visual no está conectada a una PresentationSource".


Acabo de hacer una búsqueda rápida en el ObjectBrowser y encontré algo bastante interesante, es posible que desee comprobarlo.

System.Windows.Form.AutoScaleMode , tiene una propiedad llamada DPI. Aquí están los documentos, podría ser lo que estás buscando:

public const System.Windows.Forms.AutoScaleMode Dpi = 2 Miembro de System.Windows.Forms.AutoScaleMode

Resumen: Controla la escala relativa a la resolución de la pantalla. Las resoluciones comunes son 96 y 120 DPI.

Aplica eso a tu forma, debería hacer el truco.

{disfrutar}


Encontré una manera de hacerlo, pero no me gusta mucho:

using (var graphics = Graphics.FromHwnd(IntPtr.Zero)) { var pixelWidth = (int) (element.DesiredSize.Width * graphics.DpiX / 96.0); var pixelHeight = (int) (element.DesiredSize.Height * graphics.DpiY / 96.0); // ... }

No me gusta porque (a) requiere una referencia a System.Drawing, en lugar de utilizar las API de WPF; y (b) Tengo que hacer los cálculos yo mismo, lo que significa que estoy duplicando los detalles de implementación de WPF. En .NET 3.5, tengo que truncar el resultado del cálculo para que coincida con lo que hace ElementHost con AutoSize = verdadero, pero no sé si esto todavía será preciso en las versiones futuras de .NET.

Esto parece funcionar, así que lo publico en caso de que ayude a otros. Pero si alguien tiene una mejor respuesta, por favor, envíela.


Proporción simple entre Screen.WorkingArea y SystemParameters.WorkArea :

private double PointsToPixels (double wpfPoints, LengthDirection direction) { if (direction == LengthDirection.Horizontal) { return wpfPoints * Screen.PrimaryScreen.WorkingArea.Width / SystemParameters.WorkArea.Width; } else { return wpfPoints * Screen.PrimaryScreen.WorkingArea.Height / SystemParameters.WorkArea.Height; } } private double PixelsToPoints(int pixels, LengthDirection direction) { if (direction == LengthDirection.Horizontal) { return pixels * SystemParameters.WorkArea.Width / Screen.PrimaryScreen.WorkingArea.Width; } else { return pixels * SystemParameters.WorkArea.Height / Screen.PrimaryScreen.WorkingArea.Height; } } public enum LengthDirection { Vertical, // | Horizontal // —— }

Esto funciona bien con monitores múltiples también.


Transformando un tamaño conocido a los píxeles del dispositivo

Si su elemento visual ya está adjunto a PresentationSource (por ejemplo, es parte de una ventana visible en la pantalla), la transformación se encuentra de esta manera:

var source = PresentationSource.FromVisual(element); Matrix transformToDevice = source.CompositionTarget.TransformToDevice;

Si no, use HwndSource para crear un hWnd temporal:

Matrix transformToDevice; using(var source = new HwndSource(new HwndSourceParameters())) transformToDevice = source.TransformToDevice;

Tenga en cuenta que esto es menos eficiente que construir usando un hWnd de IntPtr.Zero, pero lo considero más confiable porque el hWnd creado por HwndSource se conectará al mismo dispositivo de visualización como lo haría una ventana recién creada. De esta forma, si los diferentes dispositivos de visualización tienen diferentes DPI, está seguro de obtener el valor correcto de DPI.

Una vez que tenga la transformación, puede convertir cualquier tamaño desde un tamaño WPF a un tamaño de píxel:

var pixelSize = (Size)transformToDevice.Transform((Vector)wpfSize);

Convertir el tamaño de píxel a enteros

Si desea convertir el tamaño de píxel en enteros, simplemente puede hacer:

int pixelWidth = (int)pixelSize.Width; int pixelHeight = (int)pixelSize.Height;

pero una solución más robusta sería la utilizada por ElementHost:

int pixelWidth = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Width)); int pixelHeight = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Height));

Obtener el tamaño deseado de un UIElement

Para obtener el tamaño deseado de un UIElement, debes asegurarte de que se mide. En algunas circunstancias, ya se medirá, ya sea porque:

  1. Ya lo has medido
  2. Mediste a uno de sus antepasados, o
  3. Es parte de una PresentationSource (por ejemplo, está en una ventana visible) y se está ejecutando debajo de DispatcherPriority.Render para que sepa que la medición ya ha ocurrido automáticamente.

Si su elemento visual no se ha medido todavía, debe llamar a Measure en el control o uno de sus antepasados ​​según corresponda, pasando el tamaño disponible (o el new Size(double.PositivieInfinity, double.PositiveInfinity) si desea new Size(double.PositivieInfinity, double.PositiveInfinity) tamaño al contenido :

element.Measure(availableSize);

Una vez que se realiza la medición, todo lo que se necesita es usar la matriz para transformar el Tamaño deseado:

var pixelSize = (Size)transformToDevice.Transform((Vector)element.DesiredSize);

Poniendolo todo junto

Aquí hay un método simple que muestra cómo obtener el tamaño de píxel de un elemento:

public Size GetElementPixelSize(UIElement element) { Matrix transformToDevice; var source = PresentationSource.FromVisual(element); if(source!=null) transformToDevice = source.CompositionTarget.TransformToDevice; else using(var source = new HwndSource(new HwndSourceParameters())) transformToDevice = source.CompositionTarget.TransformToDevice; if(element.DesiredSize == new Size()) element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); return (Size)transformToDevice.Transform((Vector)element.DesiredSize); }

Tenga en cuenta que en este código llamo a Medida solo si no está presente DesiredSize. Esto proporciona un método conveniente para hacer todo, pero tiene varias deficiencias:

  1. Es posible que el elemento padre haya pasado en un tamaño de disponible más pequeño
  2. Es ineficaz si el DesiredSize real es cero (se vuelve a medir repetidamente)
  3. Puede enmascarar errores de una manera que hace que la aplicación falle debido a un tiempo inesperado (p. Ej., El código se llama a DispatchPriority.Render o superior)

Debido a estas razones, me inclinaría a omitir la llamada a Measure en GetElementPixelSize y simplemente dejar que el cliente lo haga.