Powershell Funktion zum Konvertieren einer Datatable zu einem Array
Hallo,
ich habe ein Datagridview mit einer Datatable als Quelle. Bei dieser kann Einträge hinzufügen, verändern und löschen. Wenn ich mir zur weiteren Verarbeitung die Daten des Datagridviews bzw. der Datatable ausgeben möchte (Write-Host $Datatable) dann erhalte ich pro Zeile die Ausgabe System.Data.DataRow. Damit kann ich natürlich nichts anfangen.
Vor wenigen Jahren hatte ich ein ähnliches Projekt, wobei statt einen DGV ein ListView verwendet wurde. Dort habe ich dann die Elemente des LV durchiteriert und diese einem Array hinzugefügt. Das hat soweit ganz gut funktioniert. Der Nachteil an dem Code war jedoch das ich pro Listview den Code für das durchiterieren setzen muss.
Für das DGV möchte ich das durchiterieren, sofern es notwendig ist, gerne in eine Funktion auslagern, bei der ich das DGV oder die Datatable übergebe und als Rückgabe ein Array mit den Werten erhalte. Mit dieser Funktion habe ich bereits angefangen. Aktuell übergebe ich das DGV (wobei das keinen Unterschied zur Datatable sein sollte) und kann in der Funktion mit dem DGV arbeiten. So habe ich mir die Anzahl der Spalten ausgeben lassen.
Nun stehe ich jedoch vor dem Problem folgendes variable zu gestalten:
Das Problem ist das ich mir via $dt.Columns die Anzahl der Spalten und deren Bezeichnung ausgeben kann. Wie aber kann ich dieses variable gestalten, sodass die Property Eigenschaft des New-Object den Spalten entsprechend lang wird. Hintergrund ist das ich mal eine Datatable mit einer unterschiedlichen Spaltenanzahl unterbringe und den Code nicht pro Datatable vorhalten möchte - zumal ich doppelt editieren müsste, wenn sich mal eine Spalte umbenennt.
ich habe ein Datagridview mit einer Datatable als Quelle. Bei dieser kann Einträge hinzufügen, verändern und löschen. Wenn ich mir zur weiteren Verarbeitung die Daten des Datagridviews bzw. der Datatable ausgeben möchte (Write-Host $Datatable) dann erhalte ich pro Zeile die Ausgabe System.Data.DataRow. Damit kann ich natürlich nichts anfangen.
Vor wenigen Jahren hatte ich ein ähnliches Projekt, wobei statt einen DGV ein ListView verwendet wurde. Dort habe ich dann die Elemente des LV durchiteriert und diese einem Array hinzugefügt. Das hat soweit ganz gut funktioniert. Der Nachteil an dem Code war jedoch das ich pro Listview den Code für das durchiterieren setzen muss.
Für das DGV möchte ich das durchiterieren, sofern es notwendig ist, gerne in eine Funktion auslagern, bei der ich das DGV oder die Datatable übergebe und als Rückgabe ein Array mit den Werten erhalte. Mit dieser Funktion habe ich bereits angefangen. Aktuell übergebe ich das DGV (wobei das keinen Unterschied zur Datatable sein sollte) und kann in der Funktion mit dem DGV arbeiten. So habe ich mir die Anzahl der Spalten ausgeben lassen.
Nun stehe ich jedoch vor dem Problem folgendes variable zu gestalten:
$dt.Rows | %{
$cols = $_.ItemArray
$ArrayDatatable += New-Object PSObject -Property @{"Titel1"=$cols;"Title2"=$cols[1];"Title3"=$cols[2];"Title4"=$cols[3]}
}
Das Problem ist das ich mir via $dt.Columns die Anzahl der Spalten und deren Bezeichnung ausgeben kann. Wie aber kann ich dieses variable gestalten, sodass die Property Eigenschaft des New-Object den Spalten entsprechend lang wird. Hintergrund ist das ich mal eine Datatable mit einer unterschiedlichen Spaltenanzahl unterbringe und den Code nicht pro Datatable vorhalten möchte - zumal ich doppelt editieren müsste, wenn sich mal eine Spalte umbenennt.
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 393184
Url: https://administrator.de/forum/powershell-funktion-zum-konvertieren-einer-datatable-zu-einem-array-393184.html
Ausgedruckt am: 25.12.2024 um 04:12 Uhr
10 Kommentare
Neuester Kommentar
Zitat von @derhoeppi:
Bei dieser kann Einträge hinzufügen, verändern und löschen. Wenn ich mir zur weiteren Verarbeitung die Daten des Datagridviews bzw. der Datatable ausgeben möchte (Write-Host $Datatable) dann erhalte ich pro Zeile die Ausgabe System.Data.DataRow. Damit kann ich natürlich nichts anfangen.
Logisch, eine Datatable besteht nun mal aus DataRows, deren Inhalt musst du nur anzeigen indem sie die Daten der Spalten abrufts .Bei dieser kann Einträge hinzufügen, verändern und löschen. Wenn ich mir zur weiteren Verarbeitung die Daten des Datagridviews bzw. der Datatable ausgeben möchte (Write-Host $Datatable) dann erhalte ich pro Zeile die Ausgabe System.Data.DataRow. Damit kann ich natürlich nichts anfangen.
Nun stehe ich jedoch vor dem Problem folgendes variable zu gestalten:
>
> $dt.Rows | %{
> $cols = $_.ItemArray
> $ArrayDatatable += New-Object PSObject -Property @{"Titel1"=$cols;"Title2"=$cols[1];"Title3"=$cols[2];"Title4"=$cols[3]}
> }
>
Das Problem ist das ich mir via $dt.Columns die Anzahl der Spalten und deren Bezeichnung ausgeben kann. Wie aber kann ich dieses variable gestalten, sodass die Property Eigenschaft des New-Object den Spalten entsprechend lang wird. Hintergrund ist das ich mal eine Datatable mit einer unterschiedlichen Spaltenanzahl unterbringe und den Code nicht pro Datatable vorhalten möchte - zumal ich doppelt editieren müsste, wenn sich mal eine Spalte umbenennt.
$dt.Rows | %{
$row = $_
$rht = @{}
0..($dt.Columns.Count-1) | %{$rht.($dt.Columns[$_]) = $row[$_]}
[pscustomobject]$rht
}
bekomme ich als Ausgabe jede Zeile in einem eigenen Array
Dann machst du einen Fehler oder hast deine Variable in der ISE noch als anderen Typ deklariert. Mein Hirn hinkt also momentan hinterher was der Unterschied zwischen "$ArrayDatatable += New-Object PSObject -Property @{"Titel1"=$cols;"Title2"=$cols[1];"Title3"=$cols[2];"Title4"=$cols[3]} " und dem "$ArrayDatatable += [pscustomobject]$rht" ist
Ist exakt das selbe, das letzte ist nur eine Abkürzung und seit PS3.0 eingeführt und etwas schneller beim Erzeugen. In deinem PSObject nutzt du ja auch eine Hashtable als Property. Heraus kommt bei beiden ein CustomObject, welches du dann einem Array hinzufügt, also alles kein Hexenwerk.Ziel ist jedoch eine kleine Tabelle aufzubauen, so dass es ebenso eine Tabelle ist als ob ich $test = get-process aufrufe.
Das ist keine Tabelle das sind immer Arrays aus Objekten!!Siehe das Beispiel
https://tio.run/##fZBBC4JAEIXv@yv2YKiQS3YPKjsX5FFEtpyKWF1xZxGxfrvtWkEhNY ...
Denn get-process etc liefern dir auch nur Arrays aus Objekten eben nur anderer Klassen, hier ist es nur eben nur ein CustomObject mit selbst definierten Eigenschaften und/oder Methoden.
Thema erledigt.
Zeile 38 ist dein Fehler. Die muss so aussehen:
Du willst ja ein Array für die Spalten übergeben und nicht 4 Parameter.
Außerdem ist Zeile 58 überflüssig.
Alles in allem ist das doch viel zu umständlich gedacht was du da machst. Eine Hashtable die mit einem Datagridview verbunden ist, ist automatisch synchron wenn man das Datagrid und dessen Items verändert! Da hatte mein Vorposter in deinem letzten Thread absolut recht.
[void]$dtValues.Rows.Add(@("1", "2", "3", "4"))
Außerdem ist Zeile 58 überflüssig.
Alles in allem ist das doch viel zu umständlich gedacht was du da machst. Eine Hashtable die mit einem Datagridview verbunden ist, ist automatisch synchron wenn man das Datagrid und dessen Items verändert! Da hatte mein Vorposter in deinem letzten Thread absolut recht.
Zitat von @derhoeppi:
Das heißt es liegt nur an dieser einen Zeile. Danke - aber was lief denn dann falsch, wenn ich Einträge über den Add Button hinzufüge? Diese wurden auch nicht vernünftig hinzugefügt.
Wenn ich ganz ehrlich bin, habe ich die Antwort in meinem letzten Thread nicht ganz verstanden. Ich habe meine Datatable als Datasource im DGV verknüpft. In der Antwort vom letzten Thread habe stand, dass ich ein Binding aktivieren soll. Wo aber ist der Unterschied? Aus den verlinkten URL's bin ich ebenso nicht schlau geworden.
Vielleicht kannst du es mir kurz erläutern. Am liebsten möchte ich nur die Datatable pflegen und analog einem Array Werte hinzufügen, verändern und löschen. Das habe ich zwar inzwischen hinbekommen, aber bei der Ausgabe von der Datatable habe ich immer nur system.data.datarow als Wert erhalten. Wenn ich mir aber Artikel bei MXFAQ ansehe, funktioniert es analog einem Array. Und genau das habe ich nicht hinbekommen. Deshalb bin ich jetzt wieder den Umweg über ein separates Array gegangen.
Das heißt es liegt nur an dieser einen Zeile. Danke - aber was lief denn dann falsch, wenn ich Einträge über den Add Button hinzufüge? Diese wurden auch nicht vernünftig hinzugefügt.
Wenn ich ganz ehrlich bin, habe ich die Antwort in meinem letzten Thread nicht ganz verstanden. Ich habe meine Datatable als Datasource im DGV verknüpft. In der Antwort vom letzten Thread habe stand, dass ich ein Binding aktivieren soll. Wo aber ist der Unterschied? Aus den verlinkten URL's bin ich ebenso nicht schlau geworden.
Vielleicht kannst du es mir kurz erläutern. Am liebsten möchte ich nur die Datatable pflegen und analog einem Array Werte hinzufügen, verändern und löschen. Das habe ich zwar inzwischen hinbekommen, aber bei der Ausgabe von der Datatable habe ich immer nur system.data.datarow als Wert erhalten. Wenn ich mir aber Artikel bei MXFAQ ansehe, funktioniert es analog einem Array. Und genau das habe ich nicht hinbekommen. Deshalb bin ich jetzt wieder den Umweg über ein separates Array gegangen.
Du scheinst das mit der Objektorientierten Programmierung noch nicht ganz verinnerlicht zu haben. Eine Datatable ist ein Objekt, das hat Eigenschaften wie die Rows Eigenschaft die selbst ein Array ist, also Objekte vom Typ DataRow beinhaltet, dieses kannst du in Powershell wie gehabt entweder mit Schleifen durchlaufen oder auch direkt mit Index [n] hinter der Eigenschaft ansprechen. Die Spaltennamen der Datarows kannst du in Powershell direkt mit Ihren Namen als Eigenschaft des Objekts ansprechen.
Hier mal zusammengefasst, das siehst du das wie es geht: Füge ein paar Rows hinzu und klicke dann auf irgendeine Zeile im DGV doppelt mit der Maus dann bekommst du Beispiele der verknüpften Datatable aus dem Gridview angezeigt. Daran siehst du das die Datatable immer synchron mit dem DGV ist wenn sie per DataSource verknüpft ist.
function Show-DatagridviewExampleDatatable_psf {
#----------------------------------------------
#region Import the Assemblies
#----------------------------------------------
[void][reflection.assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
[void][reflection.assembly]::Load('System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
#endregion Import Assemblies
#----------------------------------------------
#region Generated Form Objects
#----------------------------------------------
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object 'System.Windows.Forms.Form'
$buttonEdit = New-Object 'System.Windows.Forms.Button'
$buttonAdd = New-Object 'System.Windows.Forms.Button'
$buttonDelete = New-Object System.Windows.Forms.Button
$labelValue2 = New-Object 'System.Windows.Forms.Label'
$labelValue3 = New-Object 'System.Windows.Forms.Label'
$labelValue1 = New-Object 'System.Windows.Forms.Label'
$textboxValue2 = New-Object 'System.Windows.Forms.TextBox'
$textboxValue3 = New-Object 'System.Windows.Forms.TextBox'
$textboxValue1 = New-Object 'System.Windows.Forms.TextBox'
$datagridviewProjectUser = New-Object 'System.Windows.Forms.DataGridView'
$buttonClose = New-Object 'System.Windows.Forms.Button'
$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
#endregion Generated Form Objects
#----------------------------------------------
# User Generated Script
#----------------------------------------------
$global:cEditIndex = -1
#Create datatable as datasource for datagridview
$dtValues = New-Object System.Data.DataTable
$dtValues.Columns.Add('Value1', [string])
$dtValues.Columns.Add('Value2', [string])
$dtValues.Columns.Add('Value3', [string])
$datagridviewProjectUser.DataSource = $dtValues
$form1_Load={
}
$buttonDelete_Click = {
$datagridviewProjectUser.SelectedRows | %{$datagridviewProjectUser.Rows.RemoveAt($_.Index)}
}
$buttonEdit_Click={
IF ($buttonEdit.Text -eq "Edit")
{
# das ist der Index des DG den wir bearbeiten
$global:cEditIndex = $datagridviewProjectUser.SelectedRows.Index
$textboxValue1.Text = $datagridviewProjectUser.SelectedRows.Cells.Value
$textboxValue2.Text = $datagridviewProjectUser.SelectedRows.Cells[1].Value
$textboxValue3.Text = $datagridviewProjectUser.SelectedRows.Cells[2].Value
$buttonAdd.Text = "Abort"
$buttonEdit.Text = "Save"
}
ELSE
{
$datagridviewProjectUser.Rows[$global:cEditIndex].Cells.Value = $textboxValue1.Text
$datagridviewProjectUser.Rows[$global:cEditIndex].Cells[1].Value = $textboxValue2.Text
$datagridviewProjectUser.Rows[$global:cEditIndex].Cells[2].Value = $textboxValue3.Text
$textboxValue1.Text = ""
$textboxValue2.Text = ""
$textboxValue3.Text = ""
$buttonAdd.Text = "Add"
$buttonEdit.Text = "Edit"
}
}
$buttonAdd_Click={
IF ($buttonAdd.Text -eq "Add")
{
$ValuesAdd = $dtValues.NewRow()
$ValuesAdd['Value1'] = $textboxValue1.Text
$ValuesAdd['Value2'] = $textboxValue2.Text
$ValuesAdd['Value3'] = $textboxValue3.Text
$dtValues.Rows.Add($ValuesAdd)
$textboxValue1.Text = ""
$textboxValue2.Text = ""
$textboxValue3.Text = ""
}
ELSE
{
$textboxValue1.Text = ""
$textboxValue2.Text = ""
$textboxValue3.Text = ""
$buttonAdd.Text = "Add"
$buttonEdit.Text = "Edit"
}
}
# --End User Generated Script--
#----------------------------------------------
#region Generated Events
#----------------------------------------------
$Form_StateCorrection_Load=
{
#Correct the initial state of the form to prevent the .Net maximized form issue
$form1.WindowState = $InitialFormWindowState
}
$Form_Cleanup_FormClosed=
{
#Remove all event handlers from the controls
try
{
$buttonEdit.remove_Click($buttonEdit_Click)
$buttonAdd.remove_Click($buttonAdd_Click)
$form1.remove_Load($form1_Load)
$form1.remove_Load($Form_StateCorrection_Load)
$form1.remove_FormClosed($Form_Cleanup_FormClosed)
}
catch { Out-Null <# Prevent PSScriptAnalyzer warning #> }
}
#endregion Generated Events
#----------------------------------------------
#region Generated Form Code
#----------------------------------------------
$form1.SuspendLayout()
#
# form1
#
$form1.Controls.Add($buttonDelete)
$form1.Controls.Add($buttonEdit)
$form1.Controls.Add($buttonAdd)
$form1.Controls.Add($labelValue2)
$form1.Controls.Add($labelValue3)
$form1.Controls.Add($labelValue1)
$form1.Controls.Add($textboxValue2)
$form1.Controls.Add($textboxValue3)
$form1.Controls.Add($textboxValue1)
$form1.Controls.Add($datagridviewProjectUser)
$form1.Controls.Add($buttonClose)
$form1.AutoScaleDimensions = '6, 13'
$form1.AutoScaleMode = 'Font'
$form1.ClientSize = '606, 261'
$form1.MaximizeBox = $False
$form1.MinimizeBox = $False
$form1.Name = 'form1'
$form1.ShowIcon = $False
$form1.Text = 'Form'
$form1.add_Load($form1_Load)
#
# buttonDelete
#
$buttonDelete.Anchor = 'Bottom, Right'
$buttonDelete.Location = '310, 226'
$buttonDelete.Name = 'buttonEdit'
$buttonDelete.Size = '66, 23'
$buttonDelete.TabIndex = 25
$buttonDelete.Text = 'Delete'
$buttonDelete.UseCompatibleTextRendering = $True
$buttonDelete.UseVisualStyleBackColor = $True
$buttonDelete.add_Click($buttonDelete_Click)
$buttonDelete.Enabled = $false
#
# buttonEdit
#
$buttonEdit.Anchor = 'Bottom, Right'
$buttonEdit.Location = '385, 226'
$buttonEdit.Name = 'buttonEdit'
$buttonEdit.Size = '66, 23'
$buttonEdit.TabIndex = 25
$buttonEdit.Text = 'Edit'
$buttonEdit.UseCompatibleTextRendering = $True
$buttonEdit.UseVisualStyleBackColor = $True
$buttonEdit.add_Click($buttonEdit_Click)
$buttonEdit.Enabled = $false
#
# buttonAdd
#
$buttonAdd.Anchor = 'Bottom, Right'
$buttonAdd.Location = '457, 226'
$buttonAdd.Name = 'buttonAdd'
$buttonAdd.Size = '66, 23'
$buttonAdd.TabIndex = 24
$buttonAdd.Text = 'Add'
$buttonAdd.UseCompatibleTextRendering = $True
$buttonAdd.UseVisualStyleBackColor = $True
$buttonAdd.add_Click($buttonAdd_Click)
#
# labelValue2
#
$labelValue2.AutoSize = $True
$labelValue2.Location = '464, 69'
$labelValue2.Name = 'labelValue2'
$labelValue2.Size = '40, 17'
$labelValue2.TabIndex = 21
$labelValue2.Text = 'Value2'
$labelValue2.UseCompatibleTextRendering = $True
#
# labelValue3
#
$labelValue3.AutoSize = $True
$labelValue3.Location = '463, 115'
$labelValue3.Name = 'labelValue3'
$labelValue3.Size = '40, 17'
$labelValue3.TabIndex = 20
$labelValue3.Text = 'Value3'
$labelValue3.UseCompatibleTextRendering = $True
#
# labelValue1
#
$labelValue1.AutoSize = $True
$labelValue1.Location = '464, 26'
$labelValue1.Name = 'labelValue1'
$labelValue1.Size = '40, 17'
$labelValue1.TabIndex = 19
$labelValue1.Text = 'Value1'
$labelValue1.UseCompatibleTextRendering = $True
#
# textboxValue2
#
$textboxValue2.Location = '464, 89'
$textboxValue2.Name = 'textboxValue2'
$textboxValue2.Size = '121, 20'
$textboxValue2.TabIndex = 17
#
# textboxValue3
#
$textboxValue3.Location = '463, 135'
$textboxValue3.Name = 'textboxValue3'
$textboxValue3.Size = '122, 20'
$textboxValue3.TabIndex = 18
#
# textboxValue1
#
$textboxValue1.Location = '463, 46'
$textboxValue1.Name = 'textboxValue1'
$textboxValue1.Size = '122, 20'
$textboxValue1.TabIndex = 15
#
# datagridviewProjectUser
#
$datagridviewProjectUser.AllowUserToAddRows = $False
$datagridviewProjectUser.ColumnHeadersHeightSizeMode = 'AutoSize'
$datagridviewProjectUser.Location = '12, 12'
$datagridviewProjectUser.Name = 'datagridviewProjectUser'
$datagridviewProjectUser.ReadOnly = $True
$datagridviewProjectUser.SelectionMode = 'FullRowSelect'
$datagridviewProjectUser.ShowEditingIcon = $False
$datagridviewProjectUser.Size = '445, 206'
$datagridviewProjectUser.TabIndex = 13
$datagridviewProjectUser.add_SelectionChanged({
if ($datagridviewProjectUser.SelectedRows.Count -gt 0){
$buttonEdit.Enabled = $true
$buttonDelete.Enabled = $true
}else{
$buttonEdit.Enabled = $false
$buttonDelete.Enabled = $false
}
})
$datagridviewProjectUser.add_CellMouseDoubleClick({
if ($datagridviewProjectUser.SelectedRows.Count -gt 0){
[System.Windows.Forms.MessageBox]::Show("Current Datatable: `n`n" + ($dtValues | ft -Auto | out-string))
[System.Windows.Forms.MessageBox]::Show("Values from first column: `n`n" + ($dtValues.Rows.Value1 -join "`n"))
[System.Windows.Forms.MessageBox]::Show("Values from second column: `n`n" + ($dtValues.Rows.Value2 -join "`n"))
[System.Windows.Forms.MessageBox]::Show("Values from third column: `n`n" + ($dtValues.Rows.Value3 -join "`n"))
[System.Windows.Forms.MessageBox]::Show("Value from first item and second column: `n`n" + ($dtValues.Rows.Value2))
[System.Windows.Forms.MessageBox]::Show("Nu alles klar?")
}
})
#
# buttonClose
#
$buttonClose.Anchor = 'Bottom, Right'
$buttonClose.CausesValidation = $False
$buttonClose.DialogResult = 'Cancel'
$buttonClose.Location = '529, 226'
$buttonClose.Name = 'buttonClose'
$buttonClose.Size = '75, 23'
$buttonClose.TabIndex = 14
$buttonClose.Text = '&Close'
$buttonClose.UseCompatibleTextRendering = $True
$buttonClose.UseVisualStyleBackColor = $True
$form1.ResumeLayout()
#endregion Generated Form Code
#----------------------------------------------
#Save the initial state of the form
$InitialFormWindowState = $form1.WindowState
#Init the OnLoad event to correct the initial state of the form
$form1.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$form1.add_FormClosed($Form_Cleanup_FormClosed)
#Show the Form
return $form1.ShowDialog()
} #End Function
#Call the form
Show-DatagridviewExampleDatatable_psf | Out-Null
alles via Write-Host ausgeben
Fehler!Write-Host gibt nur das aus was ein Objekt zur Umwandlung in einen String mit toString() machen kann, wenn es nur ein Objekt ist wie in dem Fall eins vom Typ Datarow gibt es natürlich nur den Datentyp aus anstatt einer Tabelle. Weil Write-Host ja Strings erwartet und eine Datarow ist nun mal kein String sondern ein Objekt. Nur Objekte die explizit in Ihrer toString() Methode definiert haben was in dem Falle auszugeben ist, werden dir den reinen Inhalt per write-host anzeigen, deshalb also die Eigenschaften abrufen damit du den Inhalt bekommst.
Write-host musst du also mitteilen was du als String ausgeben willst ansonsten gibt es bei einem Objekt einfach nur den Klassentyp aus.
Scheinbar ist allein das mein Problem.
Das ist dann aber nur ein reines Verständnisproblem.p.s. : Seit ihr eigentlich hauptberuflich Entwickler / Programmierer?
Wer ist wir? Ist hier noch jemand ?