[VS Extensibility] Part 3 : Quelques interactions avec Visual Studio
Après avoir vu le fonctionnement d’un Add-In au sein de Visual Studio (voir part 1) et comment créer ses propres fenêtres « VS like » hébergeant nos UserControls .NET (voir part 2), nous allons voir dans cette 3ème partie quelques interactions possibles avec l’IDE.
C’est depuis notre objet DTE2 (le _applicationObject dans notre classe Connect) que nous avons la possibilité d’accéder à notre IDE depuis le code. Voyons tout cela plus détails avant d’illustrer quelques interactions avec la StatusBar, Propeties Window ou encore les Window Panes.
L’objet DTE
L’objet DTE2 (EnvDTE80.DTE2 ou anciennement EnvDTE.DTE) est l’objet de haut niveau du modèle objet de Visual Studio. C’est lui qui nous permet d’interagir avec l’IDE depuis notre code.
Nous nous en sommes déjà servi dans notre part 1 et 2 afin de créer des fenêtres VS (DTE2.Windows) ou encore de rajouter le bouton de notre Add-In dans le menu Tools (DTE2.Commands).
Je vous laisserai observer l’ensemble des membres de notre objet DTE2 que vous pourrez retrouver dans l’intégralité sur la MDSN à l’adresse : http://msdn.microsoft.com/en-us/library/envdte80.dte2_members(VS.80).aspx
Les membres qui nous intéresserons dans cet article :
- ActiveDocument : permet de récupérer le document actuel.
- StatusBar : représente la StatusBar de Visual Studio.
- ItemOperations : permettant d’effectuer des opérations sur des éléments dans VS (ex: créer/ouvrir des documents)
- Windows : permet d’accéder aux fenêtres de VS.
Informer sur l’état de notre Add-In dans la StatusBar
Sans rentrer dans les détails, nous nous servirons principalement de trois membres :
- Clear : permet de reset la StatusBar
- Progress : permet d’afficher une barre de progression dans la Status Bar
- Text : défini le texte à afficher dans la Status Bar
Prenons pour ‘exemple de rajouter le code ci-dessus après la création de notre fenêtre (cf. Part 2) :
1 | _applicationObject.StatusBar.Text = "MonAddinDemo lancé !"; |
Observez le résultat dans notre VS une fois notre Add-In lancé :
Nous avons aussi la possibilité d’afficher une barre de progression avec la méthode Progress. Exemple :
1 | _applicationObject.StatusBar.Progress(true, "Etape 1", 33, 100); |
Le 1er argument précise si l’on doit afficher la barre de progression. Le 2eme argument est le texte à afficher dans la StatusBar et les deux derniers arguments définissent l’état d’avancement de notre barre de progression (ici 33 pour 100 !).
Le résultat dans notre Visual Studio :
N’oubliez pas de rappeler la méthode Progress en précisant le 1er argument (InProgress) à false pour masquer la barre de progression une fois la tache terminée !
Ouvrir un site web avec les ItemOperations
Les ItemOperations vont permettre de manipuler l’équivalent des fenêtres des dialogues « Ajouter un élément » ou « Créer/Ouvrir un fichier ».
On retrouvera parmi les méthodes de cet objet :
- AddExistingItem : ajoute un élément existant au projet
- AddNewItem : ajoute un nouvel élément au projet
- IsFileOpen : indique si le fichier est ouvert
- Navigate : permet d’ouvrir un URL dans le web browser de VS
- NewFile : crée un nouveau fichier
- OpenFile : ouvre un fichier
Pour l’exemple nous allons simplement ouvrir un site web dans une nouvelle fenêtre. (Cela ressemblera étrangement à notre UC contenant notre Web Browser !)
1 2 | _applicationObject.ItemOperations.Navigate("https://sebastien.warin.fr", vsNavigateOptions.vsNavigateOptionsNewWindow); |
Écrire dans les Output Window Panes
Nous nous souvenons de l’objet EnvDTE80.Windows pour son CreateToolWindow qui nous avait servi dans notre Part 2 pour la création de fenêtre dans Visual Studio.
Nous avons aussi une méthode forte intéressante nommé Item ! Elle permet de récupérer une fenêtre (objet Window) à partir de son Guid. Pour nous aider, l’énumération EnvDTE.Constants contient tous les GUID des fenêtres principales de VS :
Nous allons dans notre exemple, créer un nouveau « Pane » dans la fenêtre de sortie (Output) de Visual Studio. Ajoutons le code ci-dessous :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | private OutputWindowPane monWindowPane = null; private void EcrireDansMonWindowPane(string texte) { if (monWindowPane == null) { // Récupération de la fenetre Output (vsWindowKindOutput) var window = _applicationObject.Windows.Item(EnvDTE.Constants.vsWindowKindOutput) as Window2; var outputWindow = window.Object as OutputWindow; // Pour tous les Window Panes dans mon Output foreach (OutputWindowPane pane in outputWindow.OutputWindowPanes) { // Si il existe deja un pane nommée "Mon Addin Demo" if (pane.Name == "Mon Addin Demo") { // sauvegarde la référence dans monWindowPane monWindowPane = pane; break; } } // Si pas trouvé, on le crée (OutputWindowPanes.Add) if (monWindowPane == null) monWindowPane = outputWindow.OutputWindowPanes.Add("Mon Addin Demo"); } // Activation du Pane monWindowPane.Activate(); // Ecriture de l'heure et du texte dasn notre pane "Mon Addin Demo" monWindowPane.OutputString(string.Format("{0} : {1}\n", DateTime.Now.ToLongTimeString(), texte)); } |
Cette méthode cherchera à récupérer notre « Pane » nommé « Mon Addin Demo » ou le créera si celui ci n’existe pas. Une fois récupéré, elle écrira le texte passé en paramètre ainsi que l’heure.
Ajoutons maintenant, après la création de notre fenêtre dans notre méthode OnConnection, le code ci-dessus pour tester notre méthode :
1 2 | EcrireDansMonWindowPane("MonAddinDemo lancé"); EcrireDansMonWindowPane("Ceci est un test !"); |
Observons le résultat dans notre Visual Studio :
Afficher les propriétés d’un objet dans la Properties Window
La Properties Window, bien connu des développeurs, permet d’afficher les propriétés d’un objet dans une sorte de tableau (en WinForm on utilise le PropertyGrid pour reproduire le même rendu) :
Pour passer un objet à cette fenêtre nous utiliserons la méthode SetSelectionContainer en passant la référence à un tableau d’objet (object[]).
Pour cela nous allons créer un nouveau UserControl dans lequel nous placerons un bouton qui appellera le SetSelectionContainer pour afficher un objet de type Personne que nous créerons également :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public partial class UserControl1 : UserControl { public class Personne { public string Nom { get; set; } public string Prenom { get; set; } public int Age { get; set; } } public Window Window { get; set; } public UserControl1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { object[] tmpl = new object[] { new Personne() { Age=22, Prenom="Sebastien", Nom = "Warin"} }; Window.SetSelectionContainer(ref tmpl); } } |
Dans notre classe Connect, l’appel à la méthode CreateToolWindow2 diffère un peu ! (N’oubliez pas de passer la référence de la Window créée à votre UC !)
1 2 3 4 5 6 7 8 9 10 11 | object monUC = null; var vsWindows = _applicationObject.Windows as Windows2; // Creation de la fenetre VS _maWindow = vsWindows.CreateToolWindow2(_addInInstance, Assembly.GetExecutingAssembly().Location, typeof(UserControl1).FullName, "Ma 1er fenetre VS", Guid.NewGuid().ToString("B"), ref monUC); // On passe la ref de notre Window (monUC as UserControl1).Window = _maWindow; // Affichage de la fenetre VS _maWindow.IsFloating = false; _maWindow.Linkable = false; _maWindow.Visible = true; |
Le résultat dans Visual Studio :
Attention, la méthode SetSelectionContainer ne peut être appelée que par une fenêtre ayant été créée par un CreateToolWindow. C’est pour cela que nous passons l’objet _maWindow à notre UC et que notre UC se sert de cet objet pour appeler le SetSelectionContainer.
Nous aurions pu penser à récupérer la Properties Window pour appeler le SetSelectionContainer comme ci-dessous :
1 2 3 4 | // Récuperation de la fenetre de Propriété var propWin = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindProperties) as Window2; // Affichage de l'objet dans la Properties Window propWin.SetSelectionContainer(ref tmpl); |
Mais une exception se lèvera vous indiquant que cela n’est possible que par une fenêtre ayant été créée par CreateToolWindow :
1 2 | System.Runtime.InteropServices.COMException was caught Message="Only tool windows created by Add-ins (Window.CreateToolWindow) can offer Selection" |
Récupérer le document en cours : le ActiveDocument
Le ActiveDocument permet de récupérer le document en cours. Vous pouvez par exemple tester si le type du document en cours est de type « Text ». Si oui vous pourrez, grâce à la méthode Object() le récupérer sous forme un TextDocument. Vous pourrez ensuite interagir avec le contenu du document très facilement.
A ce sujet, en Février 2006 (il y a déjà 3 ans !!), je publiais sur mon blog une macro permettant de générer des propriétés très rapidement (https://sebastien.warin.fr/2006/02/07/7-macrosebgenererproprietes/). La macro utilisait le DTE.ActiveDocument pour générer le code dans vos fichiers sources.
Pour notre exemple nous allons simplement afficher e texte sélectionné dans une MessageBox. Le code est :
1 2 3 4 5 | if (_applicationObject.ActiveDocument.Type == "Text") { var txtDoc = _applicationObject.ActiveDocument.Object("TextDocument") as TextDocument; MessageBox.Show(txtDoc.Selection.Text); } |
Exécutons notre projet, et ouvrons un fichier de type « Text » dans VS. Sélectionnons du texte au hasard et activons notre Add-In. Le résultat en image :
Conclusion
S’achève ainsi notre découverte des possibilités d’extension par Add-In de votre Visual Studio. Nous avons vu ici quelques interactions possibles avec celui-ci pour s’intégrer le plus possible dans l’IDE.
Nous verrons par la suite quelques tips (astuces) sur le packaging d’Add-In ou encore la personnalisation de l’icône de votre Add-In dans le menu Tools.