Feb.04

[VS Extensibility] Part 2 : créez vos fenêtres d’outils avec CreateToolWindow2 !

image Continuons notre découverte sur développement d’add-ins pour Microsoft Visual Studio, l’IDE de prédilection pour les développements sur plateforme Microsoft/.NET. Dans la 1ere partie nous avons vu de quoi était constitué un add-in et comment il fonctionnait. Dans cette 2ème partie nous verrons comment créer des fenêtres d’outils dans Visual Studio.

Nous nous étions contenté, dans la partie 1, d’afficher une simple MessageBox lors du clique sur le bouton de notre add-in présent dans le menu Tools (ou Outils) de VS. Nous avons vu que la gestion du clique se faisait dans la méthode Exec (implémentée par l’interface IDTCommandTarget) quand l’argument commandName était égal au nom de notre commande (ex : commandName == « MonAddinDemo.Connect.MonAddinDemo »).

Mais un Add-In lançant une MessageBox n’est pas forcement très utile 🙂 Nous avons plutôt besoin d’afficher notre propre fenêtre embarquant toute la GUI de notre Add-In.

Fenêtre Visual Studio ou simple WinForm ?

imageEt si à la place de notre MessageBox.Show() je faisais un new MaWinForm().Show() afin d’afficher une WinForm que j’aurais préalablement créée dans mon projet ?

La réponse serait que cela marcherai sans aucun problème, il en résulterait l’affichage notre propre fenêtre dans depuis Visual Studio.

Vous allez me dire « mais pourquoi écrire un article sur l’affichage d’une simple WinForm ? ». La réponse est ci-dessus :  un new MaWinForm().Show()  permet simplement d’afficher la fenêtre depuis VS. Il ne s’agit en aucun cas d’une fenêtre dans Visual Studio.

En d’autre terme vous ne bénéficiez pas du look&feel VS, vous ne pouvez pas non plus la docker dans l’IDE ou l’afficher en tant que document VS. Pour ce faire, il faut créer la fenêtre par Visual Studio.

D’ailleurs nous ne créerons pas de Winform mais un UserControl qui sera docké dans une fenêtre Visual Studio.

CreateToolWindow ou CreateToolWindow2 ?

La création d’une fenêtre par VS se réalise en appelant la méthode EnvDTE80.Windows2.CreateToolWindows2() ou EnvDTE.Windows.CreateToolWindow().

Avant de détailler son utilisation, nous voyons pourquoi y a t-il deux CreateToolWindow ?

On retrouve en fait deux namespaces presque identiques :

  • EnvDTE : une assembly « wrapper » vers le composant COM du Core Automation de Visual Studio (inchangée depuis VS2003).
  • EnDTE80 : une extension de EnvDTE apparut depuis VS 2005 (8.0) pour supporter de nouvelles fonctionnalités.

Dans le 1er, EnvDTE, on retrouve la classe Windows contenant la méthode CreateToolWindow.

Dans la 2ème, EnvDTE80, on retrouve la classe Windows2 contenant la méthode CreateToolWindow2. (note: on retrouve aussi CreateToolWindow dans Windows2).

Comme vous l’aurait comprit, CreateToolWindow est un peu obsolète par rapport à CreateToolWindow2. Dans des soucis de versionning, beaucoup de classes ou d’interfaces ont le suffixe « 2 » dans le namespace EnvDTE80 pour pouvoir les différencier avec ceux d’EnvDTE.

Voyons leurs signatures :

Hormis le fait que les noms des paramètres sont un peu différents, nous remarquerons surtout l’apparition d’un nouveau paramètre de type string nommé Assembly.

Analysons les différents arguments que nous trouvons dans la méthode CreateToolWindow2 :

  • Addin (ex-AddInInst): l’instance de l’addin passée dans la méthode OnConnection (variable _AddInInst dans notre Connect).
  • Assembly : répertoire de notre assembly
  • Class (ex-ProgID) :  nom complet du UserControl à afficher dans la fenêtre VS.
  • Caption : titre de la fenêtre
  • GuidPosition : un GUID permettant d’ identifier la fenêtre.
  • ControlObject (ex-DocObj) : référence vers l’instance de votre usercontrol.

Dans les deux cas, ces méthodes retournent un objet Window représentant la fenêtre VS crée.

La principale différence entre les deux méthodes est qu’ anciennement nous affichons un ActiveX dans les fenêtres VS. Depuis VS2005 (et l’arrivé du namespace EnvDTE80) nous avons la possibilité de passer directement un UserControl .NET (d’où l’ajout de l’argument Assembly).

Vous pouvez encore utiliser la méthode CreateToolWindow mais il faudra marquer votre UC ComVisible ou enregistrer l’assembly pour l’interop. COM (dans les propriétés du projet). Développeurs .NET comme nous sommes, en 2009, je pense que nous pouvons oublier les anciennes versions de Visual Studio antérieures à 2005, le CreateToolWindow et les ActiveX au profit d’une méthode plus récente et plus sûre 😉

Attention, notez aussi qu’il faut absolument que votre UC soit dans la même assembly que votre Add-In (classe Connect). Sinon lors de l’appel de la méthode CreateToolWindow2, la référence vers votre UC (ControlObject) restera à null.

Cas concret : créer des fenêtres d’outils VS

Pour détailler cela, reprenons notre projet « MonAddinDemo » vu en partie 1, et ajoutons un UserControl nommé MaFenetre.

Pour l’exemple nous allons, pour faire très simple, créer un mini browser Web. Dans votre UserControl déposez un WebBrowser docké dans l’UC. Plaçons ensuite une méthode publique NavigateTo pour définir l’adresse du WebBrowser :

Notre UC est prêt ! Voyons comment le lancer dans une fenêtre VS.

Pour cela reprenons notre classe Connect et dans la méthode Exec, ajoutons pour le clique de notre bouton le code ci-dessus :

Le résultat devient alors « VS like » 🙂

image

Étant donné que nous avons désormais une fenêtre VS, nous avons la possibilité de la docker dans un endroit de notre IDE :

image image

Et même la possibilité de la définir en tant que Document VS :

image

Nous pouvons aussi, avant d’afficher notre fenêtre (Visible = true) définir des propriétés à notre fenêtre comme par exemple :

  • Linkable (booléen) : permet de définir si la fenêtre peut être dockée dans l’IDE comme vu ci-dessus.
  • IsFloating (booléen) : permet de définir si la fenêtre peut être « volante ».

Par exemple, pour créer la fenêtre sous forme d’un document VS comme le montre la dernière capture, nous écrirons :

GuidPosition dynamique : attention au Guid.ToString()

Le GUID que l’on passe à la méthode CreateToolWindow2 permet d’identifier de manière unique la fenêtre. Visual Studio se sert de cela pour mémoriser l’état et la position de la fenêtre.

Remarquez que si vous cliquez une deuxième fois sur votre add-in dans le menu Tools, aucune nouvelle fenêtre n’est créée. En fait la méthode CreateToolWindow2 vous renverra la même référence que lors du 1er appel. Cela est normal étant donné que nous avons mis en dur le GUID pour l’argument GuidPosition.

Dans certain cas nous souhaitons créer plusieurs fenêtres du même type. Par exemple nous pouvons imaginer d’avoir la possibilité de créer plusieurs fenêtres pour plusieurs sites web. Dans ce cas là il nous faut générer un GUID dynamiquement !

Nous allons donc penser à faire un :

au niveau de l’argument GuidPosition.

Mais à l’execution nous aurons une erreur de type :

En effet VS attent un GUID au format {<guid>} or un ToString() sur un Guid n’inclut pas les { } !

Pour cela nous avons deux solutions :

  1. Faire de manière « peu propre », en concaténant :
  2. Modifier le format du Guid (cf. MSDN) :

Du fait si l’on remplace notre appel à CreateToolWindow2 par :

… dès que l’on clique plusieurs fois sur notre add-in, plusieurs fenêtres seront créées :

 image

Aller un peu plus loin dans la gestion de la fenêtre

La création des fenêtres dans la méthode Exec n’est pas « très propre ». Il est préférable de les créées dans la méthode OnConnection quand le connectMode est à Startup ou AfterStartup.

Dans notre dernière exemple nous allons permettre de créer une fenêtre volante (toujours de notre UC ‘MaFenetre’ contenant le WebBrowser vers mon site !).

Ajoutons tout d’abord une variable privée à notre classe Connect :

Dans notre méthode OnConnection nous avons actuellement le code permettant d’ajouter notre addin au menu Tools (comme vu en partie 1) lorsque le connectMode est à UISetup.

En dessus de cela ajoutons la création de notre fenêtre lorsque nous sommes à Sartup ou AfterStartup :

Dans notre méthode Exec maintenant, nous allons simplement nous contenter d’afficher _maWindow si celle ci n’est pas Visible. Voici le code complet de cette méthode : 

Pour terminer nous allons compléter la méthode QueryStatus, qui je le rappelle, définit l’état de notre bouton dans le menu Tools. Nous allons en effet griser le bouton si la fenêtre est déjà lancée. Voici le code complet de notre méthode QueryStatus :

Observez le résultat une fois la fenêtre affichée :

 image

Conclusion

Vous voilà maintenant capable de créer vos fenêtres dans Visual Studio, de les disposer dans l’IDE de manière volantes, dockées ou comme de simples documents ouverts dans Visual Studio. Dans chacune de ces fenêtres se trouve un UserControl .NET qui contiendra l’interface graphique de votre add-in. En passant l’objet DTE à votre UC, vous avez tout à votre disposition pour interagir avec l’IDE depuis celui-ci.

Dans une troisième partie nous illustrerons quelques interactions possibles avec l’IDE comme par les Window Panes, la Status Bar ou encore la Properties Window.

.NET,visual studio
Share this Story:
  • facebook
  • twitter
  • gplus

Comments(3)

  1. Sylvain Rodrigue
    le 25 février 2009 à 22:41

    Excellent article — c’est ce que je cherchais depuis un moment sur MSDN, qui est plutôt pauvre sur le sujet…

    Merci !

  2. DevMec
    le 1 novembre 2013 à 21:16

    Bonjour,

    exellement tuto, merci.

    Par contre je ne comprends pas, je n’ai qu’une seul instance de visual studio et pourtant mon code ne compile pas a cause de la librairie qui est utilisé. (En fait l’instance de visual studio que j’utilise pour créer mon add in, à déja chargé mon add in :’-( )

    Avez vous une idée ?

    merci

  3. DevMec
    le 1 novembre 2013 à 22:02

    [update]

    en fait, arreté toutes mes instances de Visual Studio,
    coupé le fichier add in dans le repertoire Mes Documents\Visual Studio\add in
    J’ai l’ancé la solution, puis collé le fichier au même endroit.(Mes Documents\Visual Studio\add in)

    Maintenant c’est OK.

Leave a comment

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

Comment