Jump to content
newbi2009

Powershell GUI – Function outsourcing

Recommended Posts

Hallo Forum,


I habe ein Problem, bei dem ich hoffe, dass Ihr mir helfen könnt:
Ich habe ein Fenster per Powershell mit dem Namen "$objForm" gebaut

$objForm = New-Object System.Windows.Forms.Form

in dieses Fensterhabe ich (unter Anderem) einen Knopf (mit namen $btn5) eingebaut.

Wird dieser Knopf gedrückt, soll eine Funktion (mit dem namen done) aufgerufen werden und eine Variable ($WindowName) übergeben werden. Diese Funktion soll einen "Done-Knopf" in das geöffnete Fenster-Formular einbblenden. (Das Ganze funktioniert einwandfrei, solange ich den Code nicht als Funktion auslagere)

 

In dem "Hauptfenster-Formular" habe ich 'objForm' an die Variable $WindowName übergeben (das ist ja der Name des Fenster-Formulares)

$WindowName ='objForm'

um die Funktion dann aufzurufen, wird beim Betätigen des entsprechnden Knopfes folgendes ausgeführt (also Aufruf der Funktion "done" mit Übergabe der Varibale "$WindowName")

$btn5.add_Click{(done $WindowName)}


Meine Funktion sieht so aus :

function done
{
param ($WindowName)

$done = New-Object System.Windows.Forms.Button
$doneFont = New-Object System.Drawing.Font("Calibri",18,[System.Drawing.FontStyle]::Bold)
$done.Font = $doneFont
$done.Location = New-Object System.Drawing.Size(320,200)
$done.Size = New-Object System.Drawing.Size(150,50)
$done.ForeColor="red"
$done.Text = "DONE!"

write-host 'Hello'
write-host $WindowName
$WindowName

$done.Add_Click({
$WindowName.Controls.Remove($done),
$WindowName.Controls.refresh()
})
$WindowName.Controls.Add($done)
}

Starte ich das Programm jetzt (Drücke also den Knopf 5 im Hauptfenster-Formular), bekomme ich foilgende Meldung:

Hello
objForm
you cannot call a method on a null-valued expression.
At C:\temp\done.ps1:22 char:1
+WindowName.Controls.Add($done)
+
+CategoryInfo : InvalidOperation: (:) [], RuntimeException
+Fully QualifiedErrorID: InvokeMethodOnNull

Das 'Hello' kommt vom 1. wrtite-Host Befehl (Da wollte ich nur schauen, ob write-Host überhaupt funktioniert)

Das 'objForm' kommt vom 2. write-Host Befehl (Super! Die variable '$WindowName' wird also übergeben!!)

 

Aber warum wird der Inhalt Variable 'WindowName' nicht auch beim nächsten befehl angewendet?

Das  $WindowName.Controls.Add($done)  sollte eigentlich umgewandelt werden in ein ' objForm.Controls.Add($done)' - dann wäre alles super und es würde funktionieren. Aber das bekomme ich nicht hin?!
 


Vielen Dank für jede Hilfe
newbi

Share this post


Link to post

Moin,

 

du hast gesehen, dass dies ein deutschsprachiges Forum ist?

 

Zu deiner Frage: Wenn deine Codefragmente zutreffen, dann hast du in die Variable $WindowName den String "objForm" geschrieben. Diesen String kannst du natürlich in der Funktion auch wieder ausgeben. Da es aber nun mal kein Objekt ist, gibt es auch keine Methoden.

 

Gruß, Nils

 

Share this post


Link to post
vor 8 Stunden schrieb NilsK:

Moin,

 

du hast gesehen, dass dies ein deutschsprachiges Forum ist?

 

Zu deiner Frage: Wenn deine Codefragmente zutreffen, dann hast du in die Variable $WindowName den String "objForm" geschrieben. Diesen String kannst du natürlich in der Funktion auch wieder ausgeben. Da es aber nun mal kein Objekt ist, gibt es auch keine Methoden.

 

Gruß, Nils

 

Ja, danke - habe ich gesehen und meine Frage gerade abgeändert :-)

Aber die Antwort hilft mir nicht so wirklich, um ehrlich zu sein - was muss ich denn anstellen, damit ich meine gewünschte Ausgabe hinbekomme?

... auch wenn ich denkompletten Befehl, den ich in der Funktion brauche im Hauptfenster als String in eine Variable schreibe

 

$done_show = 'objForm.Controls.Add($done)'

und diese Variable dann an die Funktion übergebe

 

$btn5.add_Click{(done $done_show)}

abgeänderte Funktion sieht dann so aus

function done
{
param ($done_show)

$done = New-Object System.Windows.Forms.Button
$doneFont = New-Object System.Drawing.Font("Calibri",18,[System.Drawing.FontStyle]::Bold)
$done.Font = $doneFont
$done.Location = New-Object System.Drawing.Size(320,200)
$done.Size = New-Object System.Drawing.Size(150,50)
$done.ForeColor="red"
$done.Text = "DONE!" 


write-host $done_show
$done_show

$done.Add_Click({
                
                $WindowName.Controls.Remove($done),
                $WindowName.Controls.refresh()
                            })  
$done_show
}

wird beim Ausführen der write-host-Befehl richtig ausgeführt 

aber die letzte Zeile ($done_show) wird einfach ignoriert. Ich brauche aber als letzte ausgeführte Zeile ein

 

objForm.Controls.Add($done)

 

 

Share this post


Link to post

Moin,

 

der Weg ist vom Prinzip her genau umgekehrt. ;-) In der PowerShell arbeitest du - anders als in der CMD-Shell oder anderen herkömmlichen Shell - primär mit Objekten, nicht mit Zeichenketten. Du kannst einer Variable also ein Objekt zuweisen, dann steht diese Variable für das Objekt. Das tust du aber nicht - indem du Anführungsstriche setzt, weist du deinen Variablen eben Strings zu.

 

Ich habe keine Zeit, das für dein Skript durchzuprüfen, aber da scheint mir das Problem zu liegen. Im ersten Post enthält $objForm (vermutlich) ein Windows-Forms-Objekt (und nicht den Namen - das Objekt selbst!). Die Variable $WindowName hingegen enthält den String "objForm". Im zweiten Post weist du der Variablen $done_show den String "objForm.Controls.Add($done)" zu.

 

Vermutlich ist das nicht das, was du eigentlich willst. Ich nehme eher an, dass $WindowName den Namen des Forms-Objekts erhalten soll. Das wäre dann vermutlich:

$WindowName = objForm.Name

Und wenn $done_show das Ergebnis der Add()-Methode enthalten soll, hieße das wahrscheinlich

$done_show = objForm.Controls.Add($done)

Ungetestet, wie gesagt. Ich hoffe, du verstehst den Punkt.

 

Gruß, Nils

 

Share this post


Link to post

nein, sorry. Ich glaube dass Du hier nicht richtig liegst.

das Objekt objForm hat keine Methode .Name. Es gibt also kein objForm.Name

Mit der Deklaration $objForm = New-Object System.Windows.Forms.Form habe ich ein neues Fenster-Objekt generiert und diesem direkt den Namen  $objForm zugewiesen

 

Ich habe die Variable schon absichtlich mit einem String gefüllt, damit ich später in der Funktion diesen String als Teils des Befehls wieder nutzen kann, den ich absetzen möchte...

 

Ich habe das jetzt so gelöst, dass ich den kompletten Befehl in die Variable geschrieben habe 

$done_show = {$objForm.Controls.Add($done)}

 

und in der Funktion den Befehl mit

. $done_show

ausführe

 

Share this post


Link to post
vor einer Stunde schrieb newbi2009:

Ich glaube dass Du hier nicht richtig liegst.

Also, dann lassen wir mal "glauben" weg und kommen zu Tatsachen ;-) 

 

Die Basis:

  1. Funktions- und Variablennamen sollen eindeutig und selbsterklärend sein
  2. Der Typ einer Variable muss stets klar sein
  3. Powershell definiert den Variablen-Typ nach der letzten Zuweisung

 

vor 3 Stunden schrieb newbi2009:

'objForm' an die Variable $WindowName übergeben

Powershell hat gewissen Normen. Die '' definieren einen [string]. Heißt $WindowsName ist auch eine Variable des Typ´s [string], gesetzt durch deine Übergabe ''.

 

Übrigens hat 'objForm' rein gar nichts zu tun mit $objForm! Das erste ist ein Typ String, das Zweite eine Variable des Typ´s Form.

 

Technet | How to Add a PowerShell GUI Event Handler (Part 1)

https://social.technet.microsoft.com/wiki/contents/articles/25911.how-to-add-a-powershell-gui-event-handler-part-1.aspx

 

$done.Add_Click({
            $WindowName.Controls.Remove($done),
            $WindowName.Controls.refresh()
    })
    
$WindowName.Controls.Add($done)

Der Fehler besteht darin, dass $WindowName vom Typ String keine Möglichkeit hat ein Control vom Typ Button hinzuzufügen. Das ist auch Banane. Des Weiteren existiert die Variable $WindowName nicht in deiner Funktion Add_Click.

 

Erläutere uns doch bitte mal was du genau erreichen willst, denn ich verstehe den Sinn dahinter leider nicht. Dann kann ich Dir sinnvolle Änderungen empfehlen.

 

PS: @NilsK lag nicht "nicht richtig" ;-) 

PPS: Setze das mal mit in dein Skript am Anfang. Zum programmieren lernen ist das optimaler.

Set-StrictMode -Version 5

 

 

NACHTRAG

 

Sinnig wäre folgendes Codebeispiel:

Set-StrictMode -Version 3


#
# FUNCTIONS
#

function New-Button
{  
    param
    (
        [Parameter(Mandatory)]
        [string]
        $buttonText
    )
    
    # Generiere Objekt
    $myButton = New-Object System.Windows.Forms.Button
    
    # Konfiguriere Objekt
    $myButton.Font = [System.Drawing.Font]::new("Calibri",18,[System.Drawing.FontStyle]::Bold)
    $myButton.Location = [System.Drawing.Point]::new(320,200)
    $myButton.Size = [System.Drawing.Size]::new(150,50)
    $myButton.ForeColor = [System.Drawing.Color]::Red
    $myButton.Text = $buttonText
    
    # Füge Aktion hinzu
    $myButton.Add_Click({ [System.Windows.Forms.MessageBox]::Show("Du hast mich geklickt!", "Hello World") })
        
    # Übergebe Objekt
    return $myButton
}



#
# INIT OBJECTS
#

# Build Form
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "My Form"
$Form.Size = New-Object System.Drawing.Size(200,200)
$Form.StartPosition = "CenterScreen"
$Form.Topmost = $True


#
# SCRIPT
#

$myNewButton = New-Button('Klick mich!')
$Form.controls.Add($myNewButton)
$form.ShowDialog()

PS: Einen Schnitzer hast du noch bei "Location". Dort wird kein Size-Objekt, sondern ein Point-Objekt erwartet.

Edited by MurdocX

Share this post


Link to post

Oha - Ich habe echt noch soooo viel zu lernen!!! Danke schonmal, dass Ihr Euch die Zeit nehmt! Ich versuche das alles einmal von vorne zu erklären:

 

Ich habe edas folgende Skript, das so funktioniert, wie es soll

 

$objForm = New-Object System.Windows.Forms.Form                                        
$objForm.Backcolor="white"                                                             
$objForm.StartPosition = "CenterScreen"                                                
$objForm.Size = New-Object System.Drawing.Size(800,500)                                
$objForm.Text = "Mein neues Fenster"                                                   

$Close = New-Object System.Windows.Forms.Button    
$Close.Backcolor="lightblue"
$Close.Location = New-Object System.Drawing.Size(1,1)
$Close.Size = New-Object System.Drawing.Size(200,50)
$Close.Text = "Fenster schließen"
$Close.Name = "Abbrechen"
$Close.DialogResult = "Cancel" 
$Close.Add_Click({$objForm.Close()})
$objForm.Controls.Add($Close)

$btn1 = New-Object System.Windows.Forms.Button
$btn1.Location = New-Object System.Drawing.Size(530,190)
$btn1.Size = New-Object System.Drawing.Size(150,50)
$btn1.Text = "Funktion auslagern-Test" 
$btn1.UseVisualStyleBackColor = $True 
$btn1.add_Click{($objForm.Controls.Add($done))}
$objForm.Controls.Add($btn1) 

$done = New-Object System.Windows.Forms.Button
$doneFont = New-Object System.Drawing.Font("Calibri",18,[System.Drawing.FontStyle]::Bold)
$done.Font = $doneFont
$done.Location = New-Object System.Drawing.Size(320,200)
$done.Size = New-Object System.Drawing.Size(150,50)
$done.ForeColor="red"
$done.Text = "DONE!" 
$done.Add_Click({
                $objForm.Controls.Remove($done),
                $objForm.Controls.refresh()
                            })  

$objForm.ShowDialog() 

Jetzt möchte ich, um die Code-Menge in dieser Datei zu verringern und um den "done-Knopf" auch aus anderen Fenstern zu benutzen (dieses Fenster heisst $objForm das nächste Fenster heisst vielleicht $objForm2), ohne den Quellcode für den Knop immer wieder neu zu tippen, in eine Funktion auslagern, die in einer eigenen Datei abgelegt seiin soll (D:\temp\Knopf.ps1)    

 

Dazu habe ich 2 Dateien erstellt. Die Erste Datei ist diese:

 

. D:\temp\done-Knopf-Ver1.ps1

$objForm = New-Object System.Windows.Forms.Form                                        
$objForm.Backcolor="white"                                                             
$objForm.StartPosition = "CenterScreen"                                                
$objForm.Size = New-Object System.Drawing.Size(800,500)                                
$objForm.Text = "Mein neues Fenster"                                                   

$Close = New-Object System.Windows.Forms.Button    
$Close.Backcolor="lightblue"
$Close.Location = New-Object System.Drawing.Size(1,1)
$Close.Size = New-Object System.Drawing.Size(200,50)
$Close.Text = "Fenster schließen"
$Close.Name = "Abbrechen"
$Close.DialogResult = "Cancel" 
$Close.Add_Click({$objForm.Close()})
$objForm.Controls.Add($Close)

$btn1 = New-Object System.Windows.Forms.Button
$btn1.Location = New-Object System.Drawing.Size(530,190)
$btn1.Size = New-Object System.Drawing.Size(150,50)
$btn1.Text = "Funktion auslagern-Test" 
$btn1.UseVisualStyleBackColor = $True 
$btn1.add_Click{(done)}
$objForm.Controls.Add($btn1) 

$objForm.ShowDialog()  

         und die dazu gehörige "Funktions-Datei" (D:\Temp\done-Knopf-ver1.ps1)  sieht so aus:

 

function done()
{
$done = New-Object System.Windows.Forms.Button
$doneFont = New-Object System.Drawing.Font("Calibri",18,[System.Drawing.FontStyle]::Bold)
$done.Font = $doneFont
$done.Location = New-Object System.Drawing.Size(320,200)
$done.Size = New-Object System.Drawing.Size(150,50)
$done.ForeColor="red"
$done.Text = "DONE!" 

$done.Add_Click({
                $objForm.Controls.Remove($done),
                $objForm.Controls.refresh()
                            })  
 
 $objForm.Controls.Add($done)

}

Hier habe ich noch im "Controls.Add und Remove Statement" manuell den Fensternamen des Fensters eingetragen, indem der Knopf erscheinen soll ($objForm). Dies soll aber ja auch für andere Fenster funktionieren, also möchte ich den Fensternamen an diese Funktion beim Aufruf mit übergeben, damit dies auch für z.B. das Fenster "$objForm2" funktioniert!

Allerdings habe ich hier bereits das Problem, dass zwar das 

$objForm.Controls.Add($done) 

funktioniert, aber das Ausblenden beim betätigen des Knopfes

 

$objForm.Controls.Remove($done)

funktioniert schon nicht mehr- was ich nicht verstehe.....

 

So im letzten Schritt - und das ist das Problem, das ich ursprünglich gepostet habe, möchte ich den aktuellen Fenstername an die Funktion done-Funktion übergeben, damit ich diese aus verschiedenen Fenstern heraus benutzen kann.....

 

Ich hoffe, mein Problem ist jetzt klarer dargestellt?!

 

Nochmal vielen Dank für Eure Hilfe!!

Gruß

Holger                                             

Share this post


Link to post

Moin,

 

allgemeiner Hinweis: Mir scheint, dass du dir zu viel auf einmal vornimmst: GUI, Funktionen, Moduldateien. Aus meiner Erfahrung ist es sinnvoller, wenn du dir die Grundlagen, in denen du ja anscheinend auch noch nicht sicher bist, Schritt für Schritt aneignest.

 

Gruß, Nils

 

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


Werbepartner:



×
×
  • Create New...