Windows-Anwendungen mit PowerShell und Windows Presentation Framework (WPF) – Teil 4: Dynamisches Erstellen von Steuerelementen

Bisher sehen viele Anwender und speziell Administratoren die PowerShell als ein Werkzeug, um einzelne Befehle oder kleinere Skripte auszuführen. Wer kleinere oder größere Skripte erstellt, hat entweder die Möglichkeit Parameter fest im Programm zu “verdrahten” oder diese als reale Parameter (in einen Parameterblock) von außen an das Skript zu übergeben. Bei der festen Verdrahtung (Parameter als Konstanten im Programm) ist bei jeder abweichenden Einstellung ein Anpassen des Skripts erforderlich. Dies ist besonders dann schwierig, wenn PowerShell-Skripte signiert sein müssen, bevor diese ausgeführt werden können. Einfacher ist es deshalb, die Parametereingabe von außen zu steuern. Dies macht es möglich Skripte in verschiedenen Konfigurationen und auch durch in PowerShell unerfahrene Anwender auszuführen. In einem solchen Fall bietet sich beispielsweise der Aufruf des PowerShell-Skript über eine Kommando-Datei an (in der die Parameter bereits vordefiniert sind):

powershell -executionpolicy bypass -file „meinskript.ps1“ -server „svr01“
–instance „MSSQLSERVER“ –query „Select * from sys.databases“

Doch die PowerShell bietet durch die enge Anbindung an das .NET Framework viel mehr Möglichkeiten. Eine dieser Möglichkeiten besteht darin, das PowerShell-Skript mit einer eigenen Bedienoberfläche (User Interface) auszustatten. Windows bietet dafür prinzipiell zwei Wege:

  • Windows Forms-Anwendung
    Anleitungen zu diesem Thema gibt es sehr viele im Internet.
  • Formulare basierend auf dem Windows Presentation Framework
    bereits 2008 durch das PowerShell-Team als Blog-Artikel veröffentlicht und eine Bibliothek zur Vereinfachung der Benutzung ist beispielsweise das ShowUI-Projekt
Unterschiede WPF vs. Windows Forms

WPF ist ein grafisches Subsystem zum Rendern von Benutzeroberflächen in Windows-basierten Anwendungen. Es handelt sich dabei um eine Auszeichnungssprache (markup language), die Elemente und deren Verknüpfungen innerhalb einer Benutzeroberfläche definiert.

Windows Forms ist für ereignisgesteuerte Anwendungen auf Basis des Microsoft .NET Framework. Diese Technologie ist eine grafische API, die einen Zugriff auf native Windows Benutzeroberflächenelemente bereitstellt.

Quelle: http://www.differencebetween.net/technology/difference-between-wpf-and-windows-forms/

Beide Wege haben Vor- und Nachteile! Aufgrund der Flexibilität setzen sich in den letzten Jahren verstärkt Anwendungen auf Basis des Windows Presentation Framework durch. Deshalb möchte ich Ihnen in dieser Artikelserie zeigen, wie Sie dieses Framework auch von PowerShell aus direkt ansprechen und verwenden können. Mein Ziel ist Ihnen dies schrittweise zu erklären, damit Sie dies leicht auf eigene Aufgabenstellung adaptieren können.

Das soll natürlich nicht heißen, wenn Sie sich auf eine Technologie festgelegt haben, dass Sie die andere nicht auch innerhalb einer Anwendung nutzen können. Oft stehen Sie vor dem Problem, das beispielsweise bestimmte Steuerelemente oder Funktionen in einer Technologie nicht zur Verfügung stehen – in der anderen jedoch schon. Dann können Sie sich entweder dafür entscheiden, diese aufwendig nachzuprogrammieren oder die Technologien zu mischen. Ein Beispiel dafür ist der fehlende FolderBrowserDialog in WPF.

Im Rahmen dieser Artikelserie sind folgende Teile geplant bzw. wurden bereits veröffentlicht:

Wie bereits im Teil 1 der Artikelserie beschrieben, geht es mir nicht darum, dieses Thema abstrakt zu behandeln, sondern ich habe mir eine konkrete Aufgabenstellung dafür gesucht (siehe Abschnitt “Aufgabenstellung” im ersten Teil). In den vorangegangenen Blog-Artikeln habe ich die Anwendung jeweils basierend auf einer XAML-Struktur erstellt.

Nachdem Sie am Ende des dritten Teils dazu in der Lage waren, die Windows Anwendung mit den erforderlichen Steuerelementen zu gestalten und Funktionalität/Interaktivität zu zuweisen, möchte ich Ihnen in diesem Teil zeigen, wie Sie:

  • Oberfläche mit PowerShell auf Basis von WPF ohne XAML erstellen
  • Neue Steuerelemente einer laufenden Anwendung zuweisen
  • Neue XAML-Strukturen einer laufenden Anwendung zuweisen
  • Funktionalität über Ereignisse (Events) von Objekten zuweisen
  • WPF-Anwendung (PowerShell) in einer eigenen PowerShell-Umgebung starten

Da es somit wieder um die Grundlagen geht, verwende ich als Ausgangsbasis für diesen Blogeintrag keinen vorher verwendeten Programmcode, sondern fange noch einmal ganz vor vorn an.

Oberfläche mit PowerShell auf Basis von WPF ohne XAML

Das Erstellen einer neuen Windowsanwendung auf Basis des Windows Presentation Framework (WPF) ohne dabei eine XAML-Struktur als Ausgangsbasis zu verwenden, macht für einfache Anwendungen definitiv Sinn. Als Demonstrationsbeispiel habe ich mir dafür eine Anwendung mit einem Browser-Fenster herausgesucht – oder wie es zu Neudeutsch heißt eine Hybridanwendung (Kombination einer Windows-Anwendung, die Webinhalte darstellt). Das Ergebnis soll dann so aussehen:

image

Zur technischen Umsetzung müssen Sie auch hier, wie bereits in den vorangegangenen Teilen der Serie beschrieben, das Assembly PresentationFramework als Type der PowerShell-Umgebung hinzugefügt werden.

Add-Type -Assembly PresentationFramework

In vorherigen Artikeln wurde danach eine XAML-Struktur zur Beschreibung des Windows-Fensters mit allen Steuerelementen geladen. Nachdem ich aber in diesem Artikel ohne eine solche Struktur arbeiten möchte, wird in diesem Beispiel direkt ein neues Windows-Objekt instanziiert. Wenn Sie jetzt nach dieser Zeile $windows.ShowDialog() aufrufen, wird sofort eine Windows Anwendung (ohne Inhalt) gestartet.

$window = New-Object System.Windows.Window            
$window.ShowDialog()

image

Da ein Fenster, so ganz ohne etwas, sehr leer aussieht, können Sie natürlich auch auf diesem Weg, das Fenster mit Eigenschaften weiter gestalten. Um es einfach zu halten, verwende ich für dieses Beispiel nur zwei Eigenschaften (Title und WindowStartupLocation). Zum Festlegen der Eigenschaften gibt es zwei Möglichkeiten, die auch in einer gemischten Form verwendet werden können.

Der erste Weg besteht darin, die Eigenschaften bei der Instanziierung eines Objektes direkt zuzuweisen. Dazu definieren Sie vorab eine Liste (HashTable) der entsprechenden Eigenschaften des Objekts mit den dazugehörenden Werten:

$p = @{Title = 'PowerShell';WindowStartupLocation = 'CenterScreen'}            
[System.Windows.Window]$window = New-Object System.Windows.Window -Property $p

Der zweite Weg ist, wie bereits in vorhergehenden Blog-Artikeln beschrieben, jeder einzelnen Eigenschaft des Objekts den Wert zuzuweisen:

$window.Title = 'PowerShell Test program'            
$window.WindowStartupLocation = 'CenterScreen'

Meiner persönlichen Meinung nach ist der zweite Weg besser zu lesen und auch üblicher in der Programmierung. Jedoch hat auch der erste Weg seinen Charm, wenn einem Objekt sehr viele Eigenschaften gleichzeitig zugewiesen werden sollen.

Neben den genannten Eigenschaften stehen noch viele weitere zur Verfügung, mit denen Sie das Aussehen und das Verhalten des Fensters steuern können. Eine Übersicht der Eigenschaften für diesen Objekttyp hat Microsoft in Books Online bereitgestellt (inkl. einer ausführlichen Beschreibung und vielen Beispielen). Wenn Sie nur eine bestimmte Eigenschaft suchen bzw. die Parameter einer Methode wissen wollen, reicht auch folgender Befehl aus:

New-Object System.Windows.Window | get-member

Zusätzlich liefert Ihnen Intellisense in der PowerShell ISE auch wertvolle Unterstützung bei der Suche nach Eigenschaften, Methoden oder Ereignissen eines Objekts:

image

Dies funktioniert nicht nur bei diesem, sondern auch bei allen anderen Objekten innerhalb eines PowerShell-Skripts.

Neue Steuerelemente einer laufenden Anwendung zuweisen

Wie in dem Blog-Artikel 1 dieser Serie beschrieben, sollten Sie nun ein Container-Objekt in das Fenster einfügen. Als Container-Objekt verwende ich für dieses Beispiel ein Grid-Steuerelement, das sich ganz einfach darüber instanziieren lässt, dass Sie das entsprechende Objekt einer Variablen zuweisen. Um dieses neue Grid anschließend an das Windows-Objekt zu binden, müssen Sie es nur der Content-Eigenschaft zuweisen:

$grid = New-Object Windows.Controls.Grid            
$window.Content = $grid

Das Grid ist in der aktuellen Implementierung nicht sichtbar (weißer Hintergrund des Grid auf weißem Hintergrund des Anwendungsfensters) und das ist auch für die aktuelle Beispielanwendung nicht erforderlich, denn wir wollen ja eine Browseranwendung erstellen, die eine Webseite darstellt und somit keinen eigenen Hintergrund benötigt. Um für Testzwecke das Grid sichtbar zu machen, können Sie diesem jedoch auch eine Hintergrundfarbe zuweisen:

$grid.Background = "lightblue"

Möchten Sie nun zusätzlich in das Grid noch ein (oder mehrere) Steuerelement einfügen, so müssen Sie dafür das Objekt des Steuerelements in der erforderlichen Klassen anlegen und dieses anschließend der Children-Collection des Grid zuweisen. Da ich mich in diesem Beispiel für ein Web-Browser-Steuerelement entschieden habe, instanziiere ich im Folgenden das entsprechende Objekt für dieses Steuerelement und weise der Eigenschaft Source die URL zu, die durch das Browser-Steuerelement aufgerufen werden soll:

$wb = New-Object Windows.Controls.WebBrowser
$wb.Source='https://sylvioh.wordpress.com/'            
$grid.Children.Add($wb)

Das Web-Browser Steuerelement verhält sich nun genauso wie ein Internet Explorer (Kontext-Menüs, Funktionstasten, ..).

Das vollständige Skript sieht nun so aus:

Add-Type -Assembly PresentationFramework            

$window = New-Object System.Windows.Window            
$window.Title = 'PowerShell Test program'            
$window.WindowStartupLocation = 'CenterScreen'           

$grid = New-Object Windows.Controls.Grid            
$wb = New-Object Windows.Controls.WebBrowser            
$wb.Source='https://sylvioh.wordpress.com/'            
$grid.Children.Add($wb)            
$window.Content = $grid            

$window.ShowDialog()

Das Anlegen von neuen Steuerelementen innerhalb einer WPF-PowerShell-Anwendung funktioniert nicht nur dann, wenn die gesamte Anwendung ohne XAML-Struktur angelegt wurde, sondern dies geht auch, wenn Sie eine XAML-Struktur als Basis für die Anwendung verwenden:

Add-Type -Assembly PresentationFramework            
[xml] $xaml = "
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="PowerShell Test program" Height="350" Width="525">
    <Grid Name="g1"/>
</Window>
"@
            
$reader=(New-Object System.Xml.XmlNodeReader $xaml)            
$Form=[Windows.Markup.XamlReader]::Load( $reader )            
            
[Windows.Controls.Grid]$grid= $Form.FindName("g1")            
$wb = New-Object Windows.Controls.WebBrowser            
$wb.Source='https://sylvioh.wordpress.com/'            
$grid.Children.Add($wb)            
            
$Form.ShowDialog()

Dieses Vorgehen funktioniert nicht nur für einzelne Elemente, sondern auch für mehrere Objekte (bspw. Sie wollen zehn Textbox-Steuerelemente mit dazugehörendem Label in ein Grid einfügen – nächstes Code-Beispiel) und auch für kaskadierte Steuerelemente (ineinander verschachtelte Steuerelemente – übernächstes Code-Beispiel).

Add-Type -Assembly PresentationFramework            
$window = New-Object System.Windows.Window            
$window.Title = 'PowerShell Test program'            
$window.WindowStartupLocation = 'CenterScreen'            
            
$grid = New-Object Windows.Controls.Grid            
$grid.HorizontalAlignment=[System.Windows.HorizontalAlignment]::Left            
$grid.VerticalAlignment=[System.Windows.VerticalAlignment]::Top            
for([int]$i=0;$i -lt 10; $i++) {            
    $tb = New-Object Windows.Controls.Textbox            
    $tb.Name = "tb$i"            
    $tb.Width=100            
    $tb.height = 23            
    $tb.text = $i            
    $tb.margin = "20,$($i * 80 -340),0,0"            
    $lb = New-Object System.Windows.Controls.Label            
    $lb.Content = "Textbox$i :"            
    $lb.margin = "0,$($i * 40),0,0"            
    $grid.Children.Add($lb) | Out-Null            
    $grid.Children.Add($tb) | Out-Null            
    $tb = $null            
}            
            
$window.Content = $grid            
$window.ShowDialog()

image

Die Hauptschwierigkeit in diesem ersten Beispiel bestand darin, die Label- und die Textbox-Objekte an den richtigen Stellen zu positionieren – das ist auch der Grund für die etwas merkwürdige Ränder (margin) in diesem Beispiel. Deshalb verwende ich im nächsten Beispiel ein zusätzliches Grid-Steuerelement, um Label und Textbox in einem eigenen Container zu kapseln – oder mit anderen Worten: die Steuerelemente zu kaskadieren.

Add-Type -Assembly PresentationFramework            
$window = New-Object System.Windows.Window            
$window.Title = 'PowerShell Test program'            
$window.WindowStartupLocation = 'CenterScreen'            
            
$grid = New-Object Windows.Controls.Grid            
$grid.HorizontalAlignment=[System.Windows.HorizontalAlignment]::Left            
$grid.VerticalAlignment=[System.Windows.VerticalAlignment]::Top            
for([int]$i=0;$i -lt 10; $i++) {            
    $sub_grid = New-Object Windows.Controls.Grid            
    $sub_grid.HorizontalAlignment=[System.Windows.HorizontalAlignment]::Left            
    $sub_grid.VerticalAlignment=[System.Windows.VerticalAlignment]::Top            
    $sub_grid.Margin="10,$($i * 30),0,0"            
            
    $tb = New-Object Windows.Controls.Textbox            
    $tb.Name = "tb$i"            
    $tb.Width=100            
    $tb.height = 23            
    $tb.text = $i            
    $tb.margin = "80,0,0,0"            
            
    $lb = New-Object System.Windows.Controls.Label            
    $lb.Content = "Textbox$i :"            
    $lb.margin = "0,0,0,0"            
            
    $sub_grid.Children.Add($lb) | Out-Null            
    $sub_grid.Children.Add($tb) | Out-Null            
    $grid.Children.Add($sub_grid) | Out-Null            
    $tb = $null            
}            
            
$window.Content = $grid            
$window.ShowDialog()

image

Wie Sie in diesem Beispiel sehen, ist die Positionierung der einzelnen Label- und Textbox-Objekte nun kein Problem mehr, da diese noch einmal in Grid-Objekten zusammengefasst worden sind.

Neue XAML-Strukturen einer laufenden Anwendung zuweisen

In den vorangegangenen Beispielen wurde einer laufenden Anwendung On-The-Fly ein oder mehrere Steuerelemente als WPF-Controls zugewiesen. Das Zuweisen von neuen Objekten zu einer Anwendung funktioniert jedoch auch indem Sie eine (sub) XAML-Struktur verwenden. Dafür sind aber ein paar Vorarbeiten erforderlich, die ich Ihnen im Folgenden erklären möchte.

Damit ein bestehende Zeichenkette (String) mit einer XAML-Struktur in ein Steuerelement (oder auch mehrere) konvertiert werden kann, muss diese Zeichenkette zuerst in eine durch die Anwendung lesbare XAML-Struktur konvertiert werden. Dazu müssen Sie im ersten Schritt einen Namespace definieren (Windows.Markup.Parser), bevor Sie in dem folgenden Schritt das Steuerelement mit einem XAML-Reader-Objekt einlesen können, um es dann abschließend dem Elternobjekt ($root.Children.add) zu zuweisen.

function Add-Element([string]$XAML, $parent) {            
    $pc = new-object Windows.Markup.ParserContext            
    $pc.XmlnsDictionary.Add("","http://schemas.microsoft.com/winfx/2006/xaml/presentation")            
    $element=[Windows.Markup.XamlReader]::Parse($XAML, $pc)            
    $parent.children.add($element)            
}            
            
Add-Type -Assembly PresentationFramework            
[System.Collections.Hashtable]$p = @{Title = 'PowerShell';WindowStartupLocation = 'CenterScreen'}            
[System.Windows.Window]$window = New-Object System.Windows.Window -Property $p            
            
[Windows.Controls.Grid]$grid = New-Object Windows.Controls.Grid            
$grid.HorizontalAlignment=[System.Windows.HorizontalAlignment]::Left            
$grid.VerticalAlignment=[System.Windows.VerticalAlignment]::Top            
            
Add-Element -XAML "<button content=’Test’>" -parent $grid $window.Content = $grid $window.ShowDialog()

Für das Beispiel habe ich ein sehr einfaches Objekt gewählt, indem ich einem Windows-Anwendung eine zusätzliche Schaltfläche zugewiesen habe. Jedoch sind auch wesentlich komplexere Lösungen damit denkbar.

Da diese Vorgehensweise in der Praxis mit etwas mehr Aufwand verbunden ist, gehe ich eher davon aus, dass sie nur selten Anwendung findet wird. Technisch ist sie auf jeden Fall ein valider Weg.

Funktionalität über Ereignisse (Events) von Objekten zuweisen

Zu den Objekten einer Anwendung können Sie PowerShell-Funktionen an Ereignisse binden, bevor die ShowDialog-Methode des Window-Objektes aufgerufen wird. Dies funktioniert genauso, wie bereits im Teil 3 der Artikelserie beschrieben.

Werden jedoch Steuerelemente dynamisch in einer Anwendung angelegt, so ist es auch erforderlich, einem solchen Steuerelement dynamisch Ereignisse zu zuweisen. Gehen wir nun auf das vorhergehende Beispiel zurück (Label- und Textbox-Objekte je in einem Grid-Objekt), stellen wir uns nun die Aufgabe, weitere Label- und Textbox-Objekt-Kombinationen durch ein Button-Objekt dynamisch der Anwendung hinzu zufügen.

Wichtig ist an dieser Stelle, dass die erforderlichen Ereignis-Funktionen (Event Handler) bereits vorher in der PowerShell-Anwendung definiert sind.

Dazu lagern wir die Routine in eine eigene Funktion aus und können Sie nun jedes Mal aufrufen, wenn eine Schaltfläche gedrückt wird.

Function Button_AddClick() {            
            
    $tbCounter = $grid.Children.Count            
            
    $sub_grid = New-Object Windows.Controls.Grid            
    $sub_grid.HorizontalAlignment=[System.Windows.HorizontalAlignment]::Left            
    $sub_grid.VerticalAlignment=[System.Windows.VerticalAlignment]::Top            
    $sub_grid.Margin="10,$($tbCounter * 30),0,0"            
            
    $lb = New-Object System.Windows.Controls.Label            
    $lb.Content = "Textbox$tbCounter :"            
    $lb.margin = "0,0,0,0"            
            
    $tb = New-Object Windows.Controls.Textbox            
    $tb.Name = "tb$tbCounter"            
    $tb.Width=100            
    $tb.height = 23            
    $tb.text = $tbCounter            
    $tb.margin = "80,0,0,0"            
            
    $btn = New-Object System.Windows.Controls.Button            
    $btn.Content = "+"            
    $btn.Name = "btn$tbCounter"            
    $btn.margin = "200,0,0,0"            
    $btn.Add_Click({Button_AddClick})            
            
    $sub_grid.Children.Add($lb) | Out-Null            
    $sub_grid.Children.Add($tb) | Out-Null            
    $sub_grid.Children.Add($btn) | Out-Null            
    $grid.Children.Add($sub_grid) | Out-Null            
}            
            
Add-Type -Assembly PresentationFramework            
$window = New-Object System.Windows.Window            
$window.Title = 'PowerShell Test program'            
$window.WindowStartupLocation = 'CenterScreen'            
            
$grid = New-Object Windows.Controls.Grid            
$grid.HorizontalAlignment=[System.Windows.HorizontalAlignment]::Left            
$grid.VerticalAlignment=[System.Windows.VerticalAlignment]::Top            
            
Button_AddClick            
            
$window.Content = $grid            
$window.ShowDialog()

image

Ich verwende in dem vorhergehenden Beispielen eine kurze Form der Deklaration (implicit declaration). Dabei werden Variablen ohne Typangabe definiert und die Ausführungsumgebung darf dann raten, um welchen Typ es sich handelt. Dies kann mitunter zu Problemen führen und deshalb sollten Sie sich daran gewöhnen, Variablen und andere Objekte vor der Verwendung zu definieren. Das ist zwar im ersten Schritt für den Programmierer ein wenig mehr Arbeit, erleichtert Ihnen später aber die Pflege des Programms. Der im vorherigen Beispiel verwendete Programmcode sieht dann folgendermaßen aus:

$Script:Button_AddClick=[Windows.RoutedEventHandler]{            
    param (            
        [Object]$sender,            
        [Windows.RoutedEventArgs]$e            
    )            
            
    [int]$tbCounter = $grid.Children.Count            
            
    [Windows.Controls.Grid]$sub_grid = New-Object Windows.Controls.Grid            
    $sub_grid.HorizontalAlignment=[System.Windows.HorizontalAlignment]::Left            
    $sub_grid.VerticalAlignment=[System.Windows.VerticalAlignment]::Top            
    $sub_grid.Margin="10,$($tbCounter * 30),0,0"            
    $sub_grid.Name ="sg$tbCounter"            
            
    [Windows.Controls.Label]$lb = New-Object Windows.Controls.Label            
    $lb.Content = "Textbox$tbCounter :"            
    $lb.margin = "0,0,0,0"            
            
    [System.Collections.Hashtable]$p = @{Name = "tb$tbCounter";             
        Width=100; Height = 23;             
        text = $tbCounter;             
        margin = "80,0,0,0"}            
    [Windows.Controls.Textbox]$tb = New-Object Windows.Controls.Textbox -property $p            
            
    [Windows.Controls.Button]$btn = New-Object Windows.Controls.Button            
    $btn.Content = "+"            
    $btn.Name = "btn$tbCounter"            
    $btn.margin = "200,0,0,0"            
    $btn.Add_Click($Button_AddClick)            
            
    $sub_grid.Children.Add($lb) | Out-Null            
    $sub_grid.Children.Add($tb) | Out-Null            
    $sub_grid.Children.Add($btn) | Out-Null            
    $grid.Children.Add($sub_grid) | Out-Null            
            
}            
            
Add-Type -Assembly PresentationFramework            
[System.Collections.Hashtable]$p = @{Title = 'PowerShell';WindowStartupLocation = 'CenterScreen'}            
[System.Windows.Window]$window = New-Object System.Windows.Window -Property $p            
            
[Windows.Controls.Grid]$grid = New-Object Windows.Controls.Grid            
$grid.HorizontalAlignment=[System.Windows.HorizontalAlignment]::Left            
$grid.VerticalAlignment=[System.Windows.VerticalAlignment]::Top            
            
$Script:Button_AddClick.Invoke($null, $null)            
            
$window.Content = $grid            
$window.ShowDialog()

Nachdem wir nun schon so weit gekommen sind, möchte ich trotzdem noch einmal auf unsere Ausgangsaufgabenstellung aus dem ersten Teil der Blogserie zurückkommen: Das Erstellen einer Anwendung zum Ausführen von TSQL-Abfragen.

Im zweiten Teil der Serie sind wir dort stehengeblieben, dass wir die Oberfläche zusammenbauen konnten. Nach den Erkenntnissen aus den letzten beiden Artikeln sind wir nun in der Lage die Steuerelement in der WPF-Anwendung mit Ereignisse zu versehen und diese auch dynamisch anzulegen. Da keine neuen Erkenntnisse bzw. erklärenswerte Neuerungen hinzugekommen sind, stelle ich Ihnen den Sourcecode als Download zur Verfügung.

WPF-Anwendung (PowerShell) in einer eigenen PowerShell-Umgebung starten

Bisher sind Sie wahrscheinlich über die Herausforderung gestolpert, dass eine WPF-Anwendung aus der PowerShell heraus gestartet, immer ein Fenster (PowerShell-Kommandofenster oder die ISE) geöffnet lässt und für Eingaben blockiert. Dies ist in den meisten Fällen unschön und stört die Harmonie auf dem Bildschirm. Doch auch dafür gibt es eine einfache Lösung, die sich mit wenigen Zeilen Programmcode umsetzen lässt:

Sie brauchen nur eine neue PowerShell-Session in einem PowerShell-Skript zu öffnen und der neuen Session das Skript mit der WPF-Anwendung zuweisen.

$rs=[RunspaceFactory]::CreateRunspace()            
$rs.ApartmentState = "STA"            
$rs.ThreadOptions = "ReuseThread"            
$rs.Open()            
$ps = [PowerShell]::Create()            
$ps.Runspace = $rs            
            
$ps.AddScript({            
    Add-Type -Assembly PresentationFramework            
    $window = New-Object System.Windows.Window            
    $window.Title = 'PowerShell Test program'            
    $window.WindowStartupLocation = 'CenterScreen'            
            
    $wb = New-Object Windows.Controls.WebBrowser            
    $wb.Source='https://sylvioh.wordpress.com/'            
            
    $grid = New-Object Windows.Controls.Grid            
    $grid.Children.Add($wb)            
    $window.Content = $grid            
             
    $window.Add_Loaded({             
     $window.Activate()              
    })            
            
    $window.ShowDialog()            
}).BeginInvoke() | Out-Null

Danach können Sie entweder manuell das Hauptfenster (z. B. die ISE) oder in dem äußeren Skript das PowerShell-Consolen-Fenster schließen. Diese Verfahrensweise funktioniert nicht nur für PowerShell-WPF-Anwendungen, sondern Sie können dies mit allen PowerShell-Skripten ausführen, die einen blockierenden Thread zurücklassen (wie in diesem Fall durch das ShowDialog).

Nachdem dieser Blog-Artikel bereits sehr umfangreich ist, möchte ich das Thema hier auch beenden und Ihnen Raum bieten, dies selbst auszuprobieren und möglicherweise weiter daran zu forschen. Sollten Sie dazu neue Erkenntnisse gewinnen, würde ich mich freuen, wenn Sie mich mit Kommentaren daran teilhaben lassen würden.

In einem späteren Blog-Artikel (außerhalb dieser Serie) plane ich das Thema PowerShell in PowerShell oder anderen Programmiersprachen wie z. B. C# noch weiter zu vertiefen.

Zusammenfassung

Das Windows Presentation Framework bietet Ihnen sehr viele Möglichkeiten zur Erstellung von Benutzeroberfläche. Diese lassen sich unkompliziert in PowerShell-Skripte einbinden, so dass es auch möglich ist, komplexe Benutzeroberflächen zu gestalten und diese in einem eigenen Thread ausführen zu lassen.

Einige werden jetzt vielleicht fragen, warum soll man für komplexe Anwendungen mit Benutzeroberflächen die “Programmiersprache” PowerShell verwenden und stattdessen nicht zu einer anderen Programmiersprache (C#, Java, …) wechseln. Diese Frage lässt sich nicht so ohne weiteres beantworten, denn PowerShell genauso wie andere Programmiersprachen haben ihre Daseinsberechtigung. Die Auswahl, was verwendet werden soll, hängt zum Einem immer von der Aufgabenstellung und zum Anderen vom Anwenderkreis ab.

Advertisements
Über

Die IT-Welt wird immer komplexer und zwischen den einzelnen Komponenten gibt es immer mehr Abhängigkeiten. Nachdem ich durch meine tägliche Arbeit immer wieder vor der Herausforderung stehe, komplexe Probleme zu lösen, möchte ich diese Seite dafür verwenden, Euch den einen oder anderen Tipp zu geben, wenn Ihr vor ähnlichen Aufgabenstellungen steht.

Veröffentlicht in PowerShell, SQL Server, WPF

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: