web-dev-qa-db-fra.com

Désactiver la reconnaissance DPI pour l'application WPF

Bonne journée!

Je travaille sur une application WPF depuis un certain temps maintenant (en tant qu'apprentissage et oh que c'était une expérience d'apprentissage) et elle est enfin prête à être diffusée. Communiquer signifie l’installer sur mon HTPC où il sera utilisé pour parcourir ma collection de films.

Je l'ai conçu sur mon PC fonctionnant en 1920 * 1080, mais avec le réglage DPI normal, alors que HTPC/TV fonctionne à la même résolution mais avec un réglage DPI supérieur pour des raisons évidentes.

Le problème, c’est que mon application se comporte comme un fou sur le HTPC, gâchant à peu près tout ce qui se passe visuellement. Je sais que cela est dû à un mauvais design (mea culpa), mais comme c'est une application qui ne sera utilisée que par moi-même, je cherche une solution rapide, pas une refonte complète. J'ai lu qu'il serait possible d'empêcher l'application d'être consciente de DPI en ajoutant ce qui suit à AssemblyInfo.cs:

[Assembly: System.Windows.Media.DisableDpiAwareness]

Cependant, cela ne semble pas avoir d'effet et le comportement de l'application reste le même.

Quelqu'un pourrait-il me diriger dans la bonne direction?

Merci, Johann

24
Johan Verbelen

DPIAwareness

Juste quelques idées (non essayées):

Êtes-vous en cours d'exécution sur XP? Cette option pourrait ne pas fonctionner sur cette plate-forme.

Ce qui suit ne sont probablement que différentes façons de définir la même option DpiAwareness:

  • examinez les paramètres de "mode de compatibilité" sur votre fichier EXE ... faites un clic droit sur ses propriétés ... et activez "Désactiver la mise à l'échelle de l'affichage"

  • créer un manifeste et dire que vous n'êtes pas au courant

    <Assembly xmlns="urn:schemas-Microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-Microsoft-com:asm.v3" >
     ...
      <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.Microsoft.com/SMI/2005/WindowsSettings">
          <dpiAware>false</dpiAware>
        </asmv3:windowsSettings>
      </asmv3:application>
     ...
    </Assembly>
    
  • call SetProcessDPIAware (pensez que vous devez appeler tôt, c'est-à-dire avant que Window ne soit créé)

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

Vous pouvez appeler IsProcessDPIAware pour vérifier si le paramètre a été appliqué à votre processus d'applications.

Connaître le DPI

Voici comment vous savez quel DPI vous utilisez actuellement:

Accédez à CompositionTarget via la PresentationSource de votre Window pour savoir quelle est la réduction DPI utilisée ..... vous pouvez ensuite utiliser les valeurs pour effectuer certains ajustements de la mise à l'échelle, c.-à-d. Unités indépendantes), de sorte que, lorsqu’elle est mise à l’échelle en raison d’une valeur de DPI supérieure, elle n’explose pas l’utilisation des pixels physiques (... ceci peut être effectué de différentes manières, par exemple ViewBox, ou des calculs de largeur, etc. d'éléments .

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

Faire des ajustements d'échelle

  • utiliser l'astuce ViewBox

    Voir la réponse que j'ai faite précédemment qui permettait à une variable Canvas d'utiliser des positions interprétées comme des pixels "natifs", quelle que soit la mise à l'échelle DPI.

    WPF pour LCD écran Full HD

  • accédez à la matrice de mise à l'échelle TransFormToDevice sur la CompositionTarget et calculez une nouvelle matrice qui annule simplement cette mise à l'échelle et utilisez-la dans LayoutTransform ou RenderTransform.

  • utilisez une méthode (peut-être même dans un convertisseur) qui modifie explicitement une position/longueur DIP (Device Independent Position) .... vous pouvez le faire si vous voulez que votre taille de fenêtre corresponde à une taille de pixel particulière.

32
Colin Smith

Cet article de blog explique comment utiliser une sous-classe Decorator pour faire exactement cela.

En bref, créez une classe comme celle-ci:

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

Ajoutez ensuite quelque chose comme xmlns:custom="clr-namespace:MyNamespace" à votre définition de fenêtre de niveau supérieur, puis entourez votre interface utilisateur indépendante de DPI avec <custom:DpiDecorator>...</custom:DpiDecorator>.

5
ulatekh

Aucune de ces réponses ne désactive vraiment la mise à l’échelle dpi dans WPF.

Voici comment la mise à l’échelle dpi est calculée dans .net 4.6 par WPF: 1) HwndTarget, qui est la CompositionTarget utilisée par tous les visuels. sont stockés.

WPF s'assure que la mise à l'échelle globale est calculée une fois lorsque le premier visuel est préparé. Ceci est contrôlé par un booléen sur HwndTarget {private statique bool _setDpi = true}. Après le calcul du dpi, le champ _setDpi est défini sur false et les méthodes compatibles avec dpi sont des raccourcis utilisant un code comme ceci

Une situation similaire se produit pour chaque UIElement, qui a le même modèle utilisant {private static bool _setDpi = true} pour permettre le calcul pour la première fois.

Le prochain contrôle provient de ProcessDpiAwareness, qui peut être Aucun, Processus ou Moniteur. Afin d'empêcher WPF d'envisager des moniteurs individuels, vous devez définir ProcessDpiAwareness sur Process (int 1).

Enfin, lorsque le dpi est calculé, le résultat est stocké dans 2 listes appelées DpiScaleXValues ​​et DpiScaleYValues ​​sur UIElement. Vous devez donc les initialiser pour corriger les valeurs.

Voici un exemple de code que j'utilise pour moi-même (ne fonctionne que pour .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<double>)typeof(UIElement).GetField("DpiScaleXValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);

        setDpiXValues?.Insert(0, 1);

        var setDpiYValues = (List<double>)typeof(UIElement).GetField("DpiScaleYValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);

        setDpiYValues?.Insert(0, 1);
3
Calin Pirtea

Une solution rapide consisterait simplement à envelopper le contenu de votre fenêtre dans un Viewbox . Si l'enfant immédiat de la Viewbox a une largeur et une hauteur explicites, et si le rapport d'aspect est le même sur les deux ordinateurs, cela devrait vous permettre de démarrer rapidement.

2
Joe White

DpiAwareness n'affecte que la virtualisation DPI. Sous Windows, il apparaît sous la forme "Utiliser la mise à l'échelle de style Windows XP" dans la boîte de résolution personnalisée de Windows 7 & 8, où coché signifie désactivé et non coché signifie activé. (N'oubliez pas de l'appliquer sur l'écran principal de la taille de la police si vous le modifiez! OK, la case n'est pas suffisante.)

Si la virtualisation DPI est désactivée, le fait que votre application indique le système d'exploitation qu'elle prend en charge n'a pas d'importance particulière, il utilisera toujours la mise à l'échelle standard. Lorsqu'il est activé, cela fait la différence entre une mise à l'échelle réelle (consciente) et un simple redimensionnement bilinéaire sur l'ensemble de l'application (non consciente).

Je viens de me rappeler une autre mise en garde: DPI virt ne fonctionnera pas du tout sans Aero Glass.

2
SilverbackNet

Pour ceux qui utilisent VB.NET, le moyen d'accéder à app.manifest est:

 enter image description here

puis décommentez cette partie et réglez-la sur "false"

 enter image description here

0
Carlos Borau

La réponse de Calin Pirtea désactive réellement la mise à l'échelle DPI.

Pour ceux qui veulent étendre l'application à plus de 100% et accepter les flous, la réponse de Carlos Borau fonctionne. Voici un moyen pour .NET 4.6 (C #): 

1 Ajouter un fichier manifeste à votre projet d'application

  1. clic droit sur le projet, Ajouter-> Nouvel élément
  2. Choisissez Général-> Fichier manifeste d'application

2 Décommentez ce qui suit et définissez dpiAware = false:

  <application xmlns="urn:schemas-Microsoft-com:asm.v3">
      <windowsSettings>
          <dpiAware xmlns="http://schemas.Microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
      </windowsSettings>
  </application>
0
Siamca