highspeed1
Goto Top

Powershell addShown unterbrechen wenn Abbrechen angeklickt wird

Hallo Forum.

Ich möchte während der $form.add_Shown läuft den Abbrechen-Button anklicken und hiermit das Fenster beenden. Wie kann man das umsetzen? Ist die Aktion (Hochzählen in der TextBox) abgeschlossen funktioniert der Button ja wie gewohnt.

function Get-Action
{
    $ResultText = [ordered]@{}

    $LabelLocationX = 10
    $LabelSizeX =     200
    $LabelSizeY =     30

    $TextBoxLocationX = $LabelSizeX + 10
    $TextBoxSizeX = 460

    $DialogBoxSizeX = $TextBoxLocationX + $TextBoxSizeX + 25
    $DialogBoxSizeY = 250

    # ================================================================================================================================================

    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing

    $form = New-Object System.Windows.Forms.Form
    $form.Size = New-Object System.Drawing.Size($DialogBoxSizeX,$DialogBoxSizeY)
    $form.MinimumSize = New-Object System.Drawing.Size($DialogBoxSizeX,$DialogBoxSizeY)
    $form.MaximumSize = New-Object System.Drawing.Size($DialogBoxSizeX,$DialogBoxSizeY)
    $form.ControlBox = $false
    $form.StartPosition = 'CenterScreen'  

    # CancelButton
    $CancelButton = New-Object System.Windows.Forms.Button
    $CancelButton.Location = New-Object System.Drawing.Point(10,200)
    $CancelButton.Size = New-Object System.Drawing.Size(70,23)
    $CancelButton.Text = 'Abbrechen'  
    $CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
    $form.Controls.Add($CancelButton)
    
    
    $TextBox = New-Object System.Windows.Forms.TextBox
    $TextBox.Location = "10,10"  
    $TextBox.Size = "100,100"  
    $TextBox.ReadOnly = $true
    $TextBox.Multiline = $true
    $TextBox.ScrollBars = "Vertical"  
    
    $Form.Controls.Add($TextBox)

    # === Action ===
    $form.add_Shown({
        $Counter = 0
        0..10 | foreach {
            $Counter += 1
            $TextBox.Text = $Counter
            sleep -Milliseconds 500
        }
    })

    $form.Topmost = $true
    $form.ShowDialog()
    
}

# ==============================================================================================================================================================================================================================

$Action = Get-Action

Content-ID: 41940313715

Url: https://administrator.de/contentid/41940313715

Ausgedruckt am: 21.11.2024 um 20:11 Uhr

7907292512
Lösung 7907292512 27.09.2023 aktualisiert um 14:04:36 Uhr
Goto Top
Morsche.
Genau das hatten wir ja eigentlich schon mit dir hier abgehandelt ... im nachstehenden Thread steht wie du dir den UI Thread nicht durch eine Routine blockierst.
Powershell Kopiervorgang und StatusBar mit hochlaufender Prozentzahl
Da brauchst du dann zum Abbrechen des Script-Tasks auf deinem Button nur
$ps.Stop()
$form.Close()
ausführen.

Beispielcode:
Add-Type -A System.Windows.Forms
# synchronized Hashtable für den Austausch von Daten zwischen den Threads erstellen
$ht = [hashtable]::Synchronized(@{})
# Powershell thread erstellen
$ps = [powershell]::Create()
# die Hashtable als Variable "ht" dem Powershell-Runspace hinzufügen  
$ps.Runspace.SessionStateProxy.SetVariable("ht",$ht)        
#form erzeugen
$form = New-Object System.Windows.Forms.Form -P @{
    ClientSize = '200,80'    
    Text = "Copystatus"  
    add_Shown = {
        # dem separaten Powershell Thread ein Skript hinzufügen
        $ps.AddScript({
            # hier statt der Schleife die Aktionen einfügen und Updates an die Statusbar schicken:
            1..10 | %{
                $ht.statusbar.Text = "Datei $_"    
                sleep -Milliseconds 500
            }
            $ht.statusbar.Text = "Fertig"    
        })
        $ps.BeginInvoke()
    }
}
# button erzeugen
$btn = New-Object System.Windows.Forms.Button -P @{
    Text = "Abbrechen"  
    Location = '10,20'    
    Size = '180,30'    
    Anchor = 'Bottom,Left,Right'    
    # aktion wenn auf den Button geklickt wird
    add_Click = {
         # Abbruch, Task beenden und Fenster schließen
        $ps.Stop()
        $form.Close()
    }
}
$statusbar = [System.Windows.Forms.StatusBar]@{
    Dock = "Bottom"    
}
$ht.statusbar = $statusbar
$form.Controls.AddRange(@($statusbar,$btn))
[void]$form.ShowDialog()
$ps.Dispose()

sid
mayho33
Lösung mayho33 27.09.2023 um 23:07:52 Uhr
Goto Top
Nur um sicher zu gehen @HighSpeed1 Vielleicht verstehst du nicht was man eigentlich bezweckt, wenn ein Teil des Codes, bzw die GUI in einen eigene Runspace gepackt wird:

Prinzipiell kennt ein jedes Programm nur einen linearen Ablauf. Bedeutet, dass eine Aufgabe nach der anderen abgearbeitet wird.
Startest du also eine Aufgabe nachdem die GUI geladen wurde, wird diese solange "stillgelegt" bis die Aufgabe abgearbeitet ist. Oder du startest die Aufgabe zuerst und die GUI lädt erst nachdem die Aufgabe erledigt ist.

Das will man normalerweise nicht. Deshalb muss zb die GUI in einem eigenen Thread laufen. Das bewerkstelligt man mit einem Runspace. Ab da laufen GUI und Aufgabe in 2 parallel laufenden Threads und die GUI freezed nicht mehr.

Grüße!
HighSpeed1
HighSpeed1 28.09.2023 um 08:17:41 Uhr
Goto Top
Hallo zusammen.

Ok jetzt habe ich es denke ich soweit verstanden. Einzig die Zeile
$ht.statusbar = $statusbar
verstehe ich noch nicht so ganz.

An $form.Controls wurde doch die Variable $statusbar übergeben:
$form.Controls.AddRange(@($statusbar,$btn))

Jetzt übergebe ich die Variable $statusbar an $ht.statusbar. Diese ist doch gar nicht aktiv. Wieso kommt also der Text an? Müsste es nicht umgekehrt übergeben werden ($statusbar = $ht)? Ich weiß es funktioniert, aber warum? Kann mir hierzu vielleicht noch einer eine Erklärung geben.

Danke.
7907292512
Lösung 7907292512 28.09.2023 aktualisiert um 10:22:46 Uhr
Goto Top
$ht ist eine Synchronized Hashtable die für den Datentransfer zwischen den beiden Threads fungiert.
$ht.statusbar = $statusbar
Dies dient dazu in der synced Hashtable die Statusbar für den anderen Threads zugreifbar zu machen. Man weist also dem Key "statusbar" der Hashtable das Objekt der Statusbar zu um später aus dem anderen Thread auf die Statusbar zugreifen zu können. Man könnte hier auch gleich die ganze Form als Objekt übergeben, das geht auch, dann muss man über die "Controls" Collection der Form auf die Statusbar zugreifen.

Durch das
$ps.Runspace.SessionStateProxy.SetVariable("ht",$ht)
wird diese Hashtable dann als Variable mit dem selben Namen $ht in dem anderen Thread verfügbar gemacht.
Und dadurch das es eine synced Hashtable ist, besitzt sie dann auch alle Eigenschaften die ihr im Hauptthread zugewiesen wurden.
Separate Threads wissen nämlich im Normalfall rein gar nichts von dem anderen Thread, auch nichts von dessen Variablen, deswegen dient hier die Synced-Hashtable quasi als Datentunnel zwischen beiden Threads.

Jetzt klarer?
HighSpeed1
HighSpeed1 28.09.2023 um 11:48:15 Uhr
Goto Top
Danke an euch beide. Jetzt hab ich es verstanden.

Super.
7907292512
7907292512 28.09.2023 um 12:01:54 Uhr
Goto Top
Das ist die Hauptsache 👍