web-dev-qa-db-fra.com

Création d'une application compatible DPI

J'ai une demande de formulaire en C #. Lorsque je modifie le DPI du moniteur, tous les contrôles se déplacent. J'ai utilisé le code this.AutoScaleMode = AutoScaleMode.Dpi, mais cela n'a pas évité le problème.

Est-ce que quelqu'un a une idée?

63
RRR

EDIT: Depuis .NET 4.7, Windows Forms a amélioré la prise en charge de High DPI. En savoir plus à ce sujet sur docs.Microsoft.com Cela ne fonctionne que pour Win 10 Creators Update et supérieur, il n'est donc pas encore possible de l'utiliser en fonction de votre base d'utilisateurs.


Difficile, mais pas impossible. Votre meilleure option est de passer à WPF bien sûr, mais cela pourrait ne pas être faisable.

J'ai passé BEAUCOUP de temps avec ce problème. Voici quelques règles/directives pour le faire fonctionner correctement sans FlowLayoutPanel ou TableLayoutPanel:

  • Modifiez/concevez toujours vos applications en 96 DPI par défaut (100%). Si vous concevez en 120DPI (125% f.ex), cela deviendra vraiment mauvais lorsque vous reviendrez à 96 DPI pour travailler avec plus tard.
  • J'ai utilisé AutoScaleMode.Font avec succès, je n'ai pas beaucoup essayé AutoScaleMode.DPI.
  • Assurez-vous d'utiliser la taille de police par défaut sur tous vos conteneurs (formulaires, panneaux, page à onglet, contrôles utilisateur, etc.). 8,25 px. De préférence, il ne doit pas du tout être défini dans le fichier .Designer.cs pour tous les conteneurs afin qu'il utilise la police par défaut de la classe de conteneur.
  • Tous les conteneurs doivent utiliser le même AutoScaleMode
  • Assurez-vous que tous les conteneurs ont la ligne ci-dessous définie dans le fichier Designer.cs:

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); // for design in 96 DPI

  • Si vous devez définir différentes tailles de police sur les étiquettes/zones de texte, etc., définissez-les par contrôle au lieu de définir la police sur la classe de conteneur, car winforms utilise le paramètre de police des conteneurs pour mettre à l'échelle son contenu et avoir f.ex un panneau avec une taille de police différente que ce qu'il contient est garanti de poser des problèmes. Cela pourrait fonctionner si le formulaire et tous les conteneurs du formulaire utilisent la même taille de police, mais je ne l'ai pas essayé.
  • Utilisez une autre machine ou une installation de fenêtres virtuelles (VMware, Virtual PC, VirtualBox) avec un paramètre DPI plus élevé pour tester votre conception immédiatement. Exécutez simplement le fichier .exe compilé à partir du dossier/bin/Debug sur la machine DEV.

Je vous garantis que si vous suivez ces directives, vous serez d'accord, même si vous avez placé des contrôles avec des ancres spécifiques et n'utilisez pas de panneau de flux. Nous avons une application construite de cette façon déployée sur des centaines de machines avec différentes configurations DPI et nous n'avons plus de plaintes. Tous les formulaires/conteneurs/grilles/boutons/champs de texte, etc. sont mis à l'échelle correctement, tout comme la police. Les images fonctionnent également, mais elles ont tendance à être un peu pixellisées à haute résolution.

EDIT: Ce lien a beaucoup de bonnes informations, surtout si vous choisissez d'utiliser AutoScaleMode.DPI: lien vers la question de stackoverflow associée

102
Trygve

J'ai finalement trouvé une solution au problème de l'orientation de l'écran et de la gestion DPI.
Microsoft a déjà fourni un document l'expliquant mais avec une petite faille qui tuera complètement la gestion DPI. Suivez simplement la solution fournie dans le document ci-dessous sous "Création d'un code de mise en page distinct pour chaque orientation" http://msdn.Microsoft.com/en-us/library/ms838174.aspx

Alors partie IMPORTANTE! À l'intérieur du code pour les méthodes Paysage () et Portrait () à la fin de chacune, ajoutez ces lignes:

this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;

Ainsi, le code de ces 2 méthodes serait comme:

protected void Portrait()
{
   this.SuspendLayout();
   this.crawlTime.Location = new System.Drawing.Point(88, 216);
   this.crawlTime.Size = new System.Drawing.Size(136, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(10, 216);
   this.crawlTimeLabel.Size = new System.Drawing.Size(64, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(88, 200);
   this.crawlStartTime.Size = new System.Drawing.Size(136, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(10, 200);
   this.crawlStartedLabel.Size = new System.Drawing.Size(64, 16);
   this.light1.Location = new System.Drawing.Point(208, 66);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(192, 66);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(88, 182);
   this.linkCount.Size = new System.Drawing.Size(136, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(10, 182);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 84);
   this.currentPageBox.Size = new System.Drawing.Size(214, 90);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 68);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(214, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(10, 48);
   this.noProxyCheck.Size = new System.Drawing.Size(214, 20);
   this.startButton.Location = new System.Drawing.Point(8, 240);
   this.startButton.Size = new System.Drawing.Size(216, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 24);
   this.addressBox.Size = new System.Drawing.Size(214, 22);

   //note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
   this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;   //IMPORTANT
   this.ResumeLayout(false);
}

protected void Landscape()
{
   this.SuspendLayout();
   this.crawlTime.Location = new System.Drawing.Point(216, 136);
   this.crawlTime.Size = new System.Drawing.Size(96, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(160, 136);
   this.crawlTimeLabel.Size = new System.Drawing.Size(48, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(64, 120);
   this.crawlStartTime.Size = new System.Drawing.Size(248, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(8, 120);
   this.crawlStartedLabel.Size = new System.Drawing.Size(48, 16);
   this.light1.Location = new System.Drawing.Point(296, 48);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(280, 48);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(80, 136);
   this.linkCount.Size = new System.Drawing.Size(72, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(8, 136);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 64);
   this.currentPageBox.Size = new System.Drawing.Size(302, 48);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 48);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(50, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(168, 16);
   this.noProxyCheck.Size = new System.Drawing.Size(152, 24);
   this.startButton.Location = new System.Drawing.Point(8, 160);
   this.startButton.Size = new System.Drawing.Size(304, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 20);
   this.addressBox.Size = new System.Drawing.Size(150, 22);

   //note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
   this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;   //IMPORTANT
   this.ResumeLayout(false);
}

Fonctionne comme un charme pour moi.

14
user1251307

note: cela ne résoudra pas le déplacement des commandes, lorsque le dpi change. cela ne corrigera que le texte flou !!.


Comment corriger les formulaires Windows flous dans les paramètres haute résolution:

  1. Allez dans le concepteur de formulaires, puis sélectionnez votre formulaire (en cliquant sur sa barre de titre)
  2. Appuyez sur F4 pour ouvrir la fenêtre Propriétés,
  3. puis recherchez la propriété AutoScaleMode
  4. Changez-la de Police (par défaut) à Dpi .

Maintenant, allez dans Program.cs (ou le fichier où se trouve votre méthode Main) et changez-le pour ressembler à:

namespace myApplication
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            // ***this line is added***
            if (Environment.OSVersion.Version.Major >= 6)
                SetProcessDPIAware();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }

        // ***also dllimport of that function***
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool SetProcessDPIAware();
    }
}

Enregistrez et compilez. Maintenant, votre formulaire devrait à nouveau être croustillant.


source: http://crsouza.com/2015/04/13/how-to-fix-blurry-windows-forms-windows-in-high-dpi-settings/

9
bh_earth0

Il semble que ce soit un problème avec Windows. La suppression de ces deux lignes a tout réparé.

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

C'est là que j'ai obtenu la solution:

4
Jakobitz

Il est vraiment difficile de concevoir des applications prenant en charge DPI dans Windows Forms. Vous devez utiliser des conteneurs de disposition qui redimensionnent correctement lorsque le DPI est modifié (tel que TableLayoutPanel ou FlowLayoutPanel). Tous les contrôles doivent également être redimensionnés. La configuration de ces conteneurs peut être un défi.

Pour les applications simples, cela peut être fait dans un délai raisonnable, mais pour les grandes applications, c'est vraiment beaucoup de travail.

3
testalino

De l'expérience:

  • n'utilisez pas la reconnaissance DPI avec les formulaires Windows à moins que ce ne soit critique
  • à cette fin, définissez toujours la propriété AutoScaleMode sur None sur tous les formulaires et contrôles utilisateur de votre application.
  • Résultat: type d'interface WYSIWYG lorsque les paramètres DPI changent
2
Liviu M.
  1. Si vous voulez que votre application WinForms soit une application DPI-Aware, En plus de la bonne réponse de Trygve, Si vous avez un gros projet, vous voudrez peut-être mettre à l'échelle vos formulaires et leur contenu automatiquement, Vous pouvez le faire en créant la fonction ScaleByDPI:

La fonction ScaleByDPI recevra un paramètre de contrôle qui est généralement un formulaire, puis itérera récursivement à travers tous les sous-contrôles (si (control.HasChildren == true)), et mettra à l'échelle l'emplacement et les tailles de vos contrôles d'application et les tailles et tailles de polices vers le DPI configuré par le système d'exploitation. Vous pouvez essayer de l'implémenter également pour les images, les icônes et les graphiques.

Notes spéciales pour la fonction ScaleByDPI:

une. Pour tous les contrôles avec des tailles de police par défaut, vous devrez définir leur Font.Size sur 8.25.

b. Vous pouvez obtenir les valeurs devicePixelRatioX et devicePixelRatioY par (control.CreateGraphics (). DpiX/96) et (control.CreateGraphics (). DpiY/96).

c. Vous aurez besoin de l'échelle Control.Size & Control.Location par algorithme basé sur les valeurs control.Dock & control.Anchor. Notez que control.Dock peut avoir 1 sur 6 valeurs possibles et que control.Anchor peut avoir 1 sur 16 valeurs possibles.

ré. cet algorithme devra définir des valeurs pour les variables booléennes suivantes: isDoSizeWidth, isDoSizeHeight, isDoLocationX, isDoLocationY, isDoRefactorSizeWidth, isDoRefactorSizeHeight, isDoRefactorLocationX, isDoRefactorLocationY, isDoClacLocationXBasedOnACOnOnOB.

e. Si votre projet utilise une bibliothèque de contrôles autre que les contrôles Microsoft, ces contrôles peuvent nécessiter un traitement spécial.

Plus d'informations sur les variables booléennes ci-dessus (d.):

* Parfois, un groupe de contrôles (peut-être des boutons) doivent être placés l'un après l'autre sur la même ligne verticale, et leur valeur d'ancrage inclut Droite mais pas Gauche, ou ils doivent être placés l'un après l'autre sur la même ligne horizontale, et leur La valeur d'ancrage inclut Bas mais pas Haut, dans ce cas, vous devez recalculer les valeurs d'emplacement des contrôles.

* Dans le cas des contrôles dont Anchor contient Top & Bottom et\ou Left & Right, vous aurez besoin de re-factoriser les contrôles Taille et emplacement.

Utilisations de la fonction ScaleByDPI:

une. Ajoutez la commande suivante à la fin de tout constructeur de formulaire: ScaleByDPI (this);

b. Également lors de l'ajout dynamique d'un contrôle à un appel de formulaire à ScaleByDPI ([ControlName]).

  1. Lorsque vous définissez dynamiquement la taille ou l'emplacement d'un contrôle après la fin du constructeur, créez et utilisez l'une des fonctions suivantes afin d'obtenir les valeurs mises à l'échelle de la taille ou de l'emplacement: ScaleByDPI_X\ScaleByDPI_Y\ScaleByDPI_Size\ScaleByDPI_Point

  2. Afin de marquer votre application comme étant compatible DPI, ajoutez l'élément dpiAware au manifeste d'assembly de votre application.

  3. Définissez GraphicsUnit de tous Control.Font sur System.Drawing.GraphicsUnit.Point

  4. Dans les fichiers * .Designer.cs de tous les conteneurs, définissez la valeur AutoScaleMode sur System.Windows.Forms.AutoScaleMode.None

  5. dans les contrôles comme ComboBox et TextBox, la modification de Control.Size.Hieght n'a aucun effet. Dans ce cas, changer Control.Font.Size fixera la hauteur du contrôle.

  6. Si la valeur du formulaire StartPosition est FormStartPosition.CenterScreen, vous devrez recalculer l'emplacement de la fenêtre.

1
Yossi B