PowerShell GUI Refresh unter WPF

>|Sh4d0w|<

Commander
Registriert
Jan. 2009
Beiträge
2.516
Hallo zusammen,
ich komme gerade nicht mehr bei meiner "Gameswitcher" GUI weiter.

Kurz vorab, das "Progrämmchen" öffnet sich automatisch, wenn ein XBox Controller angeschlossen wurde und ändert die Audio-Quelle auf den TV ab, startet Steam und öffnet zusätzlich ein Programm "Gopher", damit man unter Windows den Controller als Maus bedienen kann. Die bisherigen Buttons (Schaltflächen und "Icons" konnte ich mit einfachen Funktionen als Klick-Event belegen. Änderungen geschehen immer bei "klick".

Problem:
Ich möchte weitere GUI Elemente, z.B. Steam und Ubisoft Icon [Icons unter APPS], sowie den vordefinierten roten Radiobutton und die % Anzeige (Textblock) dynamisch während der Laufzeit verändern, wenn sich etwas ändert. z.B. Konnektivität Controller, Akkustatus, Check ob Programm im Taskmgr gestartet usw.

ChatGPT schlug mit einen Thread-dispatcher vor, welcher aber ganz und gar nicht funktioniert. Das war mir fachlich schon zu tief, wohin ich nur Copy & Paste nehmen konnte. Bei Google Anfragen stoße ich meist auf Schleifen oder Klickevents, welche nicht helfen. Versuche ich z.B. eine Schleife auf ein Klickevent zu legen, so hängt sich die GUI auf (=logisch) :)

Es muss also eine Art externe Prozess laufen, der die Elemente bei Änderungen aktualisiert. Wie stelle ich das an?

Die GUI schaut bei Programmstart bisher so aus, damit man ggf. sieht, was ich meine :)

screenshot.jpg
 
Warum versuchst du das nicht mit C# zu schreiben. Da wird das bestimmt so gehen wie von dir gedacht.
 
Weil ich kein C# kann :) Powershell kann doch allerlei Funktionen aus anderen Sprachen aufrufen. Da gibt es bestimmt etwas, was man einbinden kann. Oder sogar gibts da schon nativ :)
 
  • Gefällt mir
Reaktionen: aragorn92
Hast du mal etwas Beispielcode? In C# hätte man die eventuell direkt helfen können, aber wahrscheinlich rufst du über Powershell auch nur C# APIs auf. Mit etwas Code kann man da ggf. etwas gezielter raten.
 
Habe nochmal etwas umgebaut und "aufgehübscht" :D

Der Radiobutton ist weggefallen. Ich möchte z.B. die Akkuanzeige während der Laufzeit weiter prüfen, oder Aktionen ausführen, wenn die Auslastung weiter steigt. Ich könnte das z.B. in eine Abfrage einbauen, die dann mehrere Dinge aktualisiert oder startet.

screenshot.jpg


Der Code ist:
(Set-Audio mopdule, die WPF und die Icons fehlen hier)
PowerShell:
# Lade WPF Module #
Add-Type -AssemblyName PresentationFramework | Out-Null


# VARIABLEN #
$PfadRoot = "C:\1INSTALL\GameSwitcher\Tools"
$PFadIcons = $PfadRoot + "\icons"
$xamlFile = $PfadRoot + "\Projekt\WpfApp1\MainWindow.xaml"

$AudioPC = "Realtek"
$AudioTV = "SAMSUNG"
$AudioHS = "Headset"
$AudioAusnahme = "Micro" #Logitech Microphone (Headset)

$ControllerName = "XBox Wireless Controller"

$StartSteam = "C:\Program Files (x86)\Steam\steam.exe"
$StartUbisoft = "C:\Program Files (x86)\Ubisoft\Ubisoft Game Launcher\UbisoftConnect.exe"
$StartGopher = $PfadRoot + "\Gopher\Gopher.exe"


# Variable zum Testen clearen
if($ControllerConnected) { Remove-Variable -Name ControllerConnected }


function Set_Audio()
{
    param ([string]$Audio)

    $GamingAudio = Get-AudioDevice -List | Where-Object {($_.Name -match $Audio) -and ($_.Name -notmatch $AudioAusnahme) }
    Set-AudioDevice -Index $GamingAudio.Index -DefaultOnly
}


function AkkuCheck_Controller()
{
    $BTHDevices = Get-PnpDevice -FriendlyName "*$($ControllerName)*"
    $AkkuWert = Get-PnpDeviceProperty -InstanceId $BTHDevices.InstanceId -KeyName '{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2' | Select-Object -Expand Data
  
    if($AkkuWert -lt 25)
    {
        $textblockAkku.Foreground = "darkblue"
        $textblockAkku.Background = "red"
    }
  
    $textblockAkku.Text = $AkkuWert.ToString() + " %"
}



function Start_GUI()
{
    $XAMLContent = Get-Content -Path $xamlFile
    [xml]$XAMLContent = $XAMLContent -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' -replace ' x:Class="WpfApp1.MainWindow"', '' -replace 'Source="/','Source="C:\1INSTALL\GameSwitcher\Tools\icons\'


    [void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
    $Form = [Windows.Markup.XamlReader]::Load( (New-Object System.Xml.XmlNodeReader $XAMLContent) )

    # Definiere GUI Elemente
    #Button AudioPC
    $buttonPC = $Form.FindName("buttonPC")
    $buttonPC.Add_Click(
    {
        Set_Audio -Audio $AudioPC
        $buttonPC.Opacity = "1"
        $buttonTV.Opacity = "0.2"
        $buttonHS.Opacity = "0.2"
    })

    #Button AudioTV
    $buttonTV = $Form.FindName("buttonTV")
    $buttonTV.Add_Click(
    {
        Set_Audio -Audio $AudioTV
        $buttonPC.Opacity = "0.2"
        $buttonTV.Opacity = "1"
        $buttonHS.Opacity = "0.2"
    })

    # Button AudioHeadset
    $buttonHS = $Form.FindName("buttonHS")
    $buttonHS.Add_Click(
    {
        Set_Audio -Audio $AudioHS
        $buttonPC.Opacity = "0.2"
        $buttonTV.Opacity = "0.2"
        $buttonHS.Opacity = "1"
    })

    # Button Steam
    $buttonSteam = $Form.FindName("buttonSteam")
    $buttonSteam.Add_Click(
    {
        Start-Process $StartSteam
    })

    # Button Ubisoft
    $buttonUbisoft = $Form.FindName("buttonUbisoft")
    $buttonUbisoft.Add_Click(
    {
        Start-Process $StartUbisoft
        $buttonUbisoft.Opacity = "1"
    })

    # Button GOPHER
    $buttonGopher = $Form.FindName("buttonGopher")
    $buttonGopher.Add_Click(
    {
        #Beende "neustarten" und starten
        Stop-Process -name Gopher -ErrorAction SilentlyContinue
        Start-Process $StartGopher
    })

    # Bild XBox Controller nicht notwendig

    # Textblock Akku
    $textblockAkku = $Form.FindName("textblockAkku")
    AkkuCheck_Controller

    # Button BEENDEN
    $buttonBeenden = $Form.FindName("buttonBeenden")
 
    $buttonBeenden.Add_Click(
    {
        $Form.Close()
      
        Set_Audio -Audio $AudioPC
        Stop-Process -Name Gopher -ErrorAction SilentlyContinue
    })


    #Anpassung Startoptik:
    $buttonPC.Opacity = "0.2"
    $buttonTV.Opacity = "1"
    $buttonHS.Opacity = "0.2"

    $buttonUbisoft.Opacity = "0.2"

    $ubigestartet = Get-Process -Processname "upc" -ErrorAction SilentlyContinue
    if($ubigestartet) { $buttonUbisoft.Opacity = "1" }

    #Aufruf GUI
    $Form.ShowDialog()
}



function main()
{
    do
    {
        $BTHDevices = Get-PnpDevice -FriendlyName "*$($ControllerName)*"
        $ControllerConnected = Get-PnpDeviceProperty -InstanceId $BTHDevices.InstanceId -KeyName '{83DA6326-97A6-4088-9453-A1923F573B29} 15' | Select-Object -Expand Data

        if($ControllerConnected -eq $true)
        {
            # ändere Audioquelle bei Programmstart
            Set_Audio -Audio $AudioTV

            # starte Steam, Gopher und GUI
            Start-Process $StartSteam

            $GopherSchonGestartet = Get-Process -Processname "Gopher" -ErrorAction SilentlyContinue
            if(!($GopherSchonGestartet)) { Start-Process $StartGopher -WindowStyle Minimized }

            $GUIschonGestartet = Get-Process -Processname "gameswitcher" -ErrorAction SilentlyContinue
            if(!($GUIschonGestartet)) { Start_GUI }

        }
        start-sleep -Seconds 10
        Write-Host "Starte Loop erneut"


    }while($true)


}



#Programmstart
main
#Start_GUI
 
Zuletzt bearbeitet:
Ich bin jetzt nicht am Rechner, aber ShowDialog sorgt dafür, dass das Skript an der Stelle stehen bleibt und wartet, dass das Fenster geschlossen wird. Du könntest stattdessen Show aufrufen und danach eine while Schleife starten, welche die GUI alle x Sekunden (z. B. AkkuCheck_Controller aufrufen) aktualisiert. Die while Schleife stoppst du, sobald das Fenster geschlossen wurde.

So in etwa (bin nur am Handy)

PowerShell:
function Start_GUI()
{
   ... 
 
    $buttonBeenden.Add_Click(
    {
        $Form.Close()
        $formOffen = $false
      
        Set_Audio -Audio $AudioPC
        Stop-Process -Name Gopher -ErrorAction SilentlyContinue
    })

   ... 

    #Aufruf GUI
    $Form.Show()
    $formOffen = $true

    while ($formOffen) {
      Start-Sleep -seconds 60
      AkkuCheck_Controller
    }
}
[CODE]
 
  • Gefällt mir
Reaktionen: areiland
@marcOcram
Sowas dachte ich mir auch, das Form.Show oder Form.Load Event nutzen, um das GUI zu aktualisieren.
 
$Form.Show() und der Rest bewirken, dass sich die GUI "aufhängt" bzw. wartet und somit unbenutzbar ist.

Form.Load() wirft den Fehler "Fehler beim Aufrufen der Methode, da [System.Windows.Window] keine Methode mit dem Namen "Load" enthält." aus
 
Dann musst Du schauen, was in Deinem Fall möglich ist. In der Powershell hab ich noch nie ein solches Event genutzt, sondern nur in eigenen nativen Anwendungen.
 
Ich denke ich habe einen Weg gefunden.

Wenn ich vorm Erzeugen der Form folgendes setze, dann kann ich in dem Beispiel jede Sekunden Prüfungen einbauen und Elemente verändern. Hier schreibe ich beim Beenden-Button die aktuelle Uhrzeit rein, die sich jede Sekunde aktualisiert:
PowerShell:
    # Timer einrichten
    $timer = New-Object System.Windows.Threading.DispatcherTimer
    $timer.Interval = [TimeSpan]::FromSeconds(1)  # Alle 1 Sekunde aktualisieren
    $timer.Add_Tick({
    # Zufälligen Text generieren
    $buttonBeenden.Content = (Get-Date).ToString("T")
  
    $timer.start()
  
    #Aufruf GUI
    $Form.ShowDialog()
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: marcOcram
Fehlt eigentlich fast nur noch das Databinding und schon hast du MVVM implementiert. Darauf setzen die meisten WPF-Programme, die mit C# programmiert werden. Den Dispatcher für das Ändern "außer der Reihe" für die GUI hast du ja auch schon gefunden. :daumen:
Das Skript sieht übrigens C#-Code schon recht ähnlich. Ich glaube nicht, dass du große Probleme bekommen würdest, das zu übersetzen.
 
  • Gefällt mir
Reaktionen: aragorn92
Zurück
Oben