none
Problem beim Erstellen einer GUI mit Powershell RRS feed

  • Allgemeine Diskussion

  • Hallo zusammen,

    ich habe zu meinem unten stehenden Skript eine Frage!
    Bzw. hoffe ich, dass ihr mir helfen könnt, den Fehler zu finden.

    Es geht darum, dass ich für eine Abfrage eine kleine GUI schreiben möchte. In diesem speziellen Fall soll ein Kunde ausgewählt werden (erster Knopfdruck), wodurch eine Funktion aufgerufen wird, welche die GUI verändert. Man soll nun den Kunden auswählen (hier durch die Zahlen 1 bis 15 repräsentiert) und nachdem man auf dem entsprechenden Button gedrückt hat, soll unter der Combobox der ausgewählte Kunde ausgegeben werden.

    Leider kann ich mich drehen und wenden, wie ich will - das $objCombobox.selectedItem bleibt leer.

    Als Workaround habe ich das Ganze ohne Functions geschrieben und es funktioniert. Deswegen vermute ich, dass es irgendwas mit der Parameterübergabe zu tun hat. Aber kann/muss man Objekte anderen Funktionen übergeben?

    Die umgeschriebene Fassung ist leider sehr unübersichtlich und zudem lässt mich dieses Problem nicht los.

    Könnt ihr mir helfen?

    Vielen Dank schon mal, dass du dir das bis hierhin durchgelesen hast. :-)

    Viele Grüße

    #################
    # Informationen #
    #################
    # Skriptname: GUI_stoerbericht
    # Version: 0.1
    # Datum: 15.07.2019
    # Autor: german_erd ;-)
    #################################
    #############
    # Preskript #
    #############
    Add-Type -AssemblyName System.Windows.Forms
    [System.Windows.Forms.Application]::EnableVisualStyles()
    #############
    # Variablen #
    #############
    ##############
    # Funktionen #
    ##############
    function KundeInKontextUebernehmen{
        $outputLabel              = New-Object System.Windows.Forms.Label
        $outputLabel.AutoSize     = $true
        $outputLabel.width        = 300
        $outputLabel.height       = 20
        $outputLabel.location     = New-Object System.Drawing.Point(100,325)
        $outputLabel.text         = "Ausgewählter Kunde: " + $objCombobox.SelectedItem
        $outputLabel.Font         = 'Microsoft Sans Serif,10'
        $objForm.controls.AddRange(@($outputLabel))
    }
    function EinKundeAusgewaehlt {
        # GUI
        # Button Alle Kunden Ausblenden
        $objForm.Controls.remove($AllCustButton)
        $objForm.refresh()
        # Combobox
        $objCombobox             = New-Object System.Windows.Forms.ComboBox
        $objCombobox.Location               = New-Object System.Drawing.Point(100,300)
        $objCombobox.Size                   = New-Object System.Drawing.Size(600,200)
        1..15 | ForEach-Object {$objCombobox.Items.Add($_)}
        # Button
        $Button                             = New-Object System.Windows.Forms.Button
        $Button.Location                    = New-Object System.Drawing.Point(500,325)
        $Button.Size                        = New-Object System.Drawing.Size(200,50)
        $Button.Font                        = "Microsoft Sans Serif,12"
        $Button.Text                        = "Gewählten Kunden übernehmen"
        #$button.Visible                     = $false
        $objForm.controls.AddRange(@($objCombobox,$Button))
        $Button.add_click({ KundeInKontextUebernehmen })
    }
    #############
    # Basic GUI #
    #############
    # Objekt Fenster
    $objForm                            = New-Object system.Windows.Forms.Form
    $objForm.ClientSize                 = '800,600'
    $objForm.text                       = "Störberichte Skript"
    $objForm.TopMost                    = $true
    $objForm.BackgroundImageLayout      = 2
    $objForm.BackgroundImage            = [System.Drawing.Image]::FromFile('C:\Users\xxxxx\Desktop\xxxx.jpg')
    $objForm.AcceptButton               = $Button
    $objForm.Controls.Add($Button)
    # Abbrechen-Button#
    $CancelButton                       = New-Object System.Windows.Forms.Button
    $CancelButton.Location              = New-Object System.Drawing.Size(600,500)
    $CancelButton.Size                  = New-Object System.Drawing.Size(150,50)
    $CancelButton.Text                  = "Abbrechen"
    $CancelButton.Name                  = "Abbrechen"
    $CancelButton.DialogResult          = "Cancel"
    $CancelButton.Add_Click({$objForm.Close()})
    # Alle Kunden auswählen#
    $AllCustButton                      = New-Object System.Windows.Forms.Button
    $AllCustButton.Location             = New-Object System.Drawing.Size(100,100)
    $AllCustButton.Size                 = New-Object System.Drawing.Size(250,150)
    $AllCustButton.Text                 = "Alle Kunden auswählen"
    $AllCustButton.Name                 = "Alle Kunden auswählen"
    $AllCustButton.Font                 = "Microsoft Sans Serif,16"
    $AllCustButton.Add_Click({ alleKundenAusgewaehlt })
    # Einen Kunden auswählen#
    $OneCust                            = New-Object System.Windows.Forms.Button
    $OneCust.Location                   = New-Object System.Drawing.Size(450,100)
    $OneCust.Size                       = New-Object System.Drawing.Size(250,150)
    $OneCust.Text                       = "Einen Kunden auswählen"
    $OneCust.Name                       = "Einen Kunden auswählen"
    $OneCust.Font                       = "Microsoft Sans Serif,16"
    $OneCust.Add_Click({ EinKundeAusgewaehlt })
    # Finales Einfügen der einzelnen Objekte
    $objForm.controls.AddRange(@($OneCust,$AllCustButton,$CancelButton))
    #####Ende Basic GUI#####
    # Zum Ausführen des ganzen Spaßes
    [void]$objForm.ShowDialog()



    Mittwoch, 17. Juli 2019 12:50

Alle Antworten

  • Für GUI's ist die PS eigentlich weniger gut geeignet.
    Mittels z.B. VisualStudioCode geht vieles einfacher.
    Aber sei's drum:

    Wichtig bei Forms-GUI ist, dass alle Variablen nur gültig sind, so lange der Dialog geöffnet bleibt.
    Du musst also den Ereignissen der Controls Funktionen oder Funktionsblöcke zuweisen, die dann auf die Controls zugreifen können.
    Dabei müssen die Variablen der Controls global definiert werden sonst haben die Funktionen keinen Zugriff drauf!
    Die Combobox hat z.B. auch ein Ereignis auf "SelectedIndexChanged".
    https://docs.microsoft.com/de-de/dotnet/api/system.windows.forms.combobox.selectedindexchanged?view=netframework-4.8

    Du solltest den Aufbau des Formulars an den Anfang legen und die Funktionen nach dem ShowDialog.


    • Bearbeitet bfuerchau Mittwoch, 17. Juli 2019 17:52
    Mittwoch, 17. Juli 2019 17:50
  • was ich Dir empfehlen kann für die GUI Erstellung ist PowerShell Studio von SAPIEN

    ich verwende das seit Jahren und bin insbesondere bei der Erstellung von GUI echt begeistert. 

    Du hast hier insbesondere direkten Zugriff auf die Events hinter der GUI was sehr hilfreich ist. 

    Ein weitere Vorteil ist das Du direkt aus der Anwendung raus eine .exe erstellen kannst. 

    lg Klaus 

    | Please Mark This As Answer if it solved your issue |
    | Please Vote This As Helpful if it helps to solve your issue |
    | Disclaimer:
     This posting is provided with no warranties and confers no rights. |
    | N 48° 8' 39.8419" E 11° 36' 1.3359" |


    Klaus

    Mittwoch, 17. Juli 2019 17:54
  • Ja, wenn man nicht immer alles umsonst haben will, kann eine gute Software schon hilfreich sein.
    Mittwoch, 17. Juli 2019 21:39
  • Sorry ich mache das urngern, aber dein Problem hat nichts mit der GUI, Winforms oder Editoren zu tun. Sondern schlicht mit Scopes.
    Im Gegensatz zum Rest deiner GUI erstellst deine Combobox innerhalb einer Funktion und weißt sie der Variablen

    $objCombobox

    zu. Wie alle anderen Variablen gilt diese nur in Ihrem und untergeordneten Gültigkeitsbereichen (Scope). Das ist im Falle einer Funktion nur die Funktion selbst und Funktionen die aus dieser aufgerufen werden (und so weiter).
    Innerhalb der anderen Funktion in der dein Click-Event steht oder im Script-Root nicht

    Das besondere bei GUIs ist hier nur, das obwohl die Variable nicht mehr zugänglich ist, das GUI-Objekt, in dem Fall die Combobox, nicht verschwindet solange die Form existiert. Du kannst sie nur nicht mehr Ansprechen, du hast sozusagen die Adresse weggeworfen. 
    Die Lösung ist somit z.b. die Combobox einer Scriptweiten Variable zuzuweisen:

        $script:objCombobox = New-Object System.Windows.Forms.ComboBox

     
    Deutlich besser wäre es aber, alle UI-Elememnte im ROOT zu erstellen und innerhalb der Funktionen nur die Eigenschaften zu verändern. So wie du es jetzt machst, wird jedesmal ein neues Label erstellt. Das funktioniert zwar theoretisch, praktisch aber ist das neue Label nicht zu sehen bevor das alte gelöscht wird. Und abgesehen davon ist es nicht schön. :)
    Also: alle UI-Elemente ins Root.

    Das gleiche Problem bekommst du übrigens auch mit $outputLabel, sofern du darauf nochmal zugreifen willst.

    Grüße, Denniver


    Blog: http://www.bytecookie.de

    Powershell Code Manager: Link
    (u.a. Codesnippets verwalten + komplexe Scripte graphisch darstellen)

    Hilf mit und markiere hilfreiche Beiträge mit dem "Abstimmen"-Button (links) und Beiträge die eine Frage von dir beantwortet haben, als "Antwort" (unten).
    Warum das Ganze? Hier gibts die Antwort.

    Sonntag, 28. Juli 2019 12:55
    Moderator
  • > Du solltest den Aufbau des Formulars an den Anfang legen und die Funktionen nach dem ShowDialog.
     
    Wenn man das tut werden die Funktionen aber erst definiert wenn die Form wieder geschlossen wird und können daher nicht aufgerufen werden. :)
    Nach ShowDialog() kann bis zum Schließen der Form ausschließlich der Code ausgeführt werden der vorher definiert wurde. Deshalb sollte der Aufruf der Form immer am Ende des Scriptes stehen.


    Blog: http://www.bytecookie.de

    Powershell Code Manager: Link
    (u.a. Codesnippets verwalten + komplexe Scripte graphisch darstellen)

    Hilf mit und markiere hilfreiche Beiträge mit dem "Abstimmen"-Button (links) und Beiträge die eine Frage von dir beantwortet haben, als "Antwort" (unten).
    Warum das Ganze? Hier gibts die Antwort.

    Sonntag, 28. Juli 2019 13:18
    Moderator
  • Da die Form eine Controls-Auflistung hat, kann jedes Control wieder über seinen Namen aus dieser Auflistung aufgerufen werden. Eigene Variaben sind daher direkt nicht erforderlich, sie erleichtern nur den Zugriff.
    Wichtig ist halt nur, dass Controls einen Namen haben müssen.
    Ebesno gilt dies auch für sog. Containerconrols wie z.B. Groupboxen. Der Zugriff erfolgt eben dann über
    $MyForm.Controls["NameDesControls"]
    $MyForm.Controls["NameDes Containers"].Controls["NameDesControls"].

    Bei der Zuweisung von Events können eben Funktionen zugewiesen werden, die generalisierbar sind. Jedes Ergeignis hat genau 2 Argumente:
    Sender => das Control selber
    EventArgs => Die Argumene des Ereignisses.
    Somit enthält der Sender immer bereits das Objekt, dass das Ereignis ausgelöst hat, so dass man z.B. über den Namen erfährt, welches Control denn aktiv geworden ist.

    Sonntag, 28. Juli 2019 15:37
  • Absolut richtig, das war ungenau. Es ist nicht unmöglich dann noch auf die Controls zuzugreifen, aber sehr unpraktisch. ;) Ich bin allerdings auch ein Fan davon die Informationstiefe etwas an die Zielgruppe anzupassen.

    Blog: http://www.bytecookie.de

    Powershell Code Manager: Link
    (u.a. Codesnippets verwalten + komplexe Scripte graphisch darstellen)

    Hilf mit und markiere hilfreiche Beiträge mit dem "Abstimmen"-Button (links) und Beiträge die eine Frage von dir beantwortet haben, als "Antwort" (unten).
    Warum das Ganze? Hier gibts die Antwort.

    Sonntag, 28. Juli 2019 16:49
    Moderator