Mise à l'échelle de la zone non-client (barre de titre, barre de menu) pour le soutien per-monitor high-DPI

Windows 8.1 a introduit la possibilité d'avoir différents paramètres DPI pour différents moniteurs. Cette fonctionnalité est appelée "par-moniteur de haute résolution d'appui."Il persiste et a été raffiné dans Windows 10.

si une application n'accepte pas ( i.e. , est soit DPI-unknown ou High-DPI aware), elle sera automatiquement mise à niveau par DWM vers le DPI approprié. La plupart des applications relèvent de l'une de ces deux catégories. catégories, y compris la plupart des utilitaires fournis avec Windows ( par exemple , Bloc-notes). Sur mon système de test, le moniteur haute-DPI est réglé à l'échelle de 150% (144 DPI), tandis que le moniteur normal est réglé sur le système DPI (échelle de 100%, 96 DPI). Par conséquent, lorsque vous ouvrez l'une de ces applications sur l'écran high-DPI (ou que vous la faites glisser là-bas), la virtualisation entre en jeu, amplifiant tout, mais aussi la rendant incroyablement floue.

d'un autre côté, si une application indique explicitement qu'elle prend en charge un niveau élevé de DPI par moniteur, alors 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 mettant <dpiAware>True/PM</dpiAware> dans le manifeste. Cette opte vous en recevant WM_DPICHANGED messages , qui vous indique à la fois le nouveau réglage DPI ainsi que la nouvelle taille et la position suggérées pour votre fenêtre. Il vous permet également d'appeler la fonction GetDpiForMonitor et d'obtenir le actual DPI, sans être menti pour des raisons de compatibilité. Kenny Kerr a également écrit un tutoriel complet .

j'ai eu tout ça avec succès dans une petite application de test C++. C'est beaucoup de boilerplate et surtout des paramètres de projet, donc je ne vois pas beaucoup d'intérêt à poster un exemple complet ici. Si vous voulez le tester, suivez les instructions de Kenny, ce tutoriel sur MSDN , ou téléchargez l'échantillon officiel SDK . Maintenant, le texte dans la zone client semble bon( à cause de mon traitement de WM_DPICHANGED ), mais parce que la virtualisation n'est plus effectuée, il n'y a pas d'échelle de la zone non-client. Le résultat est que la barre de titre/Légende et la barre de menu sont le mauvaise taille - ils ne deviennent pas plus grand sur l'écran haut-DPI:

alors la question Est, Comment puis-je obtenir le non-zone client de la fenêtre à l'échelle à la nouvelle DPI?

Il n'a pas d'importance si vous créez votre propre classe de fenêtre ou utilisez un dialogue-ils ont un comportement identique à cet égard.

It a été suggéré qu'il est pas de réponse-que votre seul choix est de dessiner la fenêtre entière, y compris la zone non-client. 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 bureautiques qui utilisez de nombreux widgets non-client et l'espoir d'avoir l'air natif.

mis à part cela, c'est manifestement faux. Les barres de titre dessinées sur mesure ne peuvent pas être le seul moyen d'obtenir le comportement correct, puisque L'équipe Windows shell l'a fait. Le dialogue humble Run se comporte exactement comme prévu, redimensionnant correctement les zones client et non client comme vous le faites glisser entre les moniteurs avec des DPIs différents:

enquête avec Spy++ confirme qu'il s'agit juste d'un Bog-standard Win32 dialogue-rien de fantaisie. Tous les contrôles sont des contrôles standard Win32 SDK. Il ne s'agit pas d'une application UWP, et ils n'ont pas non plus dessiné sur mesure la barre de titre-il a encore le style WS_CAPTION . Il est lancé par l'explorateur.processus exe, dont est marqué comme per-monitor high-DPI aware (vérifié avec L'Explorateur de processus et GetProcessDpiAwareness). This blog post confirme que la boîte de dialogue Exécuter et L'invite de commande ont été réécrits dans Windows 10 à l'échelle correctement (voir " commande shells et al. "). Que fait le dialogue Run pour redimensionner sa barre de titre?

le Common Item Dialog API , responsable des dialogues Open et Save de nouveau style, se balance également correctement lorsqu'il est lancé à partir d'un processus qui est per-moniteur high-DPI conscient, comme vous pouvez voir en cliquant sur le bouton "Parcourir" dans la boîte de dialogue Exécuter. Même chose pour l'API de dialogue de tâche" 1519920920 , créant la situation étrange où une application lance une boîte de dialogue avec une barre de titre de taille différente . (L'API MessageBox legacy N'a pas été mise à jour, cependant, et affiche le même comportement que mon application de test.)

si l'équipe shell le fait, cela doit être possible. je ne peux pas imaginer que l'équipe chargée de la conception et de la mise en œuvre de l'assistance per-monitor DPI a négligé de fournir un moyen raisonnable aux développeurs de produire des applications compatibles. Des fonctionnalités comme celle-ci nécessitent le support du développeur, ou elles sont "Out-of-the-box". Même les applications WPF sont cassées-le projet de Microsoft Per-Monitor Aware WPF Sample ne parvient pas à mettre à l'échelle la zone non-client, résultant en une barre de titre qui est la mauvaise taille. Je ne suis pas pour les théories de conspiration, mais ça sent marketing mouvement pour décourager le développement d'applications bureautiques. Si c'est le cas, et il n'y a pas de moyen officiel, j'accepterai des réponses qui reposent sur un comportement non-documenté.

en parlant de comportement non documenté, les messages de la fenêtre de journalisation quand la boîte de dialogue Run est déplacée entre les moniteurs avec différents paramètres DPI montrent qu'elle reçoit un message non documenté, 0x02E1 . C'est assez intéressant parce que ce message ID est exactement un plus grand que le documenté WM_DPICHANGED message ( 0x02E0 ). Mon application de test ne reçoit jamais ce message, cependant, indépendamment de ses paramètres DPI-sensibilisation. (Curieusement, une inspection minutieuse révèle que Windows légèrement augmente la taille des glyphes de minimize/maximize/close sur la barre de titre lorsque la fenêtre se déplace sur le moniteur high-DPI. 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 système-DPI non scalé.)

jusqu'à présent, ma meilleure idée a été de gérer le message" 15191100920 WM_NCCALCSIZE 1519130920 "pour ajuster la taille de la zone non-client. En utilisant le drapeau SWP_FRAMECHANGED avec le SetWindowPos fonction , je peux forcer la fenêtre à redimensionner et redessiner sa zone non-client en réponse à WM_DPICHANGED . cela fonctionne très bien pour réduire la hauteur de la barre de titre, ou même l'enlever tout à fait, mais il ne sera jamais plus grand . 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 avec la barre de menu dessinée par le système ou les barres de défilement...mais au moins ce serait un début. D'autres idées?

* Je sais que cet article dit "notez que la zone non-client d'une application consciente de per monitor–DPI n'est pas mise à l'échelle par Windows, et apparaîtra proportionnellement plus petit sur un écran haut DPI." Voir ci-dessus pour expliquer pourquoi c'est (1) mauvais et (2) insatisfaisant. Je cherche une solution autre que le dessin personnalisé de la zone non-client.

33
demandé sur Community 2016-04-26 15:24:42

2 réponses

dans n'importe quelle construction D'initié de Windows à jour (build >= 14342, SDK version# >= 14332) il y a une API EnableNonClientDpiScaling (qui prend un HWND comme argument) qui permettra la mise à l'échelle DPI non-client pour les HWND de haut niveau. Cette fonctionnalité exige que la fenêtre de haut niveau s'exécute en mode de prise de conscience DPI par moniteur. Cette API doit être appelée à partir du gestionnaire WM_NCCREATE pour la fenêtre. Lorsque cette API est appelée sur une fenêtre de haut niveau, sa barre de légende, barres de défilement de haut niveau, menu du système et menubar vont changer L'échelle du DPI lorsque le DPI de l'application change (cela peut se produire lorsque l'application est déplacée vers un écran avec une valeur d'échelle d'affichage différente ou lorsque le DPI change pour d'autres raisons telles que la modification des paramètres de l'utilisateur ou lorsqu'une connexion RDP change le facteur d'échelle).

cette API ne supporte pas la mise à l'échelle des zones non-client pour les fenêtres enfant, telles que les barres de défilement dans une fenêtre enfant.

à ma connaissance, il n'y a aucun moyen avoir une balance DPI 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 Windows 10 Anniversary. Gardez un oeil sur MSDN pour la documentation officielle quand elle sera définitive.

26
répondu peterfelts 2016-06-03 22:30:19

avec par Moniteur V2 DPI awareness dans Windows 10 Creators Update (build 15063) vous pouvez résoudre ce problème facilement sans le EnableNonClientDpiScaling .

pour activer par Moniteur v2 DPI awareness , tout en supportant l'ancien par Moniteur DPI awareness sur les anciennes versions de Windows 10 builds et Windows 8.1 et DPI awareness sur les versions encore plus anciennes de Windows, Faites 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>

, les Références:

7
répondu Martin Prikryl 2018-07-23 09:14:44