web-dev-qa-db-fra.com

Mise à l'échelle de la zone non client (barre de titre, barre de menus) pour une prise en charge haute résolution par moniteur

Windows 8.1 a introduit la possibilité d'avoir différents paramètres DPI pour différents moniteurs. Cette fonctionnalité est connue sous le nom de "prise en charge haute résolution par moniteur". Il persiste et a été affiné dans Windows 10.

Si une application n'accepte pas ( c.-à-d., n'est pas consciente du DPI ou du DPI élevé), elle sera automatiquement mise à l'échelle par DWM au DPI approprié. La plupart des applications entrent dans l'une de ces deux catégories, y compris la plupart des utilitaires fournis avec Windows ( par exemple, Bloc-notes). Sur mon système de test, le moniteur haute résolution est réglé sur une échelle de 150% (144 DPI), tandis que le moniteur normal est réglé sur la résolution système (échelle 100%, 96 DPI). Par conséquent, lorsque vous ouvrez l'une de ces applications sur l'écran haute résolution (ou que vous la faites glisser là-bas), la virtualisation entre en jeu, agrandit tout, mais la rend également incroyablement floue.

D'un autre côté, si une application indique explicitement qu'elle prend en charge le DPI élevé par moniteur, aucune virtualisation n'est effectuée et le développeur est responsable de la mise à l'échelle. Microsoft a une explication assez complète ici*, mais pour le bénéfice d'une question autonome, je vais résumer. Tout d'abord, vous indiquez le support en définissant <dpiAware>True/PM</dpiAware> dans le manifeste. Cela vous permet de recevoir WM_DPICHANGED messages , qui vous indique à la fois le nouveau paramètre DPI ainsi qu'une nouvelle taille et position suggérées pour votre fenêtre. Il vous permet également d'appeler la fonction GetDpiForMonitor et d'obtenir le réel DPI, sans être menti pour des raisons de compatibilité. Kenny Kerr a également écrit n tutoriel complet .

J'ai obtenu tout cela avec succès dans une petite application de test C++. C'est beaucoup de passe-partout et surtout des paramètres de projet, donc je ne vois pas beaucoup d'intérêt à publier un exemple complet ici. Si vous voulez le tester, suivez les instructions de Kenny, ce tutoriel sur MSDN , ou téléchargez l'exemple officiel du SDK . Maintenant, le texte dans la zone client semble bon (à cause de ma gestion de WM_DPICHANGED), mais comme la virtualisation n'est plus effectuée, il n'y a pas de mise à l'échelle de la zone non cliente. Le résultat est que la barre de titre/légende et la barre de menu sont de la taille mauvaise taille - elles ne s'agrandissent pas sur l'écran haute résolution:

La question est donc de savoir comment obtenir la zone non cliente de la fenêtre à l'échelle du nouveau DPI?
Peu importe que vous créiez votre propre classe de fenêtres ou que vous utilisiez une boîte de dialogue — elles ont un comportement identique à cet égard.

Il a été suggéré qu'il n'y a est pas de réponse — que votre seul choix est de dessiner la fenêtre entière, y compris la zone non cliente. Bien que cela soit certainement possible, et en fait ce que font les applications UWP (celles précédemment connues sous le nom de Metro), comme la calculatrice Windows 10, ce n'est pas une option viable pour les applications de bureau qui utilisent de nombreux widgets non-clients et espèrent avoir un aspect natif.

En dehors de cela, c'est manifestement faux. Barres de titre personnalisées ne peut pas être le seul moyen d'obtenir le comportement correct, puisque l'équipe Windows Shell l'a fait. L'humble boîte de dialogue Exécuter se comporte exactement comme prévu, redimensionnant correctement les zones clientes et non clientes lorsque vous les faites glisser entre des moniteurs avec des DPI différents:

L'enquête avec Spy ++ confirme qu'il ne s'agit que d'une boîte de dialogue Win32 standard, rien de compliqué. Tous les contrôles sont des contrôles SDK Win32 standard. Ce n'est pas une application UWP, et ils n'ont pas non plus dessiné la barre de titre, elle a toujours le WS_CAPTION style. Il est lancé par le processus Explorer.exe, qui est marqué comme sensible à haute résolution par moniteur (vérifié avec Process Explorer et GetProcessDpiAwareness). Ce billet de blog confirme que la boîte de dialogue Exécuter et l'invite de commande ont été réécrites dans Windows 10 pour évoluer correctement (voir " Command shells et al."). Que fait la boîte de dialogue Exécuter pour redimensionner sa barre de titre?

Common Item Dialog API , responsable des boîtes de dialogue d'ouverture et d'enregistrement de nouveau style, évolue également correctement lorsqu'elle est lancée à partir d'un processus prenant en charge la haute résolution par moniteur, comme vous pouvez le voir en cliquant sur le bouton "Parcourir "dans la boîte de dialogue Exécuter. Même chose pour Task Dialog API , création la situation étrange où une application lance une boîte de dialogue avec une barre de titre de taille différente . (L'API MessageBox héritée n'a cependant pas été mise à jour et présente le même comportement que mon application de test.)

Si l'équipe Shell le fait, cela doit être possible. Je ne peux tout simplement pas imaginer que l'équipe responsable de la conception/mise en œuvre du support DPI par moniteur ait négligé pour fournir aux développeurs un moyen raisonnable de produire des applications compatibles. Des fonctionnalités comme celle-ci nécessitent le support du développeur, ou elles sont hors service. Même les applications WPF sont endommagées - le projet Microsoft échantillon WPF par moniteur ne parvient pas à mettre à l'échelle la zone non client, ce qui entraîne une barre de titre de taille incorrecte. Je ne suis pas beaucoup pour les théories du complot, mais cela sent un mouvement marketing pour décourager le développement d'applications de bureau. Si c'est le cas, et qu'il n'y a aucun moyen officiel, j'accepterai des réponses qui reposent sur un comportement non documenté.

En parlant de comportement non documenté, la journalisation des messages de la fenêtre lorsque la boîte de dialogue Exécuter est glissée entre des moniteurs avec des paramètres DPI différents montre qu'elle reçoit un message non documenté, 0x02E1. Ceci est quelque peu intéressant car cet ID de message est exactement un de plus que le documenté WM_DPICHANGED message (0x02E0). Cependant, mon application de test ne reçoit jamais ce message, quels que soient ses paramètres de prise en charge DPI. (Curieusement, une inspection minutieuse révèle que Windows légèrement augmente la taille des glyphes minimiser/maximiser/fermer dans la barre de titre lorsque la fenêtre se déplace sur le moniteur haute résolution. Ils ne sont toujours pas aussi gros qu'ils le sont lorsqu'ils sont virtualisés, mais ils sont légèrement plus gros que les glyphes qu'il utilise pour les applications DPI système non mises à l'échelle.)

Jusqu'à présent, ma meilleure idée a été de gérer les WM_NCCALCSIZE message pour ajuster la taille de la zone non cliente. En utilisant le SWP_FRAMECHANGED flag avec la fonction SetWindowPos , je peux forcer la fenêtre à redimensionner et redessiner sa zone non cliente en réponse à WM_DPICHANGED. Cela fonctionne très bien pour réduire la hauteur de la barre de titre, ou même la supprimer complètement, mais cela ne l'agrandira jamais . La légende semble culminer à la hauteur déterminée par le système DPI. Même si cela fonctionnait, ce ne serait pas la solution idéale, car cela n'aiderait pas la barre de menus ou les barres de défilement dessinées par le système… mais au moins ce serait un début. D'autres idées?

* Je sais que cet article dit "Notez que la zone non cliente d'une application prenant en charge le moniteur par DPI n'est pas mise à l'échelle par Windows et apparaîtra proportionnellement plus petite sur un écran à haute résolution." Voir ci-dessus pourquoi c'est (1) faux et (2) insatisfaisant. Je recherche une solution de contournement autre que le dessin personnalisé de la zone non cliente.

36
Cody Gray

Dans toutes les versions de Windows Insider à jour (build> = 14342, SDK version #> = 14332), il existe une API EnableNonClientDpiScaling (qui prend un HWND comme argument) qui activera la mise à l'échelle DPI non client pour les HWND de niveau supérieur . Cette fonctionnalité nécessite que la fenêtre de niveau supérieur s'exécute en mode de détection DPI par moniteur. Cette API doit être appelée à partir du gestionnaire WM_NCCREATE pour la fenêtre. Lorsque cette API est appelée dans une fenêtre de niveau supérieur, sa barre de légende, ses barres de défilement de niveau supérieur, son menu système et sa barre de menus seront mis à l'échelle DPI lorsque le DPI de l'application change (cela peut se produire lorsque l'application est déplacée vers un écran avec une échelle d'affichage différente. ou lorsque le DPI change pour d'autres raisons telles que la modification des paramètres par l'utilisateur ou lorsqu'une connexion RDP modifie le facteur d'échelle).

Cette API ne prend pas en charge la mise à l'échelle de la zone non cliente pour les fenêtres enfants, telles que les barres de défilement dans une fenêtre enfant.

À ma connaissance, il n'y a aucun moyen d'avoir automatiquement une échelle DPI de zone non client sans cette API.

Notez que cette API n'a pas encore été finalisée et qu'elle peut changer avant d'être publiée dans la mise à jour anniversaire de Windows 10. Gardez un œil sur MSDN pour la documentation officielle lorsqu'elle sera définitive.

26
peterfelts

Avec la prise en charge par moniteur V2 DPI dans la mise à jour des créateurs Windows 10 (build 15063), vous pouvez résoudre ce problème facilement sans le EnableNonClientDpiScaling.

Pour activer la sensibilisation DPI par moniteur V2 , tout en prenant en charge les anciennes sensibilisation DPI par moniteur sur les anciennes versions de Windows 10 et Windows 8.1 et sensibilisation DPI sur les versions encore plus anciennes de Windows, rendez votre application manifeste comme ceci:

<Assembly ...>
    <!-- ... --->
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.Microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>True/PM</dpiAware>
            <dpiAwareness xmlns="http://schemas.Microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
        </asmv3:windowsSettings>
    </asmv3:application>
</Assembly>

Références:

8
Martin Prikryl