Datagridview nach csv / excel exportieren

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von DerSmurf.

    Datagridview nach csv / excel exportieren

    Hallo ihr lieben
    Ich habe eine Funktion, um Daten aus einem DGV (gefüllt mit gebundenem DataSet) nach csv zu exportieren.
    Hier sind jedoch die export Spalten und deren Reihenfolge fest im Code hinterlegt.
    Ich möchte hier ein paar Dinge ändern, die ich aber alleine nicht hinbekomme. Zum basteln habe ich mal ein Demoprojekt angehängt.

    1. möchte ich gerne den Export in csv UND / bzw. ODER xls ermöglichen. Das werde ich über EPPlus erledigen. Habe ich eh angebunden. Das bekomme ich hin.

    2. möchte ich gerne nicht die Daten aus der Bindingsource, sondern direkt aus dem DGV exportieren, damit ich die gleiche Sub für jedes Datagridview in meiner Form verwenden kann.
    Hierfür habe ich eine Klasse angelegt. Dort wollte ich eine Exportsub hinschmeißen. Diese kann ich dann von überall im Programm aufrufen, und übergebe nur das zu exportierende DGV.
    Ich bekomms aber nicht hin, die Sub aufzurufen (siehe Demoprojekt).

    3. Ich möchte die Spalten gerne Variabel haben. Das heißt der User soll sowohl die zu exportierenden Spalten, als auch deren Reihenfolge auswählen können.
    Hier habe ich mal ein Bild angehängt (wie ichs in meinem alten Programm realisiert habe) angehängt.
    Bilder
    • InkedUnbenannt_LI.jpg

      1,58 MB, 2.736×1.824, 149 mal angesehen
    Dateien
    • csv export.zip

      (300,59 kB, 101 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DerSmurf“ ()

    hier mal ein Ansatz
    die feinheiten überlassen ich dir

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Form1
    3. Dim dset As DataSet
    4. Private Function LoadData() As DataSet
    5. 'Beispiel Daten
    6. Dim dset As New DataSet
    7. Dim dtbl1 As New DataTable("dt1")
    8. dtbl1.Columns.Add(New DataColumn("ID", GetType(System.Int32)))
    9. dtbl1.Columns.Add(New DataColumn("col1", GetType(System.String)))
    10. dtbl1.Columns.Add(New DataColumn("col2", GetType(System.String)))
    11. dtbl1.Columns.Add(New DataColumn("col3", GetType(System.String)))
    12. dtbl1.Columns.Add(New DataColumn("col4", GetType(System.String)))
    13. dset.Tables.Add(dtbl1)
    14. For i As Integer = 1 To 10000
    15. If Rnd() > 0.2 Then
    16. Dim drow1 As DataRow = dtbl1.NewRow
    17. drow1!ID = i
    18. drow1!col1 = "Feld 1-" & i.ToString
    19. drow1!col2 = "Feld 2-" & i.ToString
    20. drow1!col3 = "Feld 3-" & i.ToString
    21. drow1!col4 = "Feld 4-" & i.ToString
    22. dtbl1.Rows.Add(drow1)
    23. End If
    24. Next
    25. Return dset
    26. End Function
    27. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    28. dset = LoadData()
    29. With DataGridView1
    30. .DataSource = dset
    31. .DataMember = "dt1"
    32. End With
    33. End Sub
    34. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    35. 'hier reihenfolge und welche Spalten du haben willst
    36. CSVExport("E:\TestFolder\TestExport.csv", dset.Tables(0), _
    37. New DataColumn() {dset.Tables(0).Columns(1), _
    38. dset.Tables(0).Columns(4)})
    39. End Sub
    40. Private Sub CSVExport(ByVal filename As String, ByVal table As DataTable, ByVal cols As DataColumn())
    41. Dim sw As New IO.StreamWriter(filename)
    42. For Each drow As DataRow In table.Rows
    43. For i As Integer = 0 To cols.GetUpperBound(0)
    44. sw.Write(drow(cols(i)).ToString & ";")
    45. Next
    46. sw.WriteLine()
    47. Next
    48. sw.Close()
    49. End Sub
    50. End Class
    Danke. Das hilft!
    Mein Problem ist jetzt nur, dass ich keine Ahnung habe, wie ich am sinnvollsten die Form auf dem angehängten Bild erstelle.
    Also Spaltennamen einfach als string? Oder gibt es etwas wie Dim Col as DGV.Column. Was dann eigenschaften wie Col.HeaderText hat?

    Und wie ich dann im Code am besten die ausgewählten Spalten durchgehe.
    So ihr lieben.
    Ich bin ein gutes Stück weitergekommen.
    Es gelingt mir, die zu exportierenden Spalten auswählbar zu machen. (Anzeige über DGV - Speicherung in List (of DatagridviewColumn) )
    Außerdem habe ich es geschafft, dass nur die Header der ausgewählten Spalten exportiert werden. Allerdings wird noch der gesamte Rest der csv exportiert.
    Also die korrekten Header, mit dem kompletten restlichen Inhalt der Rows.
    Hier komme ich nicht weiter, da ich keine passende Eigenschaft zu DGV.Row finde, die auf den Header oder irgendwas verweist.

    Außerdem hätte ich gerne eure Meinung zu meinem Ansatz.
    Die frmChooseColumns soll noch eine Möglichkeit bekommen, dass ausgewählte Einträge in DGVExportCells nach oben und unten verschoben werden können, außerdem sollen diese gelöscht werden können.
    Aber das müsste ja mit der List (of ..) problemlos klappen. - zumindest in meinen Gedanken.

    Oder gibt es einen alternativen (besseren) Lösungsansatz?
    Hier nochmal das Problem:
    Spoiler anzeigen
    Der User soll auswählen können, welche Spalten eines DGVs exportiert werden sollen.
    Export soll nach csv und Excel möglich sein (im Test hier erstmal nur csv Export, damit sich die Helfer nicht mit EPPlus rumschlagen müssen.
    Dabei soll das ganze auf jedes beliebige DGV anwendbar sein.
    Auch eine Mehrfachauswahl der zu exportierenden Spalten soll möglich sein.
    Da es häufig so sein wird, dass ich alle Spalten erxportieren möchte, soll die Aufrufsub in der Lage sein, mit einem Parameter, die ExportColsList mit allen Einträgen aus _AllColsList zu füllen (oder eben nicht)


    Edit: Anhang entfernt. Siehe Post 7

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „DerSmurf“ ()

    DerSmurf schrieb:

    Hier komme ich nicht weiter, da ich keine passende Eigenschaft zu DGV.Row finde, die auf den Header oder irgendwas verweist.
    Wieso zu DGV.Row? Wieso nicht DGV.Cell?
    Dein Code:

    VB.NET-Quellcode

    1. For Each row As DataGridViewRow In DGV.Rows
    2. For Each cell As DataGridViewCell In row.Cells
    3. If Not cell.Value Is Nothing Then
    4. csvFile &= cell.Value.ToString & ","
    5. Else
    6. csvFile &= ","
    7. End If
    8. Next
    9. csvFile = csvFile.TrimEnd(","c)
    10. csvFile &= Environment.NewLine
    11. Next
    Du gehst doch die Zellen durch, da wird sich doch der HeaderText einer DGV-Cell finden lassen.
    Vielleicht kann man so eine Cell innerhalb der DataGridViewCellCollection sogar über ihren Namen addressieren - schau dir das mal im OB an.
    Frage: Welchen Datentyp hat row.Cells?
    Huhu
    Danke für deine Antwort. Aber leider verstehe ich diese nur bedingt.

    ErfinderDesRades schrieb:

    Du gehst doch die Zellen durch, da wird sich doch der HeaderText einer DGV-Cell finden lassen.

    Das will ich doch garnicht?
    Das einzige was mir in der CellCollection helfen könnte, wäre der IndexOf.
    Aber wenn jede Zell einen Index hat, dann muss ja auch die DGVColumn einen Index haben.
    Diese habe ich jetzt verwendet. Das müsste den Vorteil haben, dass ich nicht (wie in der Cell Variante) alle Zellen durch acker und teste, ob der Index stimmt, sondern nur die relevanten Zellen ansteuer.
    Einmal mit Zählschleife (auskommentiert) und mit for each Schleife, weiß nicht was besser ist (oder ob es einen Unterschied macht):

    VB.NET-Quellcode

    1. Dim Rowcount = DGV.Rows.Count
    2. For Each row As DataGridViewRow In DGV.Rows
    3. 'For i = 0 To Rowcount - 1
    4. For Each col In ExportColsList
    5. If Not DGV.Rows(row.Index).Cells(col.Index).Value Is Nothing Then
    6. csvFile &= (DGV.Rows(row.Index).Cells(col.Index).Value).ToString & ","
    7. 'If Not DGV.Rows(i).Cells(col.Index).Value Is Nothing Then
    8. ' csvFile &= (DGV.Rows(i).Cells(col.Index).Value).ToString & ","
    9. Else
    10. csvFile &= ","
    11. End If
    12. Next
    13. csvFile = csvFile.TrimEnd(","c)
    14. csvFile &= Environment.NewLine
    15. Next
    16. 'Next


    ErfinderDesRades schrieb:

    Frage: Welchen Datentyp hat row.Cells?

    row.cells ist vom Datentyp DatagridViewCell. Aber das weißt du selber. Ich verstehe den Hinweis nicht...

    Edit: habe die Solution nochmal hochgeladen, damit du im Code nichts ändern musst.
    Dateien
    • csv export.zip

      (304,18 kB, 91 mal heruntergeladen, zuletzt: )

    ErfinderDesRades schrieb:

    Aber jetzt haste den Code leider geändert

    Nunja, ich war glücklich einen Weg gefunden zu haben, meinen nicht funktionierenden Code in funktionierenden Code umzuändern.
    Die Art und Weise erschien mir dabei durchaus plausiblel. Aber naja, lässt sich ja zurückändern :o)

    ErfinderDesRades schrieb:

    For Each cell As DataGridViewCell In row.Cells

    Aber hier ist doch Cell as DataGridviewCell deklariert?

    DerSmurf schrieb:

    Außerdem hätte ich gerne eure Meinung zu meinem Ansatz.
    Die frmChooseColumns soll noch eine Möglichkeit bekommen, dass ausgewählte Einträge in DGVExportCells nach oben und unten verschoben werden können, außerdem sollen diese gelöscht werden können.
    Aber das müsste ja mit der List (of ..) problemlos klappen. - zumindest in meinen Gedanken.
    Ja, der Zusammenhang liegt weit zurück

    ErfinderDesRades schrieb:

    DerSmurf schrieb:

    Hier komme ich nicht weiter, da ich keine passende Eigenschaft zu DGV.Row finde, die auf den Header oder irgendwas verweist.
    Wieso zu DGV.Row? Wieso nicht DGV.Cell?
    Dein Code:

    VB.NET-Quellcode

    1. For Each row As DataGridViewRow In DGV.Rows
    2. For Each cell As DataGridViewCell In row.Cells
    3. If Not cell.Value Is Nothing Then
    4. csvFile &= cell.Value.ToString & ","
    5. Else
    6. csvFile &= ","
    7. End If
    8. Next
    9. csvFile = csvFile.TrimEnd(","c)
    10. csvFile &= Environment.NewLine
    11. Next
    Du gehst doch die Zellen durch, da wird sich doch der HeaderText einer DGV-Cell finden lassen.
    Vielleicht kann man so eine Cell innerhalb der DataGridViewCellCollection sogar über ihren Namen addressieren - schau dir das mal im OB an.
    Frage: Welchen Datentyp hat row.Cells?
    jdfs was ich sagtete, dass man über die dgvCell die dgvColumn kriegt, und über die dgvColumn den dgvColumnHeaderText.
    Daher braucht man nicht mit IndexOf herumzuprobieren - was mir unsicher vorkommt, denn manche spalten sind versteckt, andere können herumgeschoben sein - da weiss ich nicht, ob Array.IndexOf wirklich den richtigen Index liefert.
    Also ich bin dir echt dankbar für deine Hilfe.
    Aber irgendwie drehen wir uns im Kreis, weil Antworten auf meine konkreten Fragen fehlen:

    DerSmurf schrieb:

    ErfinderDesRades schrieb:
    Du gehst doch die Zellen durch, da wird sich doch der HeaderText einer DGV-Cell finden lassen.
    Das will ich doch garnicht?


    DerSmurf schrieb:

    ErfinderDesRades schrieb:
    Aber jetzt haste den Code leider geändert

    Nunja, ich war glücklich einen Weg gefunden zu haben, meinen nicht funktionierenden Code in funktionierenden Code umzuändern.
    Die Art und Weise erschien mir dabei durchaus plausiblel. Aber naja, lässt sich ja zurückändern :o)

    Warum soll ich alle Zellen durchgehen und hier suchen, zu welcher Column die Zelle gehört und dann die Header mit meiner List Of abgleichen, wenn ich auch gleich nur die Zellen ansteuern kann, die ich brauche?

    DerSmurf schrieb:

    Außerdem hätte ich gerne eure Meinung zu meinem Ansatz.
    Die frmChooseColumns soll noch eine Möglichkeit bekommen, dass ausgewählte Einträge in DGVExportCells nach oben und unten verschoben werden können, außerdem sollen diese gelöscht werden können.
    Aber das müsste ja mit der List (of ..) problemlos klappen. - zumindest in meinen Gedanken.


    Also nicht falsch verstehen. Ich bin dir wirklich, wirklich dankbar für deine Hilfe.
    Aber ich habe funktionierenden Code, der mir logisch erscheint (und auch logischer als dein Ansatz - was natürlich nicht heißt, dass ich recht hab), aber ich verstehe es nicht, wenn meine Fragen nicht beantworrtet werden.
    Ähm - du gehst die Zellen der row durch. Die gehst du durch - muss ja.
    Dass du das nicht willst hab ich nicht aufgefasst, und könnte ich auch nicht verstehen, weil du willst die Werte der Zellen ja exportieren.

    DerSmurf schrieb:

    Warum soll ich alle Zellen durchgehen und hier suchen, zu welcher Column die Zelle gehört und dann die Header mit meiner List Of abgleichen, wenn ich auch gleich nur die Zellen ansteuern kann, die ich brauche?
    Nein, brauchst du nicht.
    Die Row muss ich durchgehen, klar.
    Aber ich brauche ja nicht jede Cell durchforsten.
    Deswegen verstehe ich nicht, was gegen meine neue Lösung spricht und warum ich das machen soll:

    ErfinderDesRades schrieb:

    jdfs was ich sagtete, dass man über die dgvCell die dgvColumn kriegt, und über die dgvColumn den dgvColumnHeaderText.

    Denn mein neuer Code geht jede Row durch, aber nur die Columns die ich auch brauche.

    VB.NET-Quellcode

    1. Dim Rowcount = DGV.Rows.Count
    2. For Each row As DataGridViewRow In DGV.Rows
    3. 'For i = 0 To Rowcount - 1
    4. For Each col In ExportColsList
    5. If Not DGV.Rows(row.Index).Cells(col.Index).Value Is Nothing Then
    6. csvFile &= (DGV.Rows(row.Index).Cells(col.Index).Value).ToString & ","
    7. 'If Not DGV.Rows(i).Cells(col.Index).Value Is Nothing Then
    8. ' csvFile &= (DGV.Rows(i).Cells(col.Index).Value).ToString & ","
    9. Else
    10. csvFile &= ","
    11. End If
    12. Next
    13. csvFile = csvFile.TrimEnd(","c)
    14. csvFile &= Environment.NewLine
    15. Next
    16. 'Next

    Für mich absolut plausibel.

    Anhand deiner Reaktion:

    ErfinderDesRades schrieb:

    Aber jetzt haste den Code leider geändert

    und dem weiteren hinweisen auf die DataGridViewCellCollection, vermute ich, dass mit diesem Code etwas ist, bzw. das es anders besser oder schneller geht.
    Aber ich kann immer nur vermuten und erahnen.

    Hier beschweren sich des öfteren Helfer, (vollkommen zurecht) dass Fragesteller nur halbe, oder wertlose Infos preisgeben (ohne dich angreifen zu wollen - du auch).
    Also bitte verhalte dich auch so und REDE KLARTEXT - denn zum zusammenreimen fehlen mir oft einfach die Kentnisse.

    DerSmurf schrieb:


    Also Spaltennamen einfach als string? Oder gibt es etwas wie Dim Col as DGV.Column. Was dann eigenschaften wie Col.HeaderText hat?

    Und wie ich dann im Code am besten die ausgewählten Spalten durchgehe.


    lasse den User die option spalten anzuordnen wie sie wollen...

    VB.NET-Quellcode

    1. 'lasse User die option zu ändern
    2. DataGridView1.AllowUserToOrderColumns = True


    jetzt noch die option Columns mit Right.Click spalten ausblenden
    kann mann noch mit ContextMenu schöner machen

    VB.NET-Quellcode

    1. Private Sub DataGridView1_CellMouseDown(sender As Object, e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles DataGridView1.CellMouseDown
    2. 'blend aus was nicht exportiert werden soll
    3. If e.RowIndex <> -1 AndAlso e.ColumnIndex <> -1 Then
    4. If e.Button = MouseButtons.Right Then
    5. Me.DataGridView1.Columns(e.ColumnIndex).Visible = False
    6. End If
    7. End If
    8. End Sub


    dann Exportieren, aber hier musst du prüfen welchen sichtbar/unsichtbar sind
    auf die schnelle....

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    2. Dim sw As New StreamWriter("E:\TestFolder\ExcelTest\TestCreate112.csv")
    3. 'schreibe headers
    4. For i As Integer = 0 To DataGridView1.Columns.Count - 1
    5. If DataGridView1.Columns(i).Visible Then
    6. sw.Write(DataGridView1.Columns(i).HeaderText)
    7. If i <> DataGridView1.Columns.Count Then
    8. sw.Write(";")
    9. End If
    10. End If
    11. Next i
    12. sw.Write(sw.NewLine)
    13. 'schreibe data
    14. For Each dr As DataGridViewRow In DataGridView1.Rows
    15. For i As Integer = 0 To DataGridView1.Columns.Count - 1
    16. If DataGridView1.Columns(i).Visible Then
    17. sw.Write(dr.Cells(i).Value)
    18. If i <> DataGridView1.Columns.Count Then
    19. sw.Write(";")
    20. End If
    21. End If
    22. Next i
    23. sw.Write(sw.NewLine)
    24. Next dr
    25. sw.Flush()
    26. End Sub


    ist noch eine Baustelle, aber vielleicht ein Ansatz
    So. Ich habs jetzt fertig gestellt. Vielleicht hilft der Code ja dem ein oder anderen.
    Ob er gut ist, kann ich wie gesagt nicht sagen, aber das ganze läuft problemlos.

    Ich habe noch eine zweite Funktion hinzugefügt, um nur bestimmte Spalten aus dem gesamten DataSet zu exportieren.
    Außerdem habe ich als export csv und xlsx (mit EPPlus) eingebaut.

    Die beiden exporte könnte man problemlos auch zusammenlegen (ein Code - eine Userform). Da ich in meinen Programmen allerdings nur entwder DGV Export ODER DataSet Export benötige, hab ich getrennt gelassen.
    Dateien
    • csv export.zip

      (1,42 MB, 148 mal heruntergeladen, zuletzt: )