Powershell – Créer de petites applications graphiques (GUI) avec WPF

I. Introduction

Pour faire suite à mes derniers articles, je vous propose de créer une petite application graphique sous Powershell, en exploitant cette fois-ci le WPF (Windows Presentation Foundation) plutôt que le traditionnel Windows Forms. (Mais l’un n’empêche pas l’autre 😀 )

II. Le designer – Visual Studio

Pour dessiner notre interface graphique, il nous faut un “designer” et pour cela, le produit gratuit “Visual Studio Community” (Anciennement Express) fera parfaitement l’affaire. Il faudra toutefois télécharger et installer l’ensemble du produit qui près plus d’1 Go.

Une fois Visual Studio installé et lancé, ouvrez un nouveau projet puis choisissez “Wpf Application” dans la liste des modèles.

Nouvelle application WPF sous Visual Studio

L’écran du designer est composé de 4 zones principales

  • A gauche : La boite à outil – Contient les contrôles (objets graphiques) que vous pouvez déposer sur la forme
  • Au centre haut : Le design – Zone de dessin et rendu de la forme elle-même
  • Au centre bas : Le code XAML – Dynamiquement généré en fonction du design
  • A droite : Les propriétés des objets graphiques sélectionnés dans le designer

Aperçu du designer et du code XAML

Dessinez votre interface comme bon vous semble, en sélectionnant les contrôles de la boite à outil qu’il suffit de déposer dans la zone dessin.

Notez qu’il est également possible d’agir dans la zone de code XAML, voire d’y copier l’intégralité d’une forme.

III. Exploiter ce code XAML avec Powershell

Ce fameux code XAML généré par Visual Studio doit toutefois être légèrement adapté avant d’être exploité sous Powershell.

Prenons le code suivant, sur lequel sont surlignées les instructions qui posent problème:

Il est bien sur possible de supprimer manuellement ces directives Windows…  ,  mc:Ignorable…, x:Name, mais il faudra réitérer ces actions à chaque nouveau élément graphique – Il sera donc plus élégant d’adapter notre code Powershell à ces contraintes.

 

A. Insertion du bloc XAML issu de Visual Studio

La déclaration du bloc XAML peut se faire au travers la déclaration d’une variable de chaine stricte “Here-String”  ainsi :

 $inputXML = @"

<Window x:Name="FrmAppLauncher" x:Class="AppLauncher.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        xmlns:local="clr-namespace:AppLauncher"

        mc:Ignorable="d"

        Title="AppLauncher" Height="288.623" Width="431.337" ResizeMode="NoResize">

    <Grid Margin="0,0,-2,-2">

        <Label Content="Powershell GUI App Launcher" HorizontalAlignment="Left" Margin="64,11,0,0" VerticalAlignment="Top" Height="35" Width="289" FontSize="20" FontWeight="Bold"/>

        <Button  x:Name="BtnApp1" Content="" HorizontalAlignment="Left" Margin="21,61,0,0" VerticalAlignment="Top" Width="83" Height="80"/>

        <Button  x:Name="BtnApp2" Content="" HorizontalAlignment="Left" Margin="122,61,0,0" VerticalAlignment="Top" Width="82" Height="80"/>

        <Button  x:Name="BtnApp3" Content="" HorizontalAlignment="Left" Margin="222,61,0,0" VerticalAlignment="Top" Width="83" Height="80"/>

        <Button  x:Name="BtnApp4" Content="" HorizontalAlignment="Left" Margin="323,61,0,0" VerticalAlignment="Top" Width="83" Height="80"/>

        <Button  x:Name="BtnApp5" Content="" HorizontalAlignment="Left" Margin="22,158,0,0" VerticalAlignment="Top" Width="82" Height="80"/>

        <Button  x:Name="BtnApp6" Content="" HorizontalAlignment="Left" Margin="122,159,0,0" VerticalAlignment="Top" Width="83" Height="80"/>

        <Button  x:Name="BtnApp7" Content="" HorizontalAlignment="Left" Margin="222,158,0,0" VerticalAlignment="Top" Width="83" Height="80"/>

        <Button  x:Name="BtnQuit" Content="Quit" HorizontalAlignment="Left" Margin="323,158,0,0" VerticalAlignment="Top" Width="83" Height="80"/>

    </Grid>

</Window>

"@

 

Note : Pour le code XAML, il y a plusieurs “écoles”. Le stockage au sein d’un fichier qu’il suffit de charger dans le script via une commande “$xaml = Get-Content“, ou directement dans le script via un chaine de texte stricte dite “Here-String“. A l’instar des chaine de texte classiques, on peut soit utiliser les guillemets soit les simples quottes. Les guillemets ont ma préférence car ils permettent d’y insérer dynamiquement des variables $…

Pour rappel, les balises d’une here-string doit impérativement être en fin de ligne pour la première et sur une nouvelle ligne pour la seconde Par exemple, l’écriture suivante n’est pas valide..

$Code = @'# --- exemple de texte strict ---# '@

Il faut déclarer la variable comme suit :

$Code = @'

# --- exemple de texte strict ---#

'@

 

B. Adaptation du code XAML

Ensuite on ajuste quelques éléments apportés par Visual Studio afin de rendre cette structure XAML compatible avec Powershell

$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N'  -replace '^<Win.*', '<Window'

 

C. Chargement des assemblies

Pour exploiter ce code, il est nécessaire de charger les classes WPF

[void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')

 

Ou

try { Add-Type -AssemblyName PresentationFramework}

      catch { Write-Warning "Erreur lors du chargement de l'assembly WPF - Fin du script.";

 Exit $LASTEXITCODE}

 

Sans oublier d’assigner une nouvelle variable de type XML à partir de la chaine précédemment modifiée

[xml]$XAML = $inputXML

 

D. Interpréter le code XAML

Ensuite, il est nécessaire d’interpréter le code XAML, via la commande suivante :

$reader=(New-Object System.Xml.XmlNodeReader $xaml)

 

Il est de bon ton d’ajouter un traitement d’erreur afin d’intercepter d’éventuelles erreurs d’interprétation ou de chargement du code de l’interface.

Try { $Window =[Windows.Markup.XamlReader]::Load( $reader ) }

  Catch { Write-Host "Erreur durant le chargement du code XAML."

          Write-Host $_

    }

 

E. Les variables d’objets graphiques

A ce stade, il est conseillé de déclarer des variables pour chaque objet de l’interface graphique, à partir de son nom, et vous pourrez ensuite les exploiter simplement au sein du script.

$xaml.SelectNodes("//*[@Name]") | % { Set-Variable -Name ($_.Name) -Value $Window.FindName($_.Name)}

 

Facultatif : Pour identifier plus facilement ces variables propres à l’interface graphique, il peut être intéressant de les préfixer en modifiant la ligne précédente comme suit :

$xaml.SelectNodes("//*[@Name]") | % { Set-Variable -Name "GUI$($_.Name)" -Value $Window.FindName($_.Name) }

 

Et les énumérer, le cas échéant

Get-Variable GUI*

 

Voilà pour la partie interface, mais encore faut-il y adjoindre quelques actions 🙂

F. Ajout d’événements(Actions)

Il existe plusieurs techniques pour ajouter des événements sur des contrôles. Le plus direct, si le code n’est pas trop important, consiste à déclarer le code à exécuter entre accolades { …. } , et positionner celui-ci entre les parenthèses de la méthode déclarée .add_Click(…), comme suit :

$GUIbtnQuit.add_Click({

   $Window.Close()

})

 

Si le code est plus conséquent, il peut être judicieux de déclarer préalablement une fonction, peut d’appeler cette dernière dans le bloc de code de l’événement.

Dans cet exemple, les autres boutons n’ont pas de label. Il suffit donc d’en ajouter à notre convenance comme suit  :

$GUIBtnApp1.Content = "Regedit"

$GUIBtnApp1.add_Click({

   & regedit

})

 

A vous d’imaginer la suite 🙂

G. Afficher l’interface GUI

Et pour finir, il faudra afficher cette jolie interface via l’instruction suivante :

$Window.ShowDialog() | out-null

 

Voilà, c’est tout pour cette fois. J’espère que ce petit article vous aidera dans vos premiers essais d’interface.

Ah, j’oubliais, normalement cette interface (sans prétention) ressemble à ça :

Exemple WPF

Merci pour votre lecture et bonne continuation.

Christophe

1 Commentaire

  1. Pingback: #LinksOfTheWeek n°9 : PowerToys, danger avec la KB4515384, Netatmo, etc. | | #LinksOfTheWeek | IT-Connect

L'ajoût de commentaire est désormais suspendu.