PowerShell WPF GUI - Voraussetzungen und erste GUI Anwendung

Eine sehr einfache Variante um mit PowerShell eine GUI zu erstellen bietet WPF-XAML. Das Layout kann dabei ähnlich einer HTML-Datei erstellt werden. Das kostenlose Visual Studio Express bietet zudem die Möglichkeit, das Layout einfach in einem grafischen Editor zu erstellen. Der eigentliche Programmcode wird mit PowerShell umgesetzt. 

Grafischer Editor: Visual Studio Express for Windows Desktop

Installation, siehe: Visual Studio Express Installation

F√ľr die PowerShell GUI w√§hle ich "WPF Application"

Einfaches Beispiel: Text in das Fenster

Mit Hilfe der Toolbox k√∂nnen einzelne Fensterobjekte in die GUI¬†gezogen werden, hier zum Beispiel "Label" f√ľr einen Text.

Im unteren Bereich von Visual Studio wird der f√ľr PowerShell ben√∂tigte XAML-Code erzeugt:

<Window x:Class="WpfApplication.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:WpfApplication"
mc:Ignorable="d"
Title="MainWindow" Height="768" Width="1024">
    <Label x:Name="label" Content="Text in das Fenster" HorizontalAlignment="Left" Height="24" Margin="20,29,0,0" VerticalAlignment="Top" Width="212"/>
</Window>

Der generierte XAML-Code kann zum Bearbeiten auch jederzeit von einem PowerShell Skript zur√ľck in Visual-Studio kopiert werden.

XAML definiert das Aussehen unserer Anwendung, kombiniert mit folgendem PowerShell-Code kann diese gestartet werden:

#XAML Code kann zwischen @" und "@ ersetzt werden:
[xml]$XAML = @"
<Window x:Class="WpfApplication.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:WpfApplication"
mc:Ignorable="d"
Title="MainWindow" Height="768" Width="1024">
<Label x:Name="label" Content="Text in das Fenster" HorizontalAlignment="Left" Height="24" Margin="20,29,0,0" VerticalAlignment="Top" Width="212"/>
</Window>
"@ -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' #-replace wird benötigt, wenn XAML aus Visual Studio kopiert wird.
#XAML laden
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
try{
   $Form=[Windows.Markup.XamlReader]::Load( (New-Object System.Xml.XmlNodeReader $XAML) )
} catch {
   Write-Host "Windows.Markup.XamlReader konnte nicht geladen werden. M√∂gliche Ursache: ung√ľltige Syntax oder fehlendes .net"
}
#Fenster anzeigen:
$Form.ShowDialog()

Ich habe den Quellcode dazu in den Powershell ISE-Editor eingef√ľgt:

Mit "F5" startet das Fenster: 

 

PowerShell XAML Variablen auslesen und manipulieren

Wir bleiben bei diesem Beispiel und versuchen den Text im Fenster zu ändern.

Mit folgendem Quellcode können die Fensterobjekte angezeigt werden:

$xaml.SelectNodes("//*[@Name]") | ft

Um den Fenstertext aus PowerShell zu √§ndern, m√ľssen wir¬†das Fensterobjekt laden und die Eigenschaft "Content" √§ndern:

$Form.FindName("label").Content= "hier ein neuer Text f√ľr das Fenster"

 

Damit Variablen in Zukunft nicht √ľber $Form.FindName aufgerufen werden m√ľssen, k√∂nnen alle Fensterobjekte als Variable geladen werden:

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

Unser Text kann jetzt auch mit $label.Content= "hier ein neuer Text f√ľr das Fenster"¬†angesprochen und somit¬†ge√§ndert¬†werden.

Dynamisches Ansprechen der Objekte

In folgendem Beispiel wollte ich alle Textboxen aus XAML auslesen und den enthaltenen Text ändern.

Werden f√ľr eine Textbox immer Namen, beginnend mit zum Beispiel "txt" verwendet, k√∂nnen diese in einer ForEach-Schleife aufgerufen und ge√§ndert werden. In diesem Beispiel wird in allen vorhandenen¬†Textboxen der Name der Textbox geschrieben:

ForEach( $textbox in (get-variable txt*) ) { $Form.FindName("$($textbox.name)").Text= $($textbox.name) }

GUI-Variable und zugehörige Datenbank-Spalte?

Durch die M√∂glichkeit die GUI-Objekte dynamisch anzusprechen, k√∂nnte ein GUI erstellt werden, in der die Textboxen gleich den Datenbank-Spalten benannt werden. Im PowerShell-Code m√ľsste der Name der ben√∂tigten Spalten dann nicht zus√§tzlich¬†erw√§hnt werden. Wenn wir noch einen Schritt weiter denken, k√∂nnten sogar die GUI-Elemente dynamisch erzeugt werden:

Nachtr√§gliches Hinzuf√ľgen von GUI-Elementen

Mit folgendem Beispiel werden Buttons im Nachhinein zur GUI hinzugef√ľgt:

#XAML Code kann zwischen @" und "@ ersetzt werden:
[xml]$XAML = @"
<Window x:Class="WpfApplication.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:WpfApplication"
mc:Ignorable="d"
Title="MainWindow" Height="768" Width="1024">
        <StackPanel x:Name="StackPanel" Margin = "50,50,50,50">                                          
        </StackPanel>
</Window>
"@ -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' #-replace wird benötigt, wenn XAML aus Visual Studio kopiert wird.
#XAML laden
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
try{$Form=[Windows.Markup.XamlReader]::Load( (New-Object System.Xml.XmlNodeReader $XAML) )}
catch{Write-Host "Windows.Markup.XamlReader konnte nicht geladen werden. M√∂gliche Ursache: ung√ľltige Syntax oder fehlendes .net"}
$StackPanel = $Form.FindName("StackPanel")

function add_button($strLabel) {
        $objButton = New-Object System.Windows.Controls.Button
        $objButton.Content = $strLabel
        $objButton.Background = 'Blue'
        $objButton.Foreground = 'White'
        $objButton.Name = $strLabel
        $objButton.Add_Click({
                write-host "$($this.content) pressed"
        }) 
        #insert it into the StackPanel
        $StackPanel.Children.Insert(($StackPanel.Children.count),$objButton)   

} 

#Aufruf der Funktion:
add_button "TextNewBTN"
add_button "TextNewBTN2"
add_button "TextNewBTN3"
#Fenster anzeigen:
$Form.ShowDialog()

siehe auch: PowerShell WPF GUI Auswahl - f√ľr PowerShell-Parameter¬†

Timer: Update des Fensters im Hintergrund

Mit folgendem Script-Block kann ein Timer f√ľr das regelm√§ssige Update des Fenster gestartet werden:

$timer = new-object System.Windows.Threading.DispatcherTimer
$timer.Interval = [TimeSpan]"0:0:5.00"
$timer.Add_Tick($starttick)
$timer.Start()

 

In der Variable $starttick kann der eigentliche Task (Tick) hinterlegt werden. $timer definiert und startet den eigentlichen Timer. Mit $timer.Interval wird die Zeitspanne zwischen den Ticks, hier 5 Sekunden, festgelegt.

In folgendem Beispiel erhöhe ich in dem Tick eine Variable $i in jedem Durchgang und schreibe das Ergebnis, anstelle des vorhandenen Textes, in das Fenster:

$starttick={
   $Form.FindName("label").Content= $script:i++
}

Auch hier wird $script:i anstelle von $i verwendet, da $i in einem anderen Scope ausgef√ľhrt wird, siehe:¬†Powershell-GUI#scope-G√ľltigkeitsbereich

 

 

 

 

 

Fortsetzung zu diesem Artikel:  PowerShell wpf gui tabControl oder mehrere Oberflächen 

positive Bewertung({{pro_count}})
Beitrag bewerten:
{{percentage}} % positiv
negative Bewertung({{con_count}})

DANKE f√ľr deine Bewertung!


Fragen / Kommentare


(sortiert nach Bewertung / Datum) [alle Kommentare(neueste zuerst)]

‚úćTK1987
07.10.2021 13:18 , geändert 07.10.2021 13:19
Hallo zusammen.

Beim replace der XAML f√ľhrt "x:N","N" u.U. zu Fehlern, da dieser z. B. auch auf "x:Null" zutrifft - wo das "x:" jedoch zwingend davor stehen bleiben muss.

F√ľr welchen Fall soll dieser √ľberhaupt notwendig sein? Bei meinen Tests konnte ich diesen auch einfach weglassen und die XAML wurde problemlos geladen.
Sollte es doch iregendeinen Fall geben, so sollte der replace oben aber zumindestens in "x:N(?!ull)","N" abgeändert werden, um "x:Null" davon auszunehmen.

Gruß Thomas

‚úćanonym
07.10.2016 05:33
User: Lauch 
Danke, funktioniert super!