43 votes

Comment convertir une taille WPF en pixels physiques?

Quel est le meilleur moyen de convertir un WPF (indépendant de la résolution) la largeur et la hauteur de la physique pixels de l'écran?

Je vais les montrer WPF contenu dans un WinForms Formulaire (via ElementHost) et en essayant de travailler avec certains de dimensionnement de la logique. Je l'ai bien fonctionner lorsque le système d'exploitation est en cours d'exécution par défaut de 96 dpi. Mais il ne fonctionne pas lorsque le système d'exploitation est fixée à 120 dpi ou une autre résolution, parce qu'alors un WPF élément qui rend compte de sa Largeur 96 sera effectivement 120 pixels de large autant que WinForms est concerné.

Je ne pouvais pas trouver toutes les "pixels par pouce" paramètres de Système.De Windows.SystemParameters. Je suis sûr que je pourrais utiliser les WinForms équivalent (Système d'.De Windows.Les formulaires.SystemInformation), mais est-il une meilleure façon de le faire (lire: un chemin à l'aide de WPF Api, plutôt que d'utiliser des WinForms Api et manuellement de faire le calcul)? Quel est le "meilleur moyen" pour convertir WPF "pixels" de vrais pixels de l'écran?

EDIT: je suis aussi à la recherche de le faire avant le contrôle WPF est indiqué sur l'écran. Il ressemble Visuelle.PointToScreen pourraient être apportées à me donner la bonne réponse, mais je ne peux pas l'utiliser, car le contrôle n'est pas apparenté encore et je reçois InvalidOperationException "Ce Visuel n'est pas connecté à un PresentationSource".

75voto

Ray Burns Points 38537

La transformation d'un format connu de pixels de l'appareil

Si votre élément visuel est déjà attaché à un PresentationSource (par exemple, c'est une partie d'une fenêtre qui est visible sur l'écran), la transformation est trouvé de cette façon:

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

Si non, utilisez HwndSource pour créer un temporaire hWnd:

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

Notez que cette méthode est moins efficace que d'en construire à l'aide d'un hWnd de IntPtr.Zéro, mais je considère qu'il est plus fiable parce que le hWnd créé par HwndSource sera joint à la même dispositif d'affichage comme une réelle nouvellement créé à la Fenêtre. De cette façon, si différents dispositifs d'affichage ont différentes indicateur de performance, vous êtes sûr d'obtenir la bonne valeur des DPI.

Une fois que vous avez la transformation, vous pouvez convertir n'importe quelle taille de WPF taille à une taille de pixel:

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

La conversion de la taille des pixels pour les entiers

Si vous voulez convertir la taille des pixels pour les entiers, vous pouvez tout simplement faire:

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

mais une solution plus robuste serait celui utilisé par 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));

L'obtention de la taille désirée de UIElement

Pour obtenir la taille souhaitée d'un UIElement vous devez vous assurer qu'elle est mesurée. Dans certaines circonstances, il sera déjà mesurée, soit parce que:

  1. Vous mesuré déjà
  2. Vous mesuré l'un de ses ancêtres, ou
  3. Il fait partie d'un PresentationSource (par exemple, il est dans une Fenêtre visible) et que vous êtes d'exécution ci-dessous DispatcherPriority.Le rendu de sorte que vous savez de mesure a déjà été fait automatiquement.

Si votre élément visuel n'a pas été mesurée encore, vous devriez téléphoner à Mesure sur le contrôle ou l'un de ses ancêtres, comme approprié, en passant dans la taille (ou new Size(double.PositivieInfinity, double.PositiveInfinity) si vous voulez la taille au contenu:

element.Measure(availableSize);

Une fois que la mesure est effectuée, tout ce qui est nécessaire est d'utiliser la matrice de transformer le DesiredSize:

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

Mettre tous ensemble

Voici une méthode simple qui montre comment obtenir la taille en pixels d'un élément:

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

Notez que dans ce code, je l'ai appeler Mesurer uniquement si aucun DesiredSize est présent. Cette fournit une méthode pratique pour tout faire, mais a plusieurs lacunes:

  1. Il se peut que l'élément parent aurait passé un petit availableSize
  2. Il est inefficace si la DesiredSize est zéro (c'est réévalué à plusieurs reprises)
  3. Il peut masquer des bugs dans une manière qui provoque l'échec de l'application en raison de inattendus de synchronisation (par exemple. le code appelé à ou au-dessus de DispatchPriority.Le rendu)

En raison de ces raisons, j'ai tendance à omettre la Mesure appel dans GetElementPixelSize et de laisser le client faire.

12voto

Lu55 Points 2339

Proportion simple entre Screen.WorkingArea et 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 // --
}
 

Cela fonctionne aussi bien avec plusieurs moniteurs.

9voto

Joe White Points 32629

J'ai trouvé un moyen de le faire, mais je ne l'aime pas beaucoup:

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

Je ne l'aime pas parce que (a) elle nécessite une référence à System.Le dessin, plutôt que d'utiliser les Api WPF; et (b) je dois faire le calcul moi-même, ce qui signifie que je suis la duplication de WPF détails de mise en œuvre. Dans .NET 3.5, j'ai tronquer le résultat du calcul pour correspondre à ce ElementHost avec AutoSize=true, mais je ne sais pas si ce sera toujours précis dans les futures versions de .NET.

Cela ne semble pas fonctionner, donc je vais le poster dans le cas où il aide les autres. Mais si quelqu'un a une meilleure réponse, s'il vous plaît, post loin.

1voto

Micael Bergeron Points 423

Juste fait une recherche rapide dans le ObjectBrowser et trouvé quelque chose d'assez intéressant, vous pourriez vouloir vérifier.

Système.De Windows.Forme.AutoScaleMode, il possède une propriété appelée PPP. Voici les docs, il pourrait être ce que vous cherchez :

public const Système.De Windows.Les formulaires.AutoScaleMode Dpi = 2 Membre du Système.De Windows.Les formulaires.AutoScaleMode

Résumé: Contrôles de l'échelle, par rapport à la résolution de l'affichage. Commune les résolutions sont de 96 et 120 DPI.

S'appliquent qu'à votre formulaire, il devrait faire l'affaire.

{profiter}

-1voto

Reed Copsey Points 315315

Regardez FrameworkElement.ActualWidth et ActualHeight . Ceux-ci fournissent la "largeur et hauteur rendues", qui est la taille de pixel que vous recherchez dans ce cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X