Partager via


Prise en charge de la sensibilisation par moniteur pour les extendeurs Visual Studio

Les versions antérieures à Visual Studio 2019 avaient leur contexte de prise en charge de l’ppp défini sur le système, au lieu de prendre en charge les PPP par moniteur (PMA). L’exécution dans la prise en charge du système a entraîné une expérience visuelle détériorée (par exemple, des polices floues ou des icônes) chaque fois que Visual Studio devait effectuer un rendu sur plusieurs moniteurs avec différents facteurs d’échelle ou à distance dans des machines avec différentes configurations d’affichage (par exemple, une mise à l’échelle Windows différente).

Le contexte de prise en charge des PPP de Visual Studio 2019 est défini en tant que CONVERTISSEUR, lorsque l’environnement le prend en charge, ce qui permet à Visual Studio de s’afficher en fonction de la configuration de l’affichage où il est hébergé plutôt qu’une configuration définie par le système unique. En fin de compte, la traduction en une interface utilisateur toujours nette pour les zones de surface qui prennent en charge le mode PPP.

Pour plus d’informations sur les termes et le scénario global abordés dans ce document, reportez-vous à la documentation high DPI Desktop Application Development on Windows .

Démarrage rapide

  • Vérifiez que Visual Studio est en cours d’exécution en mode PMA (voir Activation de l’activation de l’activation de la gestion des données de gestion des données de gestion des données)

  • Validez que votre extension fonctionne correctement dans un ensemble de scénarios courants (consultez Test de vos extensions pour les problèmes DE GESTION des données de gestion des données de gestion des données de développement cloud)

  • Si vous rencontrez des problèmes, vous pouvez utiliser les stratégies/recommandations présentées dans ce document pour diagnostiquer et résoudre ces problèmes. Vous devez également ajouter le nouveau package NuGet Microsoft.VisualStudio.DpiAwareness à votre projet pour accéder aux API requises.

Activer la gestion des données de gestion des

Pour activer la gestion des données de gestion des données dans Visual Studio, les exigences suivantes doivent être remplies :

Une fois ces exigences remplies, Visual Studio active automatiquement le mode DE gestion des données de gestion des données dans le processus.

Remarque

Le contenu Windows Forms dans Visual Studio (par exemple, l’Explorateur de propriétés) ne prend en charge que lorsque vous disposez de Visual Studio 2019 version 16.1 ou ultérieure.

Tester vos extensions pour les problèmes de GESTION des données de gestion des données

Visual Studio prend officiellement en charge les frameworks d’interface utilisateur WPF, Windows Forms, Win32 et HTML/JS. Lorsque Visual Studio est placé en mode PPP, chaque pile d’interface utilisateur se comporte différemment. Par conséquent, quelle que soit l’infrastructure de l’interface utilisateur, il est recommandé qu’une passe de test soit effectuée pour s’assurer que l’interface utilisateur est conforme au mode PPP.

Il est recommandé de valider les scénarios courants suivants :

  • Modification du facteur d’échelle d’un environnement de moniteur unique pendant l’exécution de l’application.

    Ce scénario permet de tester que l’interface utilisateur répond au changement dynamique d’ppp Windows.

  • Ancrage/dédocrage d’un ordinateur portable où un moniteur attaché est défini sur le moniteur principal et le moniteur attaché a un facteur d’échelle différent de celui de l’ordinateur portable pendant l’exécution de l’application.

    Ce scénario permet de tester que l’interface utilisateur répond au changement DPI d’affichage, ainsi que la gestion des affichages en cours d’ajout ou de suppression dynamique.

  • Avoir plusieurs moniteurs avec différents facteurs d’échelle et déplacer l’application entre eux.

    Ce scénario permet de tester que l’interface utilisateur répond au changement DPI d’affichage

  • Communication à distance sur un ordinateur lorsque les machines locales et distantes ont différents facteurs d’échelle pour le moniteur principal.

    Ce scénario permet de tester que l’interface utilisateur répond au changement dynamique d’ppp Windows.

Un bon test préliminaire pour déterminer si votre interface utilisateur peut rencontrer des problèmes, c’est si le code utilise les classes Microsoft.VisualStudio.Utilities.Dpi.DpiHelper, Microsoft.VisualStudio.PlatformUI.DpiHelper ou VsUI ::CDpiHelper. Ces anciennes classes DpiHelper prennent uniquement en charge la prise en charge de l’indicateur DPI système et ne fonctionnent pas toujours correctement lorsque le processus est DESM.

L’utilisation classique de ces DpiHelpers se présente comme suit :

Point screenTopRight = logicalBounds.TopRight.LogicalToDeviceUnits();

POINT screenIntTopRight = new POINT
{
    x = (int)screenTopRIght.X,
    y = (int)screenTopRIght.Y
}

// Declared via P/Invoke
IntPtr monitor = MonitorFromPoint(screenIntTopRight, MONITOR_DEFAULTTONEARST);

Dans l’exemple précédent, un rectangle représentant les limites logiques d’une fenêtre est converti en unités d’appareil afin qu’il puisse être transmis à la méthode native MonitorFromPoint qui attend les coordonnées de l’appareil afin de retourner un pointeur de moniteur précis.

Classes de problèmes

Lorsque le mode DE gestion des données de distribution de données est activé pour Visual Studio, l’interface utilisateur peut répliquer les problèmes de plusieurs manières courantes. La plupart, si ce n’est pas le cas, de ces problèmes peuvent se produire dans l’une des infrastructures d’interface utilisateur prises en charge par Visual Studio. En outre, ces problèmes peuvent également se produire lorsqu’une partie de l’interface utilisateur est hébergée dans des scénarios de mise à l’échelle ppp en mode mixte (reportez-vous à la documentation Windows pour en savoir plus).

Création de fenêtre Win32

Lors de la création de fenêtres avec CreateWindow() ou CreateWindowEx(), un modèle courant consiste à créer la fenêtre à des coordonnées (0,0) (le coin supérieur/gauche de l’affichage principal), puis à sa position finale. Toutefois, cela peut entraîner le déclenchement d’un message ou d’un événement modifié par ppp, ce qui peut retener d’autres messages ou événements d’interface utilisateur et éventuellement entraîner un comportement ou un rendu non souhaités.

Positionnement des éléments WPF

Lorsque vous déplacez des éléments WPF à l’aide de l’ancien fichier Microsoft.VisualStudio.Utilities.Dpi.DpiHelper, les coordonnées supérieure gauche peuvent ne pas être calculées correctement chaque fois que les éléments se trouvent sur un ppp non principal.

Sérialisation des tailles ou positions des éléments d’interface utilisateur

Lorsque la taille ou la position de l’interface utilisateur (si enregistrée en tant qu’unités d’appareil) est restaurée dans un contexte DPI différent de celui auquel elle a été stockée, elle est positionnée et dimensionnée de manière incorrecte. Cela se produit parce que les unités d’appareil ont une relation DPI inhérente.

Mise à l’échelle incorrecte

Les éléments d’interface utilisateur créés sur l’ppp principal sont mis à l’échelle correctement, mais lorsqu’ils sont déplacés vers un affichage avec un autre ppp, ils ne sont pas mis à l’échelle et leur contenu est trop grand ou trop petit.

Délimitation incorrecte

Comme pour le problème de mise à l’échelle, les éléments d’interface utilisateur calculent correctement leurs limites sur leur contexte DPI principal, mais lorsqu’ils sont déplacés vers un ppp non principal, ils ne calculent pas correctement les nouvelles limites. Par conséquent, la fenêtre de contenu est trop petite ou trop grande par rapport à l’interface utilisateur d’hébergement, ce qui entraîne un espace vide ou une capture.

Glisser-déplacer

Chaque fois que dans les scénarios DPI en mode mixte (par exemple, différents éléments d’interface utilisateur rendus dans différents modes de prise en charge DPI), les coordonnées de glisser-déplacer peuvent être mal calculées, ce qui entraîne une fin de position de déplacement finale incorrecte.

Interface utilisateur hors processus

Certaines interfaces utilisateur sont créées hors processus et si le processus externe de création est en mode de prise en charge DPI différent de Visual Studio, cela peut introduire l’un des problèmes de rendu précédents.

Contrôles, images ou dispositions Windows Forms affichés de manière incorrecte

Le contenu Windows Forms ne prend pas tous en charge le mode DE DISTRIBUTION de données. Par conséquent, vous pouvez voir un problème de rendu avec des dispositions ou une mise à l’échelle incorrectes. Dans ce cas, une solution possible consiste à restituer explicitement le contenu Windows Forms dans « System Aware » DpiAwarenessContext (reportez-vous à Forcer un contrôle dans un DpiAwarenessContext spécifique).

Contrôles Windows Forms ou fenêtres qui ne s’affichent pas

L’une des principales causes de ce problème est que les développeurs tentent de réparer un contrôle ou une fenêtre avec un DpiAwarenessContext à une fenêtre avec un autre DpiAwarenessContext.

Les images suivantes montrent les restrictions actuelles du système d’exploitation Windows par défaut dans les fenêtres parentes :

A screenshot of the correct parenting behavior

Remarque

Vous pouvez modifier ce comportement en définissant le comportement d’hébergement de threads (reportez-vous à Dpi_Hosting_Behavior énumération).

Par conséquent, si vous définissez la relation parent-enfant entre les modes non pris en charge, elle échoue et le contrôle ou la fenêtre peut ne pas être rendu comme prévu.

Diagnostiquer les problèmes

Il existe de nombreux facteurs à prendre en compte lors de l’identification des problèmes liés à la gestion des problèmes liés à la GESTION des données :

  • L’interface utilisateur ou l’API attend-elle des valeurs logiques ou d’appareil ?

    • L’interface utilisateur et les API WPF utilisent généralement des valeurs logiques (mais pas toujours)
    • L’interface utilisateur et les API Win32 utilisent généralement des valeurs d’appareil
  • Où proviennent les valeurs ?

    • Si vous recevez des valeurs à partir d’une autre interface utilisateur ou d’une AUTRE API, il transmet des valeurs d’appareil ou logiques.
    • Si vous recevez des valeurs provenant de plusieurs sources, utilisez-ils tous/attendent-ils les mêmes types de valeurs ou les conversions doivent-elles être mixtes et mises en correspondance ?
  • Les constantes de l’interface utilisateur sont-elles utilisées et dans quel formulaire sont-elles contenues ?

  • Le thread est-il dans le contexte DPI correct pour les valeurs qu’il reçoit ?

    Les modifications permettant d’activer l’hébergement DPI mixte doivent généralement placer les chemins de code dans le contexte approprié. Toutefois, le travail effectué en dehors de la boucle de message principale ou du flux d’événements peut s’exécuter dans le contexte DPI incorrect.

  • Les valeurs dépassent-ils les limites du contexte PPP ?

    Le glisser-déplacer est une situation courante où les coordonnées peuvent traverser des contextes PPP. La fenêtre tente d’effectuer la bonne opération, mais dans certains cas, l’interface utilisateur de l’hôte peut avoir besoin d’effectuer une conversion pour garantir la correspondance des limites de contexte.

Package NuGet DE LPM

Les nouvelles bibliothèques DpiAwarness sont disponibles sur le package NuGet Microsoft.VisualStudio.DpiAwareness .

Les outils suivants peuvent aider à déboguer des problèmes liés à la gestion des données de gestion des données de gestion des données dans certaines des différentes piles d’interface utilisateur prises en charge par Visual Studio.

Snoop

Snoop est un outil de débogage XAML qui dispose de fonctionnalités supplémentaires que les outils XAML intégrés de Visual Studio n’ont pas. En outre, Snoop n’a pas besoin de déboguer activement Visual Studio pour pouvoir afficher et modifier son interface utilisateur WPF. Les deux principales façons dont Snoop peut être utile pour diagnostiquer les problèmes de GESTION des données de gestion des données est de valider les coordonnées de positionnement logique ou les limites de taille, et pour valider l’interface utilisateur a le bon PPP.

Outils XAML Visual Studio

À l’instar de Snoop, les outils XAML de Visual Studio peuvent aider à diagnostiquer les problèmes de GESTION des données de gestion des données. Une fois qu’un coupable probable est trouvé, vous pouvez définir des points d’arrêt et utiliser la fenêtre Arborescence des visuels en direct ainsi que les fenêtres de débogage, pour inspecter les limites de l’interface utilisateur et les ppp actuels.

Stratégies de résolution des problèmes DE GESTION des données de gestion des données

Remplacer les appels DpiHelper

Dans la plupart des cas, la résolution des problèmes d’interface utilisateur en mode PPP se résume au remplacement des appels dans le code managé à l’ancienne classe d’assistance Microsoft.VisualStudio.Utilities.DpiHelper et Microsoft.VisualStudio.PlatformUI.DpiHelper , avec des appels à la nouvelle classe d’assistance Microsoft.VisualStudio.Utilities.DpiAwareness .

// Remove this kind of use:
Point deviceTopLeft = new Point(window.Left, window.Top).LogicalToDeviceUnits();

// Replace with this use:
Point deviceTopLeft = window.LogicalToDevicePoint(new Point(window.Left, window.Top));

Pour le code natif, il implique de remplacer les appels à l’ancienne classe VsUI ::CDpiHelper par des appels à la nouvelle classe VsUI ::CDpiAwareness .

// Remove this kind of use:
int cx = VsUI::DpiHelper::LogicalToDeviceUnitsX(m_cxS);
int cy = VsUI::DpiHelper::LogicalToDeviceUnitsY(m_cyS);

// Replace with this use:
int cx = m_cxS;
int cy = m_cyS;
VsUI::CDpiAwareness::LogicalToDeviceUnitsX(m_hwnd, &cx);
VsUI::CDpiAwareness::LogicalToDeviceUnitsY(m_hwnd, &cy);

Les nouvelles classes DpiAwareness et CDpiAwareness offrent les mêmes assistances de conversion d’unité que les classes DpiHelper, mais nécessitent un paramètre d’entrée supplémentaire : l’élément d’interface utilisateur à utiliser comme référence pour l’opération de conversion. Il est important de noter que les helpers de mise à l’échelle d’images n’existent pas dans les nouveaux helpers DpiAwareness/CDpiAwareness, et si nécessaire, l’ImageService doit être utilisé à la place.

La classe DpiAwareness managée offre des assistances pour les visuels WPF, les contrôles Windows Forms et les HWNDs et HMONITORs (tous deux sous la forme d’IntPtrs), tandis que la classe CDpiAwareness native offre des helpers HWND et HMONITOR.

Boîtes de dialogue, fenêtres ou contrôles Windows Forms affichés dans le mauvais DpiAwarenessContext

Même après un parentage réussi de windows avec différents DpiAwarenessContexts (en raison du comportement par défaut de Windows), les utilisateurs peuvent toujours voir des problèmes de mise à l’échelle en tant que fenêtres avec différents DpiAwarenessContexts s’adapte différemment. Par conséquent, les utilisateurs peuvent voir des problèmes d’alignement/flou de texte ou d’image sur l’interface utilisateur.

La solution consiste à définir l’étendue DpiAwarenessContext correcte pour toutes les fenêtres et contrôles de l’application.

Boîtes de dialogue en mode mixte de niveau supérieur (TLMM)

Lors de la création de fenêtres de niveau supérieur, telles que des dialogues modals, il est important de s’assurer que le thread est dans l’état correct avant la création de la fenêtre (et de son handle). Le thread peut être placé dans la sensibilisation du système à l’aide de l’assistance CDpiScope en mode natif ou de l’assistance DpiAwareness.EnterDpiScope dans managed. (TLMM doit généralement être utilisé sur les boîtes de dialogue non WPF/windows.)

Mode mixte au niveau enfant (CLMM)

Par défaut, les fenêtres enfants reçoivent le contexte de reconnaissance DPI de thread actuel s’ils sont créés sans parent, ou le contexte de reconnaissance ppp du parent lorsqu’ils sont créés avec un parent. Pour créer un enfant avec un contexte de prise en charge DPI différent de celui de son parent, le thread peut être placé dans le contexte de sensibilisation d’ppp souhaité. Ensuite, l’enfant peut être créé sans parent et réparé manuellement à la fenêtre parente.

Problèmes CLMM

La plupart du travail de calcul de l’interface utilisateur qui se produit dans le cadre de la boucle de messagerie principale ou de la chaîne d’événements doit déjà s’exécuter dans le contexte de sensibilisation aux PPP approprié. Toutefois, si des calculs de coordonnées ou de dimensionnement sont effectués en dehors de ces principaux flux de travail (par exemple, pendant une tâche d’inactivité ou hors du thread d’interface utilisateur), le contexte de prise en charge de l’IPI actuel peut être incorrect, ce qui peut entraîner des problèmes de désaffectation ou de redimensionnement de l’interface utilisateur. Le fait de placer le thread dans l’état correct pour le travail de l’interface utilisateur résout généralement le problème.

Désactiver CLMM

Si une fenêtre d’outil non WPF est en cours de migration pour prendre entièrement en charge la gestion des données de gestion des données, elle doit refuser clMM. Pour ce faire, une nouvelle interface doit être implémentée : IVsDpiAware.

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IVsDpiAware
{
    [ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSDPIMode")]
    uint Mode {get;}
}
IVsDpiAware : public IUnknown
{
    public:
        HRRESULT STDMETHODCALLTYPE get_Mode(__RCP__out VSDPIMODE *dwMode);
};

Pour les langages managés, le meilleur endroit pour implémenter cette interface se trouve dans la même classe que celle qui dérive de Microsoft.VisualStudio.Shell.ToolWindowPane. Pour C++, le meilleur endroit pour implémenter cette interface se trouve dans la même classe que celle qui implémente IVsWindowPane à partir de vsshell.h.

La valeur retournée par la propriété Mode sur l’interface est une __VSDPIMODE (et effectue un cast en uint dans managed) :

enum __VSDPIMODE
{
    VSDM_Unaware    = 0x01,
    VSDM_System     = 0x02,
    VSDM_PerMonitor = 0x03,
}
  • Cela signifie que la fenêtre outil doit gérer 96 PPP, Windows gère la mise à l’échelle pour toutes les autres API. Résultat sur le contenu légèrement flou.
  • Le système signifie que la fenêtre outil doit gérer l’ppp pour le DPI d’affichage principal. Tout affichage avec un ppp correspondant semble net, mais si l’indicateur de résolution est différent ou modifié pendant la session, Windows gère la mise à l’échelle et il sera légèrement flou.
  • PerMonitor signifie que la fenêtre outil doit gérer toutes les API sur tous les affichages et chaque fois que le ppp change.

Remarque

Visual Studio prend uniquement en charge la sensibilisation à PerMonitorV2. Par conséquent, la valeur d’énumération PerMonitor se traduit par la valeur Windows de DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2.

Forcer un contrôle dans un DpiAwarenessContext spécifique

L’interface utilisateur héritée qui n’est pas en cours de mise à jour pour prendre en charge le mode DE gestion des données modifiées peut encore nécessiter des ajustements mineurs pendant que Visual Studio est en cours d’exécution en mode PMA. Un de ces correctifs implique de s’assurer que l’interface utilisateur est en cours de création dans le DpiAwarenessContext approprié. Pour forcer votre interface utilisateur dans un DpiAwarenessContext particulier, vous pouvez entrer une étendue DPI avec le code suivant :

using (DpiAwareness.EnterDpiScope(DpiAwarenessContext.SystemAware))
{
    Form form = new MyForm();
    form.ShowDialog();
}
void MyClass::ShowDialog()
{
    VsUI::CDpiScope dpiScope(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
    HWND hwnd = ::CreateWindow(...);
}

Remarque

Forcer le DpiAwarenessContext fonctionne uniquement sur l’interface utilisateur non WPF et les dialogues WPF de niveau supérieur. Lors de la création d’une interface utilisateur WPF qui doit être hébergée à l’intérieur de fenêtres d’outils ou de concepteurs, dès que le contenu est inséré dans l’arborescence de l’interface utilisateur WPF, il est converti en DpiAwarenessContext du processus actuel.

Problèmes connus

Windows Forms

Pour optimiser les nouveaux scénarios en mode mixte, Windows Forms a modifié la façon dont il crée des contrôles et des fenêtres chaque fois que leur parent n’a pas été défini explicitement. Précédemment, les contrôles sans parent explicite utilisaient une « fenêtre de stationnement » interne comme parent temporaire pour le contrôle ou la fenêtre en cours de création.

Avant .NET 4.8, il y avait une seule « fenêtre de stationnement » qui obtient son DpiAwarenessContext à partir du contexte de reconnaissance DPI de thread actuel au moment de la création de la fenêtre. Tout contrôle non géré hérite du même DpiAwarenessContext que la fenêtre de stationnement lorsque le handle du contrôle est créé et est réparé au parent final/attendu par le développeur de l’application. Cela entraînerait des défaillances basées sur le minutage si la « fenêtre de stationnement » avait un DpiAwarenessContext supérieur à la fenêtre parente finale.

À compter de .NET 4.8, il existe désormais une « fenêtre de stationnement » pour chaque DpiAwarenessContext qui a été rencontré. L’autre différence majeure est que le DpiAwarenessContext utilisé pour le contrôle est mis en cache lors de la création du contrôle, et non lorsque le handle est créé. Cela signifie que le comportement final global est le même, mais peut transformer ce qui était un problème basé sur le minutage en un problème cohérent. Il donne également au développeur d’applications un comportement plus déterministe pour écrire son code d’interface utilisateur et l’étendue correctement.