La prise en charge du haut DPI dans WinForms — pourquoi l'interface devient floue ou se casse sur les moniteurs 4K, et solutions concrètes

· Mis à jour le: · · WinForms, Haut DPI, Windows, .NET, C#, .NET Framework, Maintenance legacy, UI, Conseil technique

« Depuis qu’on est passés à un nouvel ordinateur portable, le texte de notre application métier est flou et difficile à lire. » « Une fois branché sur un moniteur 4K, les boutons et les libellés se chevauchent. » « L’aperçu du bordereau ne se casse que chez ce client, en environnement 125 %. » Ces dernières années, ce type de demande explose au moment du renouvellement des PC. Les écrans haute résolution dépassant le Full HD et une mise à l’échelle d’affichage de 125 à 200 % sont devenus la norme, même sur les PC professionnels, si bien que les applications WinForms conçues à l’époque du 96 DPI / 100 % ne peuvent plus s’afficher confortablement telles quelles. C’est probablement, aujourd’hui, le sujet le plus fréquent parmi les demandes de maintenance portant sur d’anciennes applications WinForms.

Ce qui complique les choses, c’est que les symptômes semblent disparates : « flou », « cassé », « seulement une partie trop petite ». En réalité, les causes se ramènent à quelques catégories bien identifiées, et une fois qu’on sait quel symptôme correspond à quelle cause, on peut aussi bien déterminer comment corriger que jusqu’où aller. Cet article organise, à partir du fonctionnement de la mise à l’échelle DPI de Windows et des modes de sensibilisation DPI, la prise en charge du haut DPI dans les applications WinForms (aussi bien .NET que .NET Framework) — méthodes de configuration, pièges classiques et stratégie de réponse par étapes — le tout dans une perspective pratique et concrète.

1. L’essentiel d’abord

  • Dans un environnement haut DPI, le flou d’une ancienne application n’est pas un bug : c’est une mesure de secours du système d’exploitation (la virtualisation DPI). Windows fait dessiner l’application non sensible au DPI à 96 DPI, puis agrandit le résultat sous forme de bitmap ; la mise en page ne se casse donc pas, mais l’image devient floue.1
  • À l’inverse, une interface « nette mais cassée » signifie que l’application a déclaré sa sensibilisation DPI, mais que la mise en page n’a pas suivi. Le flou et la casse ayant des causes exactement opposées, identifiez d’abord lequel des deux cas vous concerne avant d’intervenir.
  • La première étape consiste à déterminer dans quel mode de sensibilisation DPI (Unaware / System Aware / Per-Monitor V2) votre application s’exécute actuellement. Vérifiez si la déclaration se fait via le manifeste, app.config, un appel d’API — ou nulle part du tout.2
  • La méthode de configuration varie selon la génération. Pour .NET (Core 3.1 à .NET 8), utilisez le fichier projet ou Application.SetHighDpiMode ; pour .NET Framework 4.7 et ultérieur, app.config ; pour 4.6 et en dessous, déclarer System Aware via le manifeste reste la limite réaliste.34
  • Ouvrez toujours le concepteur sur un moniteur à 100 % (96 DPI). Ouvrir puis enregistrer un formulaire en environnement 150 % réécrit AutoScaleDimensions, un incident classique qui casse la mise en page pour toute l’équipe.5
  • Une prise en charge complète (Per-Monitor V2) demande un effort important. Une approche en deux étapes — « System Aware pour un affichage net sur le seul moniteur principal » comme première étape, puis une prise en charge complète de PMv2 comme seconde étape — permet d’ajuster l’investissement à la durée de vie de l’application et à son environnement d’utilisation, ce qui est plus réaliste.
  • Comme mesure provisoire lorsque la correction en interne n’est pas possible ou pas réalisable à temps, il est utile de connaître aussi les « paramètres de DPI élevé » que l’utilisateur peut lui-même modifier depuis Propriétés → Compatibilité de l’exe : cela facilite nettement la gestion des demandes d’assistance.6

2. Pourquoi le flou apparaît — le fonctionnement de la virtualisation DPI

La mise à l’échelle d’affichage de Windows s’exprime sous forme d’un multiplicateur où 96 DPI correspond à 100 %. 125 % = 120 DPI, 150 % = 144 DPI, 200 % = 192 DPI. Sur un moniteur 4K (3840 × 2160) de 27 pouces, utiliser 100 % rend le texte trop petit ; Windows recommande donc par défaut environ 150 %. Autrement dit, la diffusion des écrans haute résolution signifie directement la diffusion d’environnements fonctionnant à un DPI autre que 96.

Le problème, c’est que la plupart des anciennes applications de bureau ont été écrites en partant du principe que « l’écran est toujours en 96 DPI ». Les coordonnées, les polices et les icônes sont codées directement en pixels, et si on les dessine telles quelles en environnement 150 %, tout apparaît physiquement aux deux tiers de sa taille prévue. Windows ment donc aux applications qui n’ont pas déclaré leur sensibilisation DPI, en leur faisant croire que « l’écran est en 96 DPI », puis affiche, agrandi sous forme de bitmap, le résultat que l’application a dessiné en pensant être à 96 DPI. C’est la virtualisation DPI (l’étirement de bitmap).1

En gardant ce mécanisme à l’esprit, les symptômes et les causes se correspondent de façon nette. Utilisez le tableau suivant pour un premier diagnostic.

Symptôme Cause État
L’ensemble est uniformément flou/trouble ; la mise en page ne se casse pas Virtualisation DPI (l’OS agrandit un bitmap) Non sensible au DPI (Unaware). Sûr, mais peu soigné
Le texte est net, mais les contrôles se chevauchent ou sont coupés Sensibilisation DPI déclarée, mais la mise en page n’a pas suivi Prise en charge DPI à moitié faite. C’est ici que commence le travail de correction
Net sur le moniteur principal, flou en le déplaçant vers un moniteur secondaire System Aware (DPI figé sur celui du moniteur principal au démarrage) Conforme aux spécifications. À évaluer pour un passage à PMv2
La taille du texte est correcte, mais seules les icônes/images sont petites ou grossières Les ressources bitmap restent prévues pour 96 DPI Ressources image non adaptées (chapitre 6)
Seuls un écran ou un contrôle particulier sont cassés Mise en page à coordonnées fixes, dessin personnalisé, contrôles tiers À corriger au cas par cas (chapitre 6)

Le point important est qu’une application « floue » n’est justement pas cassée : la mesure de secours du système d’exploitation fonctionne correctement. Corriger cela revient à refuser cette mesure de secours et à déclarer « je gère moi-même la mise à l’échelle » (c’est-à-dire à relever le mode de sensibilisation DPI) — dès l’instant de cette déclaration, tous les problèmes de mise en page deviennent votre propre responsabilité. Les demandes du type « on est passés à PMv2 en dépannage et c’est devenu encore pire » naissent exactement de ce mécanisme.

3. Panorama des modes de sensibilisation DPI

Une application déclare au système, au niveau du processus (plus précisément, à partir de Windows 10, au niveau de la fenêtre de premier niveau), jusqu’où elle sait gérer le DPI elle-même. Il existe en pratique quatre modes.16

Mode OS d’introduction DPI vu par l’application Lors d’un déplacement entre moniteurs ou d’un changement de DPI Position pratique en WinForms
Unaware Toujours 96 L’OS agrandit un bitmap (flou) Valeur par défaut si rien n’est déclaré. Flou, mais ne se casse pas
System Aware Vista Figé sur le DPI du moniteur principal au moment de la connexion Sur tout moniteur autre que le principal, ou après un changement, l’OS agrandit (flou) Utilisable sur toutes les générations. Le choix privilégié pour une solution pragmatique
Per-Monitor (V1) 8.1 Le DPI du moniteur où se trouve la fenêtre Simple notification à la fenêtre de premier niveau ; la mise à l’échelle est entièrement à la charge de l’application Aucun support du framework, peu d’intérêt pratique. À ne pas choisir
Per-Monitor V2 10 (1703) Le DPI du moniteur où se trouve la fenêtre Notification jusqu’aux fenêtres enfants ; l’OS s’occupe de la zone non cliente Le choix privilégié pour une prise en charge complète. Disponible en .NET Framework 4.7+ / .NET

En pratique, la différence entre Per-Monitor V1 et V2 est décisive. V1 est un mécanisme brut, pensé pour du Win32 pur, où « la notification arrive, mais tout le reste est à faire soi-même » — il n’y a aucune raison de l’utiliser depuis WinForms. V2, lui, fait mettre à l’échelle automatiquement par l’OS la zone non cliente (barre de titre, barres de défilement, menus, etc.), et le support apporté par le framework WinForms (voir plus loin) est lui aussi conçu en partant du principe de V2. Si vous optez pour Per-Monitor, V2 est le seul choix sensé.6

Il existe aussi un autre mode, la mise à l’échelle GDI (DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED, à partir de Windows 10 1809). L’application reste Unaware, mais seuls le texte et les formes dessinés via GDI sont agrandis par l’OS au niveau vectoriel — une version améliorée de l’étirement de bitmap.6 Comme cela peut réduire le flou du texte sans modifier une seule ligne de code, cela vaut la peine d’être gardé en tête comme mesure provisoire pour les applications qu’on ne peut pas corriger (section 4.3).

Notez également qu’à partir de Windows 10 1607, il existe un mode mixte (Mixed-Mode) permettant de faire coexister des modes différents par fenêtre de premier niveau : une migration par étapes — « seul l’écran principal passe en PMv2, tandis qu’une boîte de dialogue qu’on n’a pas fini de corriger reste en Unaware et continue d’être agrandie par l’OS » — est prise en charge au niveau Win32.7 Ce n’est pas quelque chose qu’on utilise à la légère depuis WinForms, mais la philosophie de conception sous-jacente — « il n’est pas nécessaire de corriger tous les écrans d’un seul coup » — rejoint directement la stratégie par étapes du chapitre 7.

4. Configuration — la bonne méthode selon la génération

La méthode de déclaration du mode de sensibilisation DPI dépend de la génération de votre application. Se tromper à ce niveau fait perdre du temps sur un « je l’ai configuré, mais ça ne fonctionne pas », c’est pourquoi je détaille ci-dessous génération par génération.

4.1 WinForms sous .NET (Core 3.1 à .NET 8)

.NET fournit Application.SetHighDpiMode, que ApplicationConfiguration.Initialize() — générée par le modèle de projet (.NET 6 et ultérieur) — appelle automatiquement. La valeur par défaut est SystemAware.3 Il est recommandé d’écrire ce réglage dans le fichier projet, que le concepteur Visual Studio consulte également.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
    <!-- SystemAware (par défaut) / PerMonitorV2 / DpiUnaware / DpiUnawareGdiScaled -->
    <ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
  </PropertyGroup>
</Project>

Attention : ApplicationHighDpiMode dans le fichier projet et ApplicationConfiguration.Initialize() sont des mécanismes introduits avec .NET 6.3 Dans un projet .NET Core 3.1 / .NET 5, écrire cette propriété n’a aucun effet ; il faut alors appeler directement Application.SetHighDpiMode dans le code, comme ci-dessous (c’est également le cas avec un Main de style ancien qui n’utilise pas ApplicationConfiguration.Initialize()). Dans les deux cas, l’appel doit se faire avant de créer la moindre fenêtre.

[STAThread]
static void Main()
{
    Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
}

À partir de .NET 6, la mise à l’échelle des contrôles conteneurs et des fenêtres enfants MDI sous PMv2 a été améliorée, ce qui résout une bonne partie des problèmes qui subsistaient jusqu’à .NET 5, comme des contrôles qui se décalent en déplaçant la fenêtre d’un moniteur à 200 % vers un moniteur à 100 %.3 Si vous voulez vous attaquer sérieusement à PMv2, .NET est clairement plus facile à affronter que .NET Framework, d’après notre expérience. Pour les éléments à prendre en compte avant une migration depuis .NET Framework, voir « Liste de vérification avant la migration de .NET Framework vers .NET ».

4.2 .NET Framework 4.7 et ultérieur

.NET Framework a considérablement renforcé la prise en charge du haut DPI avec la version 4.7 : amélioration de la mise à l’échelle des contrôles, événements de changement de DPI (la famille DpiChanged), propriété DeviceDpi, entre autres. C’est toutefois une fonctionnalité à activation explicite (opt-in), qui ne devient effective que si les deux points suivants sont configurés ensemble.4

Tout d’abord, déclarez la compatibilité Windows 10 dans le manifeste (sans cela, les fonctionnalités haut DPI de la version 4.7 elles-mêmes ne s’activent pas).

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  <application>
    <!-- Déclaration de compatibilité Windows 10 -->
    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
  </application>
</compatibility>

Ensuite, déclarez le mode de sensibilisation DPI dans System.Windows.Forms.ApplicationConfigurationSection, au sein d’app.config.

<configuration>
  <System.Windows.Forms.ApplicationConfigurationSection>
    <add key="DpiAwareness" value="PerMonitorV2" />
    <!-- Si certains écrans gèrent déjà leur propre mise à l'échelle, vous pouvez désactiver des fonctionnalités individuelles -->
    <!-- <add key="EnableWindowsFormsHighDpiAutoResizing" value="false" /> -->
  </System.Windows.Forms.ApplicationConfigurationSection>
</configuration>

Vérifiez en parallèle que Application.EnableVisualStyles() est bien appelé au début de Main. Une précaution s’impose ici : l’ancienne méthode consistant à écrire <dpiAware> / <dpiAwareness> dans le manifeste écrase le réglage d’app.config, si bien que la documentation officielle déconseille de combiner les deux dans WinForms sous 4.7.4 Lors de la montée vers 4.7+PMv2 d’une application qui avait par le passé inscrit <dpiAware>true</dpiAware> dans le manifeste pour passer en System Aware, commencez par nettoyer cette déclaration côté manifeste. « J’ai configuré app.config, mais ça ne prend pas effet » vient presque toujours de là.

4.3 .NET Framework 4.6 et antérieur, et bases de code mixtes VB6/MFC

WinForms en version 4.6 et antérieure ne dispose d’aucun code de framework pour prendre en charge PMv2 ; forcer la déclaration revient à devoir traiter soi-même, entièrement, toute la casse de mise en page. Pour cette génération, la solution réaliste s’arrête à la déclaration de System Aware dans le manifeste. Le manifeste est le moyen recommandé pour une déclaration au niveau de l’OS ; indiquer à la fois <dpiAware> (depuis Vista) et <dpiAwareness> (depuis Windows 10 1607) garantit une interprétation conforme à l’intention aussi bien sur les anciens que sur les nouveaux systèmes.2

<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
  <asmv3:windowsSettings>
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
    <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">system</dpiAwareness>
  </asmv3:windowsSettings>
</asmv3:application>

Avec System Aware, la mise à l’échelle automatique de WinForms (chapitre 5) se déclenche une seule fois, au démarrage, en fonction du DPI du moniteur principal, et l’affichage est net sur ce moniteur. Pour un formulaire dont AutoScaleMode.Font est correctement configuré, cette seule déclaration suffit souvent à obtenir un résultat tout à fait présentable. Il existe aussi un moyen de déclarer ce mode à l’exécution via des API comme SetProcessDpiAwareness, mais il ne peut plus être modifié après la création d’une fenêtre, et la déclaration via le manifeste est officiellement recommandée.8 Pour les applications mêlant du code VB6 ou MFC, le raisonnement est identique : c’est le manifeste de l’exe qui détermine le mode pour l’ensemble du processus (MFC lui-même n’offrant aucun support de mise à l’échelle automatique, une vérification écran par écran après la déclaration de System Aware est indispensable ; pour le traitement des actifs de cette génération, voir aussi « Qu’est-ce que MFC sous Windows »).

Je présente également une mesure provisoire côté utilisateur pour le cas où la correction elle-même est impossible : un clic droit sur l’exe → Propriétés → onglet Compatibilité → « Modifier les paramètres de DPI élevé » permet à l’utilisateur de remplacer lui-même le comportement de mise à l’échelle DPI de cette application. Régler le « remplacement du comportement de mise à l’échelle DPI élevé » sur « Système » force la virtualisation DPI (la mise en page ne se casse pas, mais reste floue) ; le régler sur « Système (amélioré) » applique la mise à l’échelle GDI évoquée au chapitre 3, ce qui rend net uniquement le texte dessiné via GDI.6 Ce n’est pas une solution universelle, mais cela vaut la peine de l’inclure dans le manuel d’assistance comme moyen de guider un client par téléphone quand il faut « trouver une solution aujourd’hui même ».

5. AutoScaleMode et le piège du concepteur

Indépendamment du mode de sensibilisation DPI, WinForms dispose de son propre mécanisme de mise à l’échelle automatique au niveau du formulaire. Sans le comprendre, même un passage à System Aware ne mettra pas correctement l’échelle à jour.

Voici comment cela fonctionne. Au moment de la conception, chaque formulaire (ContainerControl) enregistre la base de la mise à l’échelle dans AutoScaleMode, et enregistre la valeur de référence de l’environnement où il a été conçu dans AutoScaleDimensions. À l’exécution, cette valeur est comparée à celle de l’environnement actuel (CurrentAutoScaleDimensions) ; s’il y a un écart, tous les contrôles enfants sont agrandis ou réduits en conséquence.9 C’est donc un mécanisme qui absorbe « l’écart entre l’environnement de conception et celui d’exécution », et la valeur de référence est figée dans le code (Designer.cs).

AutoScaleMode offre en pratique deux choix.

AutoScaleMode Référence Caractéristiques
Font (recommandé, par défaut) Les dimensions de la police du formulaire Comme la taille réelle de la police système change avec le DPI, ce mode suit aussi le DPI par la même occasion. Il suit également les changements de réglage de police de l’utilisateur
Dpi Le DPI de l’écran Proportionnel uniquement au DPI. Adapté aux écrans principalement graphiques
None Mise à l’échelle automatique désactivée. C’est souvent le réglage des applications où le 96 DPI est codé en dur

En cas de doute, choisissez Font. Attention : la documentation officielle indique explicitement que mélanger des modes différents entre un formulaire de base et un formulaire dérivé produit des résultats imprévisibles.9 Pour les applications qui utilisent l’héritage de formulaires, la première étape consiste à faire l’inventaire pour harmoniser le mode sur l’ensemble des formulaires.

Voici maintenant le piège central. Puisque AutoScaleDimensions est un mécanisme qui enregistre « la valeur de l’environnement où la conception a eu lieu », ouvrir le concepteur de Visual Studio sur un moniteur à 150 % puis enregistrer le formulaire réécrit AutoScaleDimensions de Designer.cs avec la valeur correspondant à 150 % (en mode Font, 6F, 12F devient par exemple 9F, 18F). Les coordonnées et les tailles sont elles aussi enregistrées multipliées par 1,5. Lorsqu’un membre de l’équipe en environnement 100 % compile et exécute ensuite ce formulaire, l’ensemble apparaît rétréci, et la revue de diff affiche un énorme changement où les coordonnées de tous les contrôles ont bougé. « Un nouveau membre de l’équipe, équipé d’un portable haut DPI, a touché un seul formulaire, et la mise en page de tout le dépôt s’est cassée » — c’est l’incident classique des consultations sur le haut DPI.

La parade tient, en principe, en une seule règle : ouvrir toujours le concepteur à 100 % (96 DPI). Visual Studio lui-même est une application sensible au DPI, mais le concepteur WinForms (pour .NET Framework) ne l’est pas ; l’ouvrir sur un moniteur haut DPI fait donc apparaître une barre d’information jaune invitant à « redémarrer Visual Studio avec une mise à l’échelle à 100 % ».5 Faites de cette procédure une règle d’équipe : suivre cette barre pour redémarrer en mode non sensible au DPI, modifier le formulaire, puis revenir au mode normal une fois terminé. En ligne de commande, devenv /noScale permet aussi ce démarrage. Notez que pour les projets .NET 6 et ultérieurs, à partir de Visual Studio 2022 17.8, définir <ForceDesignerDPIUnaware>true</ForceDesignerDPIUnaware> dans le fichier projet permet de ne faire fonctionner que l’onglet du concepteur en mode non sensible au DPI, sans redémarrer tout VS (indisponible pour les projets .NET Framework).5

Pour systématiser cette vérification, une méthode simple et efficace consiste à faire rejeter, par la CI ou un hook pre-commit, tout diff où AutoScaleDimensions dans Designer.cs s’écarte de la valeur attendue. Il est moins coûteux d’arrêter un incident à l’entrée du commit que de le corriger après coup.

6. Schémas classiques de casse et comment les corriger

Lorsque vous relevez le mode de sensibilisation DPI (ou que vous vous apprêtez à le faire), les points qui se cassent se ramènent globalement aux schémas suivants.

Ce qui casse Cause Correction
Chevauchement ou troncature des contrôles Mise en page à coordonnées et tailles fixes Remplacer par Anchor/Dock, TableLayoutPanel / FlowLayoutPanel. Exploiter AutoSize
Icônes/images petites ou grossières Bitmaps pour 96 DPI collées telles quelles Préparer des images en plusieurs résolutions et basculer selon le DPI. Si possible, réduire à partir d’une seule image plus grande
Dessin personnalisé (graphiques, schémas, aperçu de bordereau) Pixels codés en dur directement dans Graphics Mettre à l’échelle coordonnées, épaisseur de trait et police en se basant sur DeviceDpi
Les lignes de DataGridView sont trop serrées RowHeight, etc. spécifié en pixels Utiliser AutoSizeRowsMode, ou définir une valeur mise à l’échelle selon le DPI
Les icônes de ToolStrip sont minuscules Taille fixe 16 × 16 Définir ImageScalingSize en fonction du DPI
Seul un contrôle tiers ou ActiveX particulier casse Le contrôle lui-même n’est pas sensible au DPI Mettre à jour vers la version compatible de l’éditeur. À défaut, c’est la limite du niveau de prise en charge atteignable

Le remplacement de la mise en page constitue l’essentiel de l’effort. À l’inverse, un écran déjà construit avec TableLayoutPanel et Dock demande très peu de travail, même après avoir relevé le mode de sensibilisation DPI. Le simple fait d’établir comme règle que tout nouvel écran est construit dès le départ avec des panneaux de mise en page réduit la dette technique future.

Pour la mise à l’échelle du dessin personnalisé, la forme de base consiste à convertir des valeurs calculées pour 96 DPI en se basant sur Control.DeviceDpi (.NET Framework 4.7+ / .NET).

public partial class ChartPanel : Panel
{
    // Convertit une valeur conçue pour 96 DPI vers le DPI actuel
    private int Scale(int value96) => value96 * DeviceDpi / 96;

    protected override void OnPaint(PaintEventArgs e)
    {
        using var pen = new Pen(Color.Navy, Scale(2));
        e.Graphics.DrawRectangle(pen,
            Scale(16), Scale(16), Scale(320), Scale(120));
    }

    // Sous PMv2, le DPI change lors d'un déplacement entre moniteurs, on déclenche donc un nouveau rendu
    protected override void OnDpiChangedAfterParent(EventArgs e)
    {
        base.OnDpiChangedAfterParent(e);
        Invalidate();
    }
}

Jusqu’à System Aware, le DPI est figé au démarrage, il suffit donc d’introduire Scale. Mais sous PMv2, DeviceDpi change à chaque déplacement entre moniteurs, ce qui impose de concevoir la reconstruction des polices, images et valeurs de mise en page mises en cache via les événements de la famille DpiChanged.4 « Combien d’écrans utilisent du dessin personnalisé ? » a un impact direct sur l’estimation de l’effort nécessaire pour la prise en charge de PMv2.

Les contrôles tiers et ActiveX/OCX sont le facteur qui détermine la limite de cette correction. Si le contrôle lui-même n’est pas sensible au DPI, aucun effort côté application hôte ne rendra cet écran pleinement conforme. Vérifiez l’état de prise en charge auprès de l’éditeur et, pour les contrôles ActiveX dont on ne peut espérer de mise à jour, décidez du traitement à leur appliquer à l’aide du tableau de décision de « Conserver, encapsuler ou remplacer un contrôle ActiveX/OCX » avant de fixer l’objectif de prise en charge du haut DPI.

7. Stratégie de réponse par étapes — un tableau de décision pour savoir jusqu’où aller

Sur la base de ce qui précède, on peut organiser le niveau de prise en charge en trois étapes. Faire passer toutes les applications en PMv2 constitue l’idéal technique, mais compte tenu du budget de correction et de la durée de vie des applications, un compromis assumé est souvent la bonne réponse.

  (1) Ne rien faire (Unaware) (2) Passage à System Aware (3) Prise en charge complète de PMv2
Apparence Flou dans tous les environnements (ne se casse pas) Net sur le moniteur principal. Flou sur les moniteurs secondaires ou après un changement de DPI Net sur tous les moniteurs
Travail principal Aucun Déclaration manifeste/configuration + vérification d’AutoScaleMode + contrôle de l’affichage de tous les écrans (2) + audit complet de la mise en page + prise en charge DPI du dessin personnalisé et des ressources image + gestion de DpiChanged
Effort Nul Faible à moyen (essentiellement un travail de vérification proportionnel au nombre d’écrans) Important (essentiellement la correction de la casse ; les contrôles tiers en fixent la limite)
Cas adaptés Retrait prévu d’ici quelques années ; les utilisateurs l’acceptent Application interne centrée sur un poste de bureau fixe et un moniteur unique. Comme première étape Usage mixte portable + moniteur externe. Application phare à longue durée de vie. Produit distribué aux clients

Il y a trois axes de décision : la durée de vie de l’application (combien d’années elle sera encore utilisée), l’environnement d’utilisation (si tout le monde travaille sur le même poste de bureau fixe, System Aware suffit dans la pratique ; si les combinaisons portable + moniteur externe sont fréquentes, le flou de System Aware à chaque déplacement entre moniteurs laissera les utilisateurs insatisfaits), et le budget de correction. La démarche recommandée consiste à mettre en œuvre d’abord (2) comme standard sur toutes les applications, puis à décider, sur la base du volume de casse observé et des contraintes imposées par les contrôles tiers, s’il faut avancer vers (3) — et sur quels écrans seulement. (2) repose essentiellement sur « déclaration + vérification », son risque d’échec est faible, et il améliore pourtant nettement le ressenti des utilisateurs.

Un mot sur les tests. Les problèmes de haut DPI ne se reproduisent souvent pas sur la machine du développeur ; créez donc délibérément les environnements suivants pour les vérifier.1

  • Connectez deux moniteurs à des échelles différentes (par exemple, l’écran intégré du portable à 150 % plus un moniteur externe à 100 %) et faites aller et venir la fenêtre entre les deux
  • Changez de moniteur principal puis reconnectez-vous avant de lancer l’application (le DPI système étant figé au moment de la connexion, il ne change pas sans une nouvelle connexion)
  • Modifiez le réglage de mise à l’échelle pendant que l’application est en cours d’exécution
  • Connectez-vous via Bureau à distance depuis un client à haut DPI (le RDP importe le DPI côté client, si bien que « c’est normal sur la console du serveur, mais cassé en RDP » est un signalement fréquent)

Si l’on vous signale que « ça casse seulement à 125 % », l’expérience montre qu’il est finalement toujours plus rapide de recréer ce facteur d’échelle et de constater par soi-même. Comme le réglage de mise à l’échelle peut aussi être modifié dans une machine virtuelle, disposer d’environnements de vérification à 100 / 125 / 150 / 200 % accélère le diagnostic.

8. Résumé

En environnement haut DPI, le « flou » correspond à la mesure de secours du système d’exploitation (la virtualisation DPI), la « casse » à une prise en charge DPI inachevée — une fois cette correspondance bien assimilée, on peut remonter du symptôme à la cause, puis au remède. La configuration est spécifique à chaque génération : pour .NET, ApplicationHighDpiMode dans le fichier projet ; pour .NET Framework 4.7 et ultérieur, app.config (à ne pas combiner avec le dpiAware du manifeste) ; pour 4.6 et en dessous, System Aware via le manifeste comme limite. Associées à cela, l’harmonisation sur AutoScaleMode.Font et la règle opérationnelle « toujours ouvrir le concepteur à 100 % » sont peu spectaculaires, mais c’est ce qui réduit le plus les incidents.

Cela posé, jusqu’où aller se décide à partir des trois étapes du chapitre 7. D’abord rendre le moniteur principal net grâce à System Aware, puis passer à PMv2 si l’environnement d’utilisation et la durée de vie de l’application le justifient — cette approche en deux temps est celle que nous recommandons réellement dans nos missions de correction. Si le moment est venu de reconsidérer le framework d’interface utilisateur lui-même (WPF est conçu, dès l’origine, pour bien gérer le DPI), prenez aussi en compte « Comment choisir entre WinForms, WPF et WinUI » comme élément de réflexion. Si vous hésitez sur le niveau jusqu’où votre application peut réalistement être corrigée, nous pouvons vous accompagner dès l’inventaire de la composition des écrans et des contrôles.

Articles connexes

Domaines de conseil associés

Komura Soft LLC prend en charge, pour les anciennes applications WinForms / MFC, la mise en conformité au haut DPI (état des lieux, détermination du niveau de prise en charge, correction de la mise en page), l’investigation des causes de dysfonctionnements d’affichage consécutifs au renouvellement de PC, ainsi que les consultations de refonte d’interface utilisateur.

Références

  1. Microsoft Learn, High DPI Desktop Application Development on Windows. Sur le contexte de la mise à l’échelle DPI, le comportement de chaque mode de sensibilisation DPI, le mécanisme d’agrandissement en bitmap des applications non sensibles au DPI, et les points à vérifier lors des tests en environnement DPI mixte.  2 3 4

  2. Microsoft Learn, Setting the default DPI awareness for a process. Sur la méthode de déclaration via les éléments dpiAware / dpiAwareness du manifeste, leur ordre de priorité, et le fait que la déclaration par API n’est pas recommandée.  2

  3. Microsoft Learn, What’s new in Windows Forms .NET 6. Sur l’amorçage via ApplicationConfiguration.Initialize, le réglage ApplicationHighDpiMode du fichier projet (SystemAware par défaut), et les améliorations de la mise à l’échelle PerMonitorV2 apportées par .NET 6.  2 3 4

  4. Microsoft Learn, High DPI support - Windows Forms. Sur le contenu des améliorations haut DPI de .NET Framework 4.7, System.Windows.Forms.ApplicationConfigurationSection dans app.config (DpiAwareness=PerMonitorV2), le fait que la déclaration via le manifeste est déconseillée car elle écrase app.config, ainsi que sur les événements de la famille DpiChanged et DeviceDpi.  2 3 4

  5. Microsoft Learn, Fix DPI display issues in Windows Form Designer. Sur le fait que le concepteur WinForms n’est pas sensible au DPI, le redémarrage avec une mise à l’échelle à 100 % proposé par la barre d’information affichée sur un moniteur haut DPI, devenv /noScale, et ForceDesignerDPIUnaware pour .NET 6+.  2 3

  6. Microsoft Learn, DPI_AWARENESS_CONTEXT handle. Sur la définition et le comportement de chaque contexte Unaware / System Aware / Per-Monitor / Per-Monitor V2 / UNAWARE_GDISCALED (mise à l’échelle GDI, à partir de Windows 10 1809).  2 3 4 5

  7. Microsoft Learn, Mixed-Mode DPI Scaling and DPI-aware APIs. Sur la coexistence de modes de sensibilisation DPI par fenêtre de premier niveau via SetThreadDpiAwarenessContext, et sur les API sensibles au DPI telles que GetDpiForWindow. 

  8. Microsoft Learn, SetProcessDpiAwareness function. Sur la définition de la sensibilisation DPI par défaut d’un processus via une API, la recommandation de préférer une déclaration via le manifeste, et le fait qu’elle ne peut plus être modifiée une fois définie. 

  9. Microsoft Learn, Automatic form scaling - Windows Forms. Sur le fonctionnement de la mise à l’échelle automatique via AutoScaleMode / AutoScaleDimensions / CurrentAutoScaleDimensions, et sur le fait que le mélange des modes Font et Dpi n’est pas pris en charge.  2

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Pourquoi mon application WinForms est-elle floue sur un moniteur 4K ?
Ce n'est pas un bug, mais une mesure de secours du système d'exploitation (la virtualisation DPI). Pour une application qui n'a pas déclaré sa sensibilisation DPI, Windows lui fait croire que « l'écran est en 96 DPI », puis affiche en l'agrandissant, sous forme de bitmap, le résultat que l'application a dessiné en pensant être à 96 DPI. La mise en page ne se casse donc pas, mais l'image devient floue. À l'inverse, une interface « nette mais cassée » correspond au cas où l'application a déclaré sa sensibilisation DPI sans que la mise en page parvienne à suivre : la cause est exactement opposée. La première étape, avant toute correction, consiste à distinguer lequel de ces deux symptômes vous observez.
Comment configure-t-on la prise en charge du haut DPI dans WinForms ?
La méthode diffère selon la génération. À partir de .NET 6, on utilise la propriété ApplicationHighDpiMode du fichier projet (la valeur par défaut est SystemAware) ; sous .NET Core 3.1/.NET 5, il faut appeler Application.SetHighDpiMode avant de créer la moindre fenêtre. Sous .NET Framework 4.7 et ultérieur, on déclare la compatibilité Windows 10 dans le manifeste, puis on utilise le paramètre DpiAwareness d'app.config — mais comme la déclaration dpiAware du manifeste écrase celle d'app.config, les recommandations officielles déconseillent de combiner les deux. En 4.6 et en dessous, déclarer System Aware dans le manifeste reste la limite réaliste.
Faut-il choisir System Aware ou Per-Monitor V2 ?
Nous recommandons une approche en deux étapes. La première étape est le passage à System Aware : l'application se met à l'échelle du DPI du moniteur principal au démarrage et s'affiche nettement sur ce moniteur. Le travail se limite essentiellement à la déclaration et à la vérification, le risque d'échec est faible, et pour une application interne centrée sur des postes de bureau fixes, cette étape suffit dans la pratique. Per-Monitor V2 rend l'affichage net sur tous les écrans, y compris lors d'un déplacement entre moniteurs, mais exige un audit complet de la mise en page, la prise en charge du DPI pour le dessin personnalisé et les ressources image, ainsi que la gestion de l'événement DpiChanged — un effort important, et si des contrôles tiers ne sont pas compatibles, c'est là que se situe la limite atteignable. Le choix se fait selon la durée de vie de l'application, son environnement d'utilisation et le budget de correction disponible.
Pourquoi la mise en page d'un formulaire s'est-elle cassée après son enregistrement dans le concepteur ?
Parce qu'ouvrir le concepteur WinForms de Visual Studio sur un moniteur à haut DPI (150 %, par exemple) puis enregistrer le formulaire réécrit AutoScaleDimensions dans Designer.cs avec la valeur correspondant à 150 %, et enregistre également les coordonnées et tailles multipliées par 1,5. Lorsqu'un membre de l'équipe travaillant en environnement 100 % compile ensuite ce formulaire, l'ensemble s'affiche rétréci. La parade consiste à toujours ouvrir le concepteur à 100 % (96 DPI) : sur un moniteur à haut DPI, la barre d'information jaune propose de redémarrer avec une mise à l'échelle à 100 %. Pour les projets .NET 6 et ultérieurs, le paramètre ForceDesignerDPIUnaware est aussi disponible. Mettre en place un mécanisme qui rejette, dans la CI ou un hook pre-commit, tout écart inattendu d'AutoScaleDimensions est également efficace.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog