PowerShell GUI?

2 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Mit Poweshell hast Du vollzugriff auf alle .Net-Objekte, so auch auf die Formsbibliothek

In einfachsten Fall baust Du mit 3 Zeilen ein einfache GUI-Fenster.

Add-Type -AssemblyName System.Windows.Forms #einbinden der Bibliothek
$Form = New-Object System.Windows.Forms.Form #neues Form-Objekt
$Form.ShowDialog() #anzeigen

Natürlich kommst du mit dem Basisobjekt nicht weit, du musst zum Fenster weitere Objekte einfügen:

Add-Type -AssemblyName System.Windows.Forms #einbinden der Bibliothek
$Form = New-Object System.Windows.Forms.Form #neues Form-Objekt
$Form.Text = 'Irgendwas' #Titel
$Form.Size = '800,400' #größe (simplifiziert)

$Label = New-Object 'System.Windows.Forms.Label'
$Label.Location = '10,10'
$Label.AutoSize = $True
$Label.Text = 'Ich bin ein Label'

$TextBox = New-Object 'System.Windows.Forms.TextBox'
$TextBox.Location = '110,10'
$TextBox.Size = '180,20'

$Button1 = New-Object System.Windows.Forms.Button
$Button1.Location = '300,10'
$Button1.Size   = '70,20'
$Button1.Text   = 'Blubb'

$Button2 = New-Object System.Windows.Forms.Button
$Button2.Location = '300,30'
$Button2.Size   = '70,20'
$Button2.Text   = 'Blah'

$Label,$TextBox,$Button1,$Button2|
  ForEach-Object{$Form.Controls.Add($_)} #bequem: alle zuvor definierten Objekte in einem Rutsch einbinden

$Form.ShowDialog() #anzeigen

Was nützen die beste Forms-Objekte , wen die nix machen? Hier kommt einer der wenigen Unterschiede zum hinzufügen von Ereignissen (gegenüber C#). Da Powershell über keine Zeiger auf den compilierten Code von Scriptblöcken hat, können diese auch nicht direkt an den Eventhandler übergeben werden. Deshalb gib es die Hilfsfunktion "Add_EventName( $Action_Scriptblock)"

Die Auszulösende Aktion (Delegat) wird in einem gewöhnlichen Scriptblock oder einer Funktion definiert.

Add-Type -AssemblyName System.Windows.Forms  #einbinden der Bibliothek
$Form = New-Object System.Windows.Forms.Form  #neues Form-Objekt
$Form.Text = 'Irgendwas'  #Titel
$Form.Size = '800,400'  #größe (simplifiziert)


$Label = New-Object 'System.Windows.Forms.Label'
$Label.Location = '10,10'
$Label.AutoSize = $True
$Label.Text = 'gib was ein:'


$OutputLabel = New-Object 'System.Windows.Forms.Label'
$OutputLabel.Location = '10,100'
$OutputLabel.AutoSize = $True
$OutputLabel.Text = 'Ich  bin  noch Leer :)'


$TextBox = New-Object 'System.Windows.Forms.TextBox'
$TextBox.Location = '110,10'
$TextBox.Size = '180,20'


$Button1 = New-Object System.Windows.Forms.Button
$Button1.Location = '300,10'
$Button1.Size     = '70,20'
$Button1.Text     = 'Click mich'


#was passieren soll wenn der Knopf gedrückt  wird:
$Button1_Click = {
    $OutputLabel.Text = $TextBox.Text #eingegebenen Text im Label der GUI anzeigen
    Write-Host $TextBox.Text -fo green  #dito in die Console schreiben
    $global:blubb=$TextBox.Text  #da mit dem schließen das komplette GUI-Objekt (und auch dessen Funktionsstack/-variablen) zerstört  wird,  in eine global gültige Variable speichern
}
$Button1.Add_Click($Button1_Click)

$Label,$OutputLabel,$TextBox,$Button1|
    ForEach-Object{$Form.Controls.Add($_)}  #alle zuvor definierten Objekte einbinden
$null=$Form.ShowDialog() #anzeigen


$global:blubb #unsere Variable abrufen
pause

Hinsichtlich der Definition der Objekteigenschaften ist Powershell sogar einfacher als C#.

Wenn Du etwas googlst, wirst Du Definitionen wie:

$Button.Size = New-Object System.Drawing.Size(120,23)

...finden. übertriebener Schwachsinn! Powershell ist die Sprache für Faulpelze oder Administratoren, welche sich nicht mit Nebensachen abgeben wollen. PowerShell gibt sich damit zufrieden, wenn man die Eigenschaften in einem String übergibt:

$Button.Size = "120,23"

Powershell erkennt den Datentyp Drawing.Size/ Drawing.Point / etc. automatisch und konvertiert den übergebenen String in#s passende Objekt

Geil....

Man kann das Ganze noch auf die Spitze treiben: Powershell-Batch-Hybrid... (kann man mit Doppelclick starten und unter Powershell <6.0 Umlaute verwenden )

clock.cmd (ja ist eine Batch di nichts weiter tut als sich srelbst als Powerschellsript zu übergeben😅)


<# : Batch Abschnitt ,Batch sieht in dieser Zeile die Eingabe aus der Datei # an ein SprungLabel, Powershell sieht den begin eines Kommentars. 
:: etwas Tricky um Consolenfenster nur  so kurz wie Möglich sichtbar zu lassen
:: erstmal minimierten Start von Powershell erzwingen ( bis Powershell den Parameter -WindowStyle Hidden ausführen kann)
:: akzeptiert UTF8 gespeicherte Scripte
start "" /min powershell -WindowStyle hidden "iex (gc '%~f0' -Encoding UTF8 -Raw | out-string)" &rem -ReadCount 0


:: 
exit /b
: Ende Batch und Powershellkommentar #>


using namespace System.Windows.Forms
using namespace System.Drawing


$WindowTitle='DesktopUhr'
#Wenn es bereits läuft  raus
$Mut = New-Object System.Threading.Mutex($True, $WindowTitle)
if (!$Mut.WaitOne(0)) {
    $Mut.Close();
    (New-Object -c Wscript.Shell).Popup("$WindowTitle is running",2,"",0)
    exit
}


  #folgende 3 Zeilen nur nötig, wenn Script nicht mit "-WindowStyle hidden" gestartet wird!
#$user32=Add-Type -m '[DllImport("user32.dll")] public static extern void ShowWindow(IntPtr hWnd, int nCmdShow);' -Name myAPI -passthru
#$hwnd=(gps -id $PID).MainWindowHandle
#$user32::ShowWindow($hwnd, 0)


Add-Type -a 'System.Windows.Forms'
$Form = New-Object 'Form'
$Form.Text = $WindowTitle 
$Form.BackColor = '0,0,0' 
$Form.TransparencyKey = $Form.BackColor 
$Form.TopMost = $True
$Form.FormBorderStyle = 0 
$Form.Size = '250,90'
$Form.StartPosition = 'manual'
$Form.Location = '30,30'


$ClockDisplay = New-Object 'Label'
$ClockDisplay.Font = [Font]::new("Consolas",32,[FontStyle]3)
$ClockDisplay.Text = '{0:HH\:mm\:ss}' -f (Get-Date)
$ClockDisplay.AutoSize = $True
$ClockDisplay.ForeColor = '255,0,0'


$Form.Controls.Add($ClockDisplay)


$Timer1 = New-Object 'Timer'
$Timer1.Enabled = $True
$Timer1.Interval = 100
$Timer1_Action={
    $ClockDisplay.Text = '{0:HH\:mm\:ss}' -f (Get-Date) 
}
$Timer1.add_Tick($Timer1_Action)


 #ma  frech solch ein  Formsobjekt  anschauen (gigantisch)
#$Form|Export-Clixml -Path 'demo.xml' 


$Null=$Form.ShowDialog()
$Mut.ReleaseMutex(); 
$Mut.Dispose();

Die von @JokesOnYou angesprochene Verwendung von WPF ist relativ umständlich, da man den GUI-Editor von VisualStudio benötigt.

Das XML zu schreiben, welches Die GUI definiert ist von Hand einfach zu umständlich

[xml]$xaml = @"
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window">
    <Grid>
        <Label x:Name="Label"
          Content="Ich bin ein Label"
          Width="197"
          Margin="30,10,0,0"
          HorizontalAlignment="Left"
          VerticalAlignment="Top" />
        <Button x:Name="Button"
          Content="Click Mich!"
          Width="130"


          Margin="300,10,0,0"
          HorizontalAlignment="Left"
          VerticalAlignment="Top"
          Grid.Column="0"
          Grid.Row="0" />
    </Grid>
</Window>
"@
Add-Type -AssemblyName PresentationFramework
  #xml in darstellbare Objekte umwandeln:
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$window = [Windows.Markup.XamlReader]::Load($reader)
 #Augenoperation über  den Anus XD:
$Label = $Window.FindName("Label")
$Button = $Window.FindName('Button')
 # der  Scriptblock  wie  gehabt
$Button.add_Click({
    $Label.Content = "Butten  wurd  geclickt"
})

$window.ShowDialog()

allein die Zuweisung der im xml definierten Objekte an Powershellvariablen ist eine Farce... (rektale Augenoperation)

Ich empfehle Dir einfach Forms zu verwenden, Das Positionieren von Elementen ist ja nun kein Hexenwerk, welches unbedingt eines grafischen Editors bedarf.

Mit etwas Fantasie schreibst Du Dir einfach eigene Vorlagen.... (zur Not malst Du erstmal auf das gute alte Millimeterpappier)

Solch eine Chance völlig "nackt" mit Objekten zu agieren bekommst Du nicht so schnell wieder

kleiner Tipp noch, Du musst nicht alle Properties der Objekte im Kopf haben einfach in die Poweshellconsole eintippen:

Add-Type -AssemblyName System.Windows.Forms
New-Object System.Windows.Forms.Form|fl *
New-Object System.Windows.Forms.Button|fl *
New-Object System.Windows.Forms.TextBox|fl *

oder alle Feder ,Events und Methoden:;

New-Object System.Windows.Forms.Button|gm *
New-Object System.Windows.Forms.TextBox|gm *
Woher ich das weiß:eigene Erfahrung – Ich mach das seit 30 Jahren

Ne simple GUI mit WPF oder Window schreiben mit den Shell skripten in Kategorien und Funktionien unterteilt. Je nach ausgewählter Funktion müssten dann die zur Verfügung stehenden Parameter auf ihre Validity überprüft werden. Hier ein Link zu Stackoverflow wo jemand Powershell Skripte in C# ausführen lassen wollte:

https://stackoverflow.com/questions/33654318/c-sharp-run-powershell-command-get-output-as-it-arrives

Woher ich das weiß:Berufserfahrung – Software-Entwickler und Consultant für BMW

Erzesel  26.07.2023, 18:10

Warum erst ein C#-Programm basteln um damit Powershell auszuführen.

Hirnrissig in einer C# Anwendung einen Posh-Runspace zu kreiren. Allein das Initialisieren dauert ewig...😴

Jetzt kommt mein altbekannte "vom Ferrari auf ein rostiges Fahrrad umsteigen"- Vergleich. So nenne ich es wenn jemand von einer Höheren auf eine Niedere Sprache wechselt.

Powershell ist zwar bequem, aber bietet nicht unbedingt den Spielraum von C#.

Andersherum kann es von Vorteil sein in Powershell die Möglichkeiten von C# zu nutzen (Geschwindigkeit, Multithreading etc.) Dafür gibt es Add-Type .

Das manuelle einbinden von WPF in Powershell ist Umständlich und Fehleranfällig. Sollte man sich verkneifen...

Am besten und Saubersten ist es einfach Forms-Objekte direkt per Powershell zu steuern...

Fast lächerlich einfach...

1