Powershell GUI - Elemente einer Listbox weiter verwenden?
Hallo.
Ich bin dabei eine GUI für meine Arbeitskollegen zu schreiben um deren Arbeit am PC zu erleichtern.
Der Aufbau der GUI mit WinForms funktioniert so:
Der Anwender klickt auf Überprüfung starten, dann werden alle Dateien die eventuell kopiert werden müssen.
Diese Dateien bzw. deren Namen werden mit Formatierung (und Informationen) in einer Listbox gespeichert.
Mit klick auf "Ausgewähltes kopieren" soll / sollen die ausgewählte / ausgewählten Dateien dann kopiert werden.
Mein Problem:
Dadurch dass ich nicht den ganzen Pfad in der Listbox darstelle und speichere, sondern, nur einen Teil mit Formatierung, weiß ich nicht wirklich wie ich das weiter verwenden soll. Kann mir da vielleicht jemand weiterhelfen?
Die ganze Funktion kann ich leider nicht schreiben, weil die Zeichenlänge zu lang ist. Der Teil mit dem die Listbox gefüllt wird lautet:
[void]$ListBox.Items.Add("Maschinen-Nr.: $(Split-Path $_.Directory -Leaf) // Programm-Name: $($_.Name)")
Hätte gedacht dass ich zusätzlich zum Listbox eintrag noch eine Array befülle, aber woher weiß ich dann bei "xxxx.selected-items" welchen eintrag ich aus dem zusätzlich befüllten Array verwenden soll?
So sieht die Gui aus:
Wäre echt toll wenn mir da jemand helfen könnte.
1 Antwort
In Powershell lässt sich so wunderbar mit Objekten arbeiten. Man kann was hinzufügen oder entfernen oder sich nur auf eine Teil des Objekts beziehen.. oder gar etwas völlig neues kreieren.
Du brauchst lediglich zu Deinen mit Get-Childitem eingelesenen und mit Select ausgewählten Properties ein Neue hinzufügen (den KurzNamen /MaschinNummer oder Whatever)
Damit hältst Du alle Daten in einer intexierbaren Liste/Array zusammen. danach brauchst Du dich nicht mehr um den Zusemenhang zu kümmern du sprichst lediglich die Elemente über den Index der gewählten Objekte an.
Deiner Listbox übergibst du nur das, was man sehen soll. Welche Daten da sonst noch dranhängen ist unwichtig, wenn Du was auswählst, kennt die Listbox die Inidizes (Indexe) ...und die ButtonAktion nimmt sich diese Nummern und macht was mit der Liste.
(falls jemandem aufstößt, das ich besser mit Bindings hätte arbeiten können... Ich wollte der Einfachheit halber bewusst nicht noch eine Schicht reinbringen . Darum knallharte Direktzugriffe)
using namespace System.Windows.Forms
Add-Type -a System.Windows.Forms , System.Drawing
#Rohdaten ... mit Arraylist lässt sich gezielter arbeiten als mit PowershellArrays
[System.Collections.ArrayList]$TableData = @(Get-ChildItem $env:Temp -file)|
Select-Object Name,Directory,Fullname|
Foreach-Object {
$_MyShortString = (($_.FullName -split '\\')|select -last 2) -join '\'
$_|Add-Member -NotePropertyName Shorty -NotePropertyValue $_MyShortString #neue Property und deren wert hizufügen
$_
}
$TableData|ft #nur mal etwas Datengucken
$Form = New-Object Form
$Form.Size = '900,300'
$ListBox = New-Object ListBox
$ListBox.Size = '700,200'
$ListBox.Location = '10,30'
$ListBox.DataSource = $TableData.Shorty; #wir schmeißen einfach knallhart ale Shortys rein (powershell regelt das schon ;p )
$ListBox.SelectionMode = 'MultiExtended'
$Form.Controls.Add($ListBox)
$Button1 = New-Object Button
$Button1.Size = '150,25'
$Button1.Location = '720,30'
$Button1.Text = 'Show Path in Console'
$Button1_Action = {
$ListBox.SelectedIndices|
Sort-Object -Descending| #...mit höchstem Index beginnen... (hier nicht zwingend nötig)
Where-Object {
Write-Host $_
Write-Host $TableData[$_].Shorty -fo Red #der Index zeigt auf die Arrayelemente der DatenQuelle.
Write-Host $TableData[$_].FullName -fo Magenta
}
}
$Button1.Add_Click($Button1_Action)
$Form.Controls.Add($Button1)
$Button2 = New-Object Button
$Button2.Size = '150,25'
$Button2.Location = '720,60'
$Button2.Text = 'remove selected'
$Button2_Action = {
$ListBox.SelectedIndices|
Sort-Object -Descending| # wichtig!!! hier ist es wirklich nötig Array/List -Aktionen von oben zu beginnen, da sich sonst der Index innerhalb des Ziels bei jeder Einzelaktion verändert
Where-Object {
Write-Host $_
#hier was mit den durch die Daten in der Quelle[Index] represntierten Objekten passiern soll
#das kannst Du im prinzip auch als Job erledigen
#wichtig , das muss passieren bevor Du den Eintrag entfenst in der Datenquelle entfernst!!!!
Write-Host ('Tue so als würde ich die Datei: "{0}" im Ordner {1} loeschen' -f $TableData[$_].Name,$TableData[$_].Directory) -fo red
$TableData.RemoveAt($_) # Eintrag aus der Liste entfernen der Index zeigt auf die Arrayelemente der DatenQuelle.
}
#das folgende hat nichs mit Get_Item oder anderen Physischne Datenquellen zu tun , sondern bezieht sich lediglich auf die zu Anfang erstellte Liste/Array
#..mit geänderter Datenquelle sychronisieren
$ListBox.DataSource = $TableData.Shorty;
}
$Button2.Add_Click($Button2_Action)
$Form.Controls.Add($Button2)
$Form.ShowDialog()
Write-Host So sieht unsere Arraylist am ende aus: -fo Blue
sleep 2
$TableData|ft
pause
etwa blöd kommt mir die Listbox trotzdem vor...
Forms hat auch eine wunderbare Tabelle, welche sich automatisch an die übergebenen Daten anpasst. DataGridView
Man kann sich die Spalten zurechtziehen, wie auch beim Explorer. Im folgenden habe ich die Selekttirbarkeit von Einzelzellen blockiert, weil wir das für Deine Ansprüche nicht benötigen.
using namespace System.Windows.Forms
Add-Type -a System.Windows.Forms , System.Drawing
#DataGridView will kein Normales Powershell [Array] !
[System.Collections.ArrayList]$TableData = @(Get-ChildItem $env:Temp -file)|
Foreach-Object {
$_MyShortString = (($_.FullName -split '\\')|select -last 2) -join '\'
$_|Add-Member -NotePropertyName Shorty -NotePropertyValue $_MyShortString #neue Property und deren wert hizufügen
$_
}|
Select-Object Shorty,Name,Directory,FullName,Length,CreationTime,LastAccessTime,LastWriteTime #was Du von den ganzen Korb an Properties in der Tabelle anzeigen willst
$TableData|ft
$Form = New-Object Form
$Form.Size = '900,300'
$DataGridView = New-Object DataGridView
$DataGridView.Size = '700,200'
$DataGridView.Location = '10,30'
$DataGridView.DataSource = $TableData
$DataGridView.ColumnHeadersVisible = $true
$DataGridView.ReadOnly = $true
$dataGridView.SelectionMode = 'FullRowSelect'
$Form.Controls.Add($DataGridView)
$Button1 = New-Object Button
$Button1.Size = '150,25'
$Button1.Location = '720,30'
$Button1.Text = 'Show Path in Console'
$Button1_Action = {
$Datagridview.SelectedRows|
Sort-Object Index -Descending| #Array-Aktionen möglichst immer mit dem höchsten Index beginnen
Where-Object {
Write-Host $_.Index
Write-Host $Datagridview.DataSource[$_.Index].Name -fo Red #der Index zeigt auf die Arrayelemente der DatenQuelle.
Write-Host $Datagridview.DataSource[$_.Index].Directory -fo Magenta
}
}
$Button1.Add_Click($Button1_Action)
$Form.Controls.Add($Button1)
$Button2 = New-Object Button
$Button2.Size = '150,25'
$Button2.Location = '720,60'
$Button2.Text = 'remove selected'
$Button2_Action = {
$Datagridview.SelectedRows|
Sort-Object Index -Descending| # wichtig!!! hier ist es wirklich nötig Array/List -Aktionen von oben zu beginnen, da sich sonst der Index innerhalb des Ziels bei jeder Einzelaktion verändert
Where-Object {
Write-Host selectet index: $_.Index
#hier was mit den durch die Daten in der Quelle[Index] represntierten Objekten passiern soll
#das kannst Du im prinzip auch als Job erledigen
#wichtig , das muss passieren bevor Du den Eintrag entfenst
Write-Host ('Tue so als würde ich die Datei: "{0}" im Ordner {1} loeschen' -f $Datagridview.DataSource[$_.Index].Name,$Datagridview.DataSource[$_.Index].Directory) -fo red
Write-Host ('Neee: "{0}" ist nich geloescht' -f $Datagridview.DataSource[$_.Index].FullName) -fo green
$Datagridview.DataSource.RemoveAt($_.Index) # Eintrag entfernen der Index zeigt auf die Arrayelemente der DatenQuelle.
}
#das folgende hat nichs mit Get_Item oder anderen Physischne Datenquellen zu tun , sondern beziht sich lediglich auf die zu Anfang erstellte Liste/Array
#narrensichere Methode, da Datagridview sonst versuchen würde Nicht mehr vorhandene Zellen einzeln abzugrasen...
$DataGridView.DataSource = $null; #...kurz von der Datenquelle lösen
$DataGridView.Update();
$DataGridView.DataSource = $TableData #Datenquelle wieder zuweisen
}
$Button2.Add_Click($Button2_Action)
$Form.Controls.Add($Button2)
$Form.ShowDialog()
Write-Host So sieht unsere Arraylist am ende aus: -fo Blue
sleep 2
$TableData|ft
pause
DataGridView ist eines der mächtigsten Controls über das Forms verfügt. Ich habe aus Gründen der Übersichtlichkeit des Codes auf das Ausblenden von Spalten oder /und vorgabe von Spalrenbreiten und den Ganzen Kram verzichtet.
Ich dachte nur das es interessant sein könnte zu demonstrieren, wie einfach sich dieses Control an die gelieferten Daten anpasst. Wenn Du die Auswahl/ Reihenfolge der Properties von Select in Tabledata änderst, wirkt sich das automatisch auch auf die GridView aus.
Theoretisch kannst Du auch nicht anzuzeigende Daten in einem zweiten Array verwalten. Dann musst Du aber dafür sorgen, das diese synchron verwaltet werden... ( mache ich ja im prinzp schon bei der Listbox... Diese bekommt ja auch nur die "Shoortys" zu sehen. Obwohl da viel mehr Daten in den Objekten sind.)
Du kannst aber auch die Gridview statisch definieren und die Properties den Spalten zuweisen.
Hallo "Erzesel".
Wie immer eine Antwort die mit beispielloser Detailgenauigkeit verfasst wurde.
Die meisten Menschen würden diese Zeit nicht mal für einen Freund opfern, und du tust das immer wieder um einem Fremden (würde gern einer sein) Programmierer auf die Sprünge zu helfen.
Zum Thema:
Version 1 deiner Antwort ist genau das wie ich es mir vorgestellt habe, aber bin am überlegen ob ich nicht deine zweite Version verwende, da man hier schön den letzten Zufgriff sieht.
Ich muss in der Arbeit mal erfragen ob es ihnen nicht zuviele Informationen sind.
Auf jeden Fall werd ich es jetzt mal Schritt für Schritt nachvollziehen damit ich es nicht nur kopiere sondern auch verstehe.
Vielen Vielen Dank für die Hilfe
Hochachtungsvoll
Gruß Thomas