Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Carl's Blog

.Net et ses amis
VSTO 3.0 : Récupérer l’ouverture d’un nouveau document

  Si l’on développe un complément Microsoft Office 2007 via les Visual Studio Tools for Office 3.0 (VSTO 3.0), le template du projet par défaut contient une classe ThisAddin qui est notre point d’entrée de l’add-in.

Cette classe contient en ensemble de propriétés bien pratiques pour inter-opérer avec l’application Office dans laquelle le complément va être embarqué. Parmi ces propriétés, une des plus importantes est Application, de type Microsoft.Office.Interop.Word.Application qui justement permet de récupérer un objet représentant l’application hôte. Via cette propriété vous allez pouvoir récupérer toutes sortes d’informations au niveau de l’application tel que les documents ouvert, le document actif, les évènements d’ouverture ou de fermeture de documents etc. Une description plus précise de l’objet est disponible sur la msdn ici : http://msdn.microsoft.com/en-us/library/bb148369.aspx

Si l’on regarde dans l’Intellisense de Visual Studio, l’on voit que nous avons accès à une ribambelle d’évènements tels que :

Exemples d'évènements disponibles

 

 

 

 

 

 

 

 

 

 

 

Néanmoins, l’Intellisense ne nous propose pas d’évènement lors de la création d’un nouveau document : en effet, celui-ci est différent de l’ouverture d’un document représenté par l’évènement DocumentOpen et qui n’est pas levé dans ce cas.

Comme tout bon développeur le premier réflexe est de se tourner vers la documentation qui surprise nous indique qu’un évènement NewDocument existe (http://msdn.microsoft.com/en-us/library/bb288738.aspx), seulement celui-ci est « écrasé » par une propriété du même nom et par conséquent il nous est impossible d’écrire la ligne suivante :

Erreur de déclaration

 

 

 

Heureusement, l’évènement étant proposé via l’interface ApplicationEvents4_Event, il nous est possible de caster l’objet Application pour pouvoir nous abonner à l’évènement, ce qui s’écrit comme ceci :

((ApplicationEvents4_Event)this.Application).NewDocument += new ApplicationEvents4_NewDocumentEventHandler(OnNewDocument);                                  

Bon code,

Carl

 

Un peu plus sur le déploiement via MSI d’un complément Office 2007

Avec la version 3.0 des Visual Studio Tools for Office (VSTO), le déploiement d’un complément Microsoft Office se fait par défaut en Click-Once ( http://msdn.microsoft.com/en-us/library/bb821233.aspx ). Néanmoins, si jamais pour une raison ou une autre, vous ne pouvez pas utiliser la technologie Click-Once, il vous est toujours possible de déployer via un MSI comme cela est expliqué ici : http://msdn.microsoft.com/en-us/library/cc563937.aspx et ici pour les points un peu plus « advanced » : http://msdn.microsoft.com/en-us/library/cc616991.aspx .

Si l’on suit pas à pas cet article, le résultat de notre projet de déploiement est composé de trois éléments : un MSI installant notre complément, un setup.exe vérifiant les pré-requis, et enfin un répertoire contenant les Primary Interop Assemblies (PIAs) qui sont un ensemble de dll permettant d’interopérer avec la suite Microsoft Office 2007 depuis des assemblies .Net. L’inconvénient est que contrairement aux autres pré-requis tel que la VSTO Runtime, ou le Framework 3.5, l’on n’a pas le droit (légalement parlant) de lancer l’installation depuis le site de Microsoft car dans ce cas cela saute l’écran où l’utilisateur accepte les conditions d’utilisations de ces PIAs. L’on se retrouve donc avec un installeur en trois parties.

En phase de développement, il est donc commun que l’on fasse une archive zip de ces trois éléments et il arrive toujours le moment où un client lance uniquement le MSI alors que des pré-requis sont manquants ou bien qu’il lance le setup sans avoir dé-zippé l’archive, ce qui cause premièrement l’échec de l’installation car le setup.exe ne trouve pas les autres éléments, et deuxièmement un appel et une perte de temps pour corriger le problème. En conséquence il est donc dans l’intérêt de tous, de se faire une installation entièrement automatisée le plus rapidement possible pour éviter ces loupés.

Un moyen commun sur la plateforme Windows est d’utiliser IExpress. IExpress est un wizard qui permet de créer des fichiers auto-extractibles qui peuvent lancer une commande automatiquement à la fin de l’extraction (http://www.microsoft.com/technet/prodtechnol/ie/ieak/techinfo/deploy/60/en/iexpress.mspx?mfr=true). Malheureusement pour nous, IExpress dans ce cas s’avère peu pratique car il ne peut contenir que des fichiers et pas de répertoires comme il nous le faut à cause de nos PIAs.

Pour résoudre ce problème j’ai récemment trouvé un petit outil gratuit qui se révèle être très pratique : Zip 2 Secure Exe de l’éditeur Chilkat (http://www.chilkatsoft.com/chilkatSfx.asp). Cet outil prend en entrée une archive zip, et permet de lancer un setup.exe après avoir extrait l’archive. De plus, il est possible d’utiliser l’outil en ligne de commande pour automatiser tout cela dans un post-build event ou un batch par exemple. Bref, la solution idéale dans ce cas, puisque sans modifier l’existant et donc tout en restant dans les chemins documentés par Microsoft, le développeur peut ne livrer qu’un seul fichier, et être ainsi assuré que l’installation de son complément Office a été correctement lancée.

 

++,

Carl

Développement d'applications .Net par composants

Bonjour, je profite de mon blog pour relayer un article écrit par Catalin Manoliu (un autre SoftFluentee Smile ) qui présente comment développer des applications .Net architecturées en composants.

Cet article est doublement intéressant car d'une part, il donne une bonne overview des principes de développement par composants, et d'autre part, il offre une bonne présentation des possibilités et de l'intérêt de l'outil de développement CodeFluent.

L'article est disponible ici : http://www.softfluent.com/article/developper-des-applications-net-architecturees-en-composants.aspx

Autre point d'intérêt : Lancement de CodeFluent 2009

Pour en savoir plus sur le développement par composants avec la fabrique logicielle CodeFluent, je vous invite à visiter le site http://www.codefluent.com/ et à venir assister à la session dédiée à CodeFluent qui aura lieu lors de l'évènement Microsoft Techdays à Paris le 10 février prochain à 14h30.

Sources du Framework 3.5 SP1

Bonjour à tous, un petit poste rapide qui peut s’avérer très utile : les sources du Framework 3.5 SP1 sont disponibles !

La liste des assemblies disponibles est ici : http://blogs.msdn.com/rscc/archive/2008/10/22/wpf-sources-for-net-framework-3-5-sp1-are-available.aspx

De plus, il est possible de configurer son Visual Studio 2008 pour pouvoir remonter la pile d’appels jusque dans le Framework, et donc de débugger et de voir ce qu’il s’y passe J

Voici le lien pour configurer son Visual Studio comme il faut : http://referencesource.microsoft.com/serversetup.aspx

N.B. : les symboles seront téléchargés lors du premier debuggage donc cela prendra quelques minutes. Ces symboles seront ensuite cachés sur votre disque, et les sessions de debugging suivantes seront comme d’habitudes.

Enjoy,

Carl

Etendre le WindowsFormsHost - Deuxième partie

J’ai fais quelques modifications à la classe décrite dans le premier post pour rendre le contrôle plus stable.

1.       Ne pas utiliser la fonction « GotFocus() » qui retournait un handle sur le contrôle ayant le focus :

En effet, bien que dans 90% des cas ce soit le contrôle hosté qui ait le focus,  dans certains cas cela renvoyait le handle du WindowsFormsHost ce qui causait un StackOverFlow. La bonne pratique est de faire ceci :

private static void KeyDownHandler(object sender, System.Windows.Input.KeyEventArgs e)

{

WindowsFormsHost host = (WindowsFormsHost)e.OriginalSource;

      Control hostedGrid = host.Child as Control;

      if (hostedGrid == null)

            return;

(...)}

“e.OriginalSource“ correspond au contrôle source qui a levé l’évènement, avant que n’importe quel RoutedEventArg.Source n'ait pu être modifié. Dans notre cas cela retournera donc toujours une instance de notre « WindowsFromsHost » et il n’y a plus cas récupérer son fils pour avoir notre contrôle WinForms.

2.       Pour gérer les touches avec modificateurs (ex : Shift+Tab), préférer l’évènement OnKeyUp à OnKeyDown :

En effet, dans le cas du Shift+Tab, la touche shift peut être enfoncée et lever N évènements ce qui rend la gestion complexe. En revanche sur le OnKeyUp du tab, il suffit de regarder si la touche a un modificateur (traduit de l’anglais « Modifier »). Enfin pour transmettre un modificateur et une touche au contrôle hosté, il faut envoyer deux messages : le modificateur puis la touche.

3.       Spy++ est ton ami :

Visual Studio installe un outil qui m'a été bien pratique dans ce dev : Spy++. Spy++ permet d’observer les messages Windows que reçoit une fenêtre, ça en fait donc l’outil idéal pour savoir ce qu’il faut rediriger et quelle est la séquence correcte à envoyer J

Etendre le WindowsFormsHost

Le contrôle WindowsFormsHost dans le namespace System.Windows.Forms.Integration permet d’ « hoster » des contrôles .Net 2.0 dans des écrans WPF.

Le contrôle est très serviable surtout au vu du manque de certain contrôles (grilles, grilles pivot, property grid, etc.) dans les contrôles WPF et qui sont pourtant courant dans les applications d’aujourd’hui.

Récemment dans un projet WPF, nous avions plusieurs écrans contenant des grilles pivots dans lesquelles un utilisateur pouvait saisir une grosse quantité de données. L’utilisation d’une grille .Net 2.0 dans un WindowsFormHost était donc incontournable ; malheureusement avec le comportement par défaut du WindowsFormsHost, le déplacement par les flèches dans cette grille n’était pas possible car si le WindowsFormsHost est dans un écran au milieu de deux autres contrôles, la flèche donne le focus au suivant (source : http://msdn.microsoft.com/en-us/library/ms751797.aspx).

Il nous était donc nécessaire de changer ce comportement pour que lorsque l’utilisateur appui sur une flèche, celle-ci se voit gérée par la grille elle-même ; de manière à ce que l’utilisateur puisse se déplacer, normalement, via le clavier.

La première étape est de surcharger le WindowsFormsHost du Framework par le notre, qui va étendre le premier en permettant de rediriger vers le contrôle contenu les touches désirées.

Dans le constructeur statique de notre classe, nous allons donc nous abonner à l’évènement « KeyDownEvent » ce qui fait que nous serons appelés à chaque fois qu’une touche du clavier sera enfoncée. Ainsi nous pourrons rediriger cette touche vers notre contrôle fils. Pour simplifier le tout, c’est aussi dans le constructeur que nous allons créer en dur une liste des touches que nous comptons gérer (l’idéal serait que cela soit configurable ou au moins définissable dans le XAML) :

 private static List<Key> _keysToRoute;

 

        static WindowsFormsHost()

        {

            _keysToRoute = new List<Key>();

            _keysToRoute.Add(Key.Up);

            _keysToRoute.Add(Key.Down);

            _keysToRoute.Add(Key.Right);

            _keysToRoute.Add(Key.Left);

 

            EventManager.RegisterClassHandler(typeof(WindowsFormsHost),

Keyboard.KeyDownEvent,

new System.Windows.Input.KeyEventHandler(WindowsFormsHost.KeyDownHandler),

true);

        }

WPF introduit une nouvelle notion au niveau des évènements : la possibilité de les « router ». Les contrôles WPF n’utilisent plus des évènements de type « Event » mais de type « RoutedEvent ». Ces nouveaux types d’évènements introduisent de nouvelles notions dont parmi elles la notion de stratégie.

Chaque « RoutedEvent » a une propriété « RoutingStrategy » qui indique la stratégie de propagation de celui-ci, celle-ci est forcément l’une de ces trois :

1.       « Bubbling » : l’évènement va de bas en haut de l’arbre WPF,

2.       « Direct » : comportement analogue à celui des contrôles Windows Forms,

3.       « Tunneling » : l’évènement va de haut en bas de l’arbre WPF.

Dans notre cas, nous voulons que l’évènement reste au niveau de la grille et ne remonte pas dans les contrôles supérieurs. C’est donc pour cela que nous nous abonnons à l’évènement « KeyDownEvent » qui a une stratégie « bubbling » et non pas à un autre évènement comme le « PreviewKeyDownEvent » par exemple qui lui a une stratégie « tunneling ». Pour en savoir plus sur les « Routed Events » voici la page MSDN : http://msdn.microsoft.com/en-us/library/ms742806.aspx.

Notre WindowsFormsHost est maintenant appelé à chaque touche enfoncée, il ne nous reste plus qu’à rediriger cet évènement à notre contrôle .Net 2.0, et à s’assurer que l’évènement reste à ce niveau. La suite se décompose ainsi en 4 étapes :

1.       Récupérer un handle du contrôle hosté,

2.       Convertir la touche WPF en touche Windows Forms,

3.       Rediriger le message vers le contrôle hosté,

4.       S’assurer que l’évènement ne remontera pas plus haut dans l’arbre WPF.

Voici la méthode implémentant ces étapes :

       private static void KeyDownHandler(object sender, System.Windows.Input.KeyEventArgs e)

        {

            if (!_keysToRoute.Contains(e.Key))

                return;

 

            // The return value is the handle to the window with the keyboard focus (which should will always be the hosted control).

            // If the calling thread's message queue does not have an associated window with the keyboard focus, the return value is NULL.

            IntPtr focusedHandle = GetFocus();

            if (focusedHandle == null)

                return;

 

            // N.B.: we can't use the "this.Child.Handle" since we're in a static method

 

            Keys winformKey = ConvertKeyToWinFormKey(e.Key);

            IntPtr wParam = new IntPtr((int)winformKey);

            SendMessage(focusedHandle, (int)WindowsMessage.WM_KeyDown, wParam, IntPtr.Zero);

 

            // Mark event as handled so it doesn't bubble up any further in the WPF tree.

            e.Handled = true;

        }

Quelques points d’intérêts concernant la méthode ci-dessus :

·         il faut bien faire attention lorsque l’on fait de l’interopérabilité à la compatibilité des types C#/C++ de manière à ce que le code soit fonctionnel sur architecture 32 bits et 64 bits (préférer la structure  IntPtr à un long, etc.).

·         dans cet exemple nous utilisons la fonction GetFocus() pour récupérer un handle sur le contrôle hosté car nous sommes dans une méthode statique, néanmoins la bonne pratique est de passer par la propriété « Handle » qui est sur la classe « Control ».

Je mettrais d'ici peu la classe complète en attaché à ce post.


Les 10 derniers blogs postés

- TechDays Paris 2010 : Déploiement de nouvelles technologies – Retour d’expérience par l’informatique de Microsoft par Blog Technique de Romelard Fabrice le il y a 23 minutes

- TechDays Paris 2010 : Plan de migration vers SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 4 heures et 6 minutes

- TechDays Paris 2010 : La pleinière du second jour par Blog Technique de Romelard Fabrice le il y a 5 heures et 11 minutes

- Visual Studio 2010 and .NET Framework 4 Release Candidate now available par Matthieu MEZIL le il y a 8 heures et 17 minutes

- Création d’une base de donnée sous SQL Azure par Le Blog (Vert) d'Arnaud JUND le il y a 9 heures et 13 minutes

- TechDays Paris 2010 : Les Services d’applications dans SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 19 heures et 13 minutes

- TechDays Paris 2010 : La GED et SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 23 heures et 11 minutes

- TechDays Paris 2010 : SharePoint 2010 et Les réseaux sociaux par Blog Technique de Romelard Fabrice le 02-08-2010, 15:40

- TechDays Paris 2010 : SharePoint 2010 – Description et nouveautés par Blog Technique de Romelard Fabrice le 02-08-2010, 14:33

- TechDays Paris 2010 : Pleinière Lundi par Blog Technique de Romelard Fabrice le 02-08-2010, 14:30