35 votos

¿Cómo convertir un tamaño WPF píxeles físicos?

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

Yo estoy mostrando contenido de WPF en un WinForms Forma (a través de ElementHost) y tratando de averiguar algunos de tamaño de la lógica. Ya lo tengo funcionando bien cuando el sistema operativo se está ejecutando en el valor predeterminado de 96 dpi. Pero no funciona cuando el sistema operativo se establece a 120 dpi o alguna otra resolución, porque entonces WPF elemento que informa de su Anchura de 96 en realidad serán 120 píxeles de ancho como mucho como WinForms se refiere.

No podía encontrar ningún "píxeles por pulgada" configuración del Sistema.Windows.SystemParameters. Estoy seguro de que podría utilizar el WinForms (equivalente del Sistema.Windows.Los formularios.SystemInformation), pero hay una manera mejor de hacer esto (leer: un camino en el uso de WPF Api, en lugar de utilizar WinForms Api y manualmente haciendo las matemáticas)? ¿Cuál es la "mejor manera" para convertir WPF "píxeles" real de píxeles de la pantalla?

EDIT: yo también estoy mirando para hacer esto antes de que el control WPF se muestra en la pantalla. Parece Visual.PointToScreen podría ser hecho para darme la respuesta correcta, pero no los puedo usar, porque el control no está emparentado con todo y que yo obtener InvalidOperationException "Este Visual no está conectado a un PresentationSource".

58voto

Ray Burns Puntos 38537

La transformación de un tamaño conocido a píxeles del dispositivo

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

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

Si no, utilice HwndSource para crear un temporal de hWnd:

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

Tenga en cuenta que este es menos eficiente que el de construir, el uso de un hWnd de IntPtr.Cero, pero considero que es más confiable porque el hWnd creado por HwndSource se adjunta a la misma pantalla del dispositivo como recién creado Ventana. De esa manera, si los diferentes dispositivos de visualización tienen diferentes inhaladores de polvo seco que está seguro de obtener el valor de DPI.

Una vez que tenga la transformación, puede convertir cualquier tamaño, desde una WPF tamaño 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 números enteros, usted puede simplemente 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 conseguir el tamaño deseado de un UIElement usted necesita para asegurarse de que se mide. En algunas circunstancias será medido, ya sea porque:

  1. En que se mide ya
  2. Se mide a uno de sus antepasados, o
  3. Es parte de un PresentationSource (por ejemplo, es visible en la Ventana) y se va a ejecutar a continuación DispatcherPriority.Render para que usted sepa de medición ya ha sucedido de forma automática.

Si el elemento visual no ha sido medido, sin embargo, usted debe llamar Medida, de control o de uno de sus antepasados, según corresponda, pasando en el tamaño disponible (o new Size(double.PositivieInfinity, double.PositiveInfinity) si desea tamaño contenido:

element.Measure(availableSize);

Una vez que la medición se hace, todo lo que es necesario es el uso de la matriz para transformar la DesiredSize:

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

Poniendo todo junto

Aquí es 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 que llame a Medir sólo en caso de no DesiredSize está presente. Esto proporciona un método conveniente para hacer de todo, pero tiene varias deficiencias:

  1. Puede ser que el elemento padre habría pasado en un pequeño availableSize
  2. Es ineficiente si el real DesiredSize es cero (se volvieron a medir en varias ocasiones)
  3. Es posible que la máscara de errores de una manera que hace que la aplicación falle debido a un tiempo (por ejemplo. el código que se llama en o por encima de DispatchPriority.Render)

Debido a estas razones, yo estaría inclinado a omitir la Medida de llamada en GetElementPixelSize y dejar que el cliente de hacerlo.

8voto

Joe White Puntos 32629

He encontrado 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) se requiere una referencia para el Sistema.El dibujo, en lugar de utilizar WPF Api; y (b) tengo que hacer la matemática a mí mismo, lo que significa que yo soy la duplicación de WPF los detalles de implementación. En .NET 3.5, tengo para truncar el resultado del cálculo para que coincida con lo que ElementHost hace con AutoSize=true, pero no sé si esto seguirá siendo precisa en futuras versiones .NET.

Esto no parece funcionar, así que estoy publicando en caso de que ayuda a los demás. Pero si alguien tiene una mejor respuesta, por favor, el post de distancia.

7voto

Lu55 Puntos 2339

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 varios monitores, así.

1voto

Micael Bergeron Puntos 423

Acabo de hacer una búsqueda rápida en la ObjectBrowser y encontré algo muy interesante, usted puede ser que desee comprobar hacia fuera.

Sistema.Windows.El formulario.AutoScaleMode, tiene una propiedad llamada PPP. Aquí está la documentación, podría ser lo que usted está buscando :

public const Sistema.Windows.Los formularios.AutoScaleMode Dpi = 2 Miembro del Sistema.Windows.Los formularios.AutoScaleMode

Resumen: los Controles de la escala en relación a la resolución de la pantalla. Común las resoluciones son las 96 y 120 DPI.

Aplicar eso a su forma, se debe hacer el truco.

{disfrutar}

-1voto

Reed Copsey Puntos 315315

Mirar FrameworkElement.ActualWidth y ActualHeight. Estos proporcionan "prestados de la anchura y altura", que es el tamaño de pixel que buscas en este caso.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X