gebundenes DataGridView mit sortierter Bindingsource -> selected Rows sortiert weitergeben

  • VB.NET
  • .NET (FX) 4.0

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von tragl.

    gebundenes DataGridView mit sortierter Bindingsource -> selected Rows sortiert weitergeben

    Hallo zusammen.

    Ich steh' aktuell vor einem Problem:

    Ich selektiere mir eine handvoll Rows (bsp. einen ganzen Monat) in einem DataGridView. Die Bindingsource des DGV ist nach "Datum" sortiert, Die ID's haben folglich eine andere Sortierung.
    Nehme ich mir jetzt die dgv.selectedrows z.B. in eine Liste bzw. übergebe die an eine temporäre DataTable dann stimmt meine Sortierung nicht mehr.

    Wie schaffe ich es, die Sortierung der BindingSource auch an die dgv.selectedrows weiterzugeben? Der User soll sich hier Daten nach Excel oder in die Zwischenablage kopieren können - bringt nur
    nix wenn die Einträge durcheinander sind ;( Über die Bindingsource und somit das DTS wollte ich nicht gehen, weil das DGV die Columns leserlich anzeigt - deshalb wollte ich gerne damit arbeiten.

    LG
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Du könntest die SelectedRows doch auch nach der Datums"spalte" sortieren. Oder Du nimmst deren Index-Property als Sortierkriterium.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    die selectedRows sind ja dgvRows - die haben einen RowIndex glaub ich - danach kann man sortieren.
    weiters hat eine dgvRow ein DataboundItem, das ist bei ab Dataset gebundenen DGVs ein DataRowview (kann man also dementsprechend casten) - letztlich eine Sicht auf die angezeigte DataRow.
    reicht das so?
    Wird ein wenig schwieriger, nicht jedes DGV hat eine Datum-Spalte.
    Aber hier würde ich (falls die BS sortiert ist) den String aus bindingsource.sort mitliefern - die Column müsste er sich eben raussuchen und danach sortieren.
    Ich weiß nur nicht, wie ich eine DataGridViewSelectedRowCollection durchsortieren kann..

    Hier mal meine Methode(n)
    Aufruf geht mit
    dgv.copyExportSelection(True, bsDatenabfrage.Sort) für Excel.

    Sortierung müsste dann im folgenden Code nach Zeile 24 stattfinden...

    Spoiler anzeigen

    VB.NET-Quellcode

    1. ''' <summary> Stellt einen Dialog zur Auswahl des Inhalts eins DGV zum Export oder Kopieren bereit. </summary>
    2. <Extension>
    3. Public Sub copyExportSelection(dgv As DataGridView, excel As Boolean, Optional orderby As String = "")
    4. Using dlg As New dlgDGVcopyExport
    5. For Each col As DataGridViewColumn In dgv.Columns
    6. If col.Visible Then dlg.chkLbSpalten.Items.Add(col.HeaderText)
    7. Next
    8. For i = 0 To dlg.chkLbSpalten.Items.Count - 1
    9. dlg.chkLbSpalten.SetItemChecked(i, True)
    10. Next
    11. If dlg.ShowDialog <> DialogResult.OK Then Return
    12. Dim lstCol As New List(Of String)
    13. For Each chkItm In dlg.chkLbSpalten.CheckedItems
    14. lstCol.Add(chkItm.ToString)
    15. Next
    16. Select Case True
    17. Case dlg._Selection = "all"
    18. If excel Then
    19. exportSelectionToExcel(dgv, "Mitarbeiter", lstCol, dlg._header)
    20. Else
    21. copySelectionToClipboard(dgv, lstCol, dlg._header)
    22. End If
    23. Case dlg._Selection = "marked"
    24. Dim lstRow = dgv.SelectedRows
    25. If excel Then
    26. exportSelectionToExcel(dgv, "Mitarbeiter", lstCol, dlg._header, lstRow)
    27. Else
    28. copySelectionToClipboard(dgv, lstCol, dlg._header, lstRow)
    29. End If
    30. End Select
    31. End Using
    32. End Sub


    Spoiler anzeigen

    VB.NET-Quellcode

    1. ''' <summary>Exportiert den selektierten Inhalt eines DGVs inkl. Angabe von Sheetname in eine Excel-Datei via EPPlus</summary>
    2. Private Sub exportSelectionToExcel(dgv As DataGridView, wsn As String, lstCol As List(Of String), Header As Boolean, Optional lstRow As DataGridViewSelectedRowCollection = Nothing)
    3. Dim excelFile = saveAs("Excel-Dateien (*.xlsx)|*.xlsx")
    4. If excelFile = "" Then Return
    5. If File.Exists(excelFile) Then File.Delete(excelFile)
    6. Dim eFi = New FileInfo(excelFile)
    7. Using pck As New ExcelPackage(eFi)
    8. Dim wks = pck.Workbook.Worksheets.Add(wsn)
    9. Dim dtExport As New DataTable
    10. lstCol.ForEach(AddressOf dtExport.Columns.Add)
    11. If lstRow IsNot Nothing Then
    12. For Each rw As DataGridViewRow In lstRow
    13. dtExport.Rows.Add()
    14. For Each cell As DataGridViewCell In rw.Cells
    15. Dim colHeader = cell.OwningColumn.HeaderText
    16. If colExist(dtExport, colHeader) Then dtExport.Rows(dtExport.Rows.Count - 1)(colHeader) = CStr(cell.FormattedValue)
    17. Next
    18. Next
    19. showDebugDGV(dtExport)
    20. Else
    21. For Each rw As DataGridViewRow In dgv.Rows
    22. dtExport.Rows.Add()
    23. For Each cell As DataGridViewCell In rw.Cells
    24. If cell.OwningColumn.Visible = True Then
    25. Dim colHeader = cell.OwningColumn.HeaderText
    26. If colExist(dtExport, colHeader) Then dtExport.Rows(dtExport.Rows.Count - 1)(colHeader) = CStr(cell.FormattedValue)
    27. End If
    28. Next
    29. Next
    30. End If
    31. If Header Then
    32. wks.Cells("A1").LoadFromDataTable(dtExport, True)
    33. Else
    34. wks.Cells("A1").LoadFromDataTable(dtExport, False)
    35. End If
    36. With wks.Cells(wks.Dimension.Address).Style.Border
    37. .Top.Style = Style.ExcelBorderStyle.Thin
    38. .Left.Style = Style.ExcelBorderStyle.Thin
    39. .Right.Style = Style.ExcelBorderStyle.Thin
    40. .Bottom.Style = Style.ExcelBorderStyle.Thin
    41. End With
    42. wks.Cells(wks.Dimension.Address).AutoFitColumns()
    43. pck.Save()
    44. End Using
    45. If msgQuestionInfo($"Der Export wurde unter {excelFile} gespeichert.{Environment.NewLine}Soll die Datei geöffnet werden?") = DialogResult.Yes Then
    46. Process.Start(excelFile)
    47. End If
    48. End Sub


    also ich will das so allgemein wie möglich halten, denn ich nutze für jedes DGV folgenden Dialog wo der User alles für den Export auswählen kann
    (ist ein Screen während der Laufzeit - in die CheckedListBox kommen alle Cols des jeweiligen DGV)


    also ich könnte mir sowas in etwa vorstellen:

    VB.NET-Quellcode

    1. Dim lstRow = dgv.SelectedRows
    2. Dim lstRv As New List(Of DataRowView)
    3. For Each dgvRw As DataGridViewRow In lstRow
    4. lstRv.Add(DirectCast(dgvRw.DataBoundItem, DataRowView))
    5. Next
    6. lstRv = lstRv.OrderBy()


    bei Zeile 6 komm ich aber nicht weiter - und was passiert, wenn das DGV ungebunden ist? Der Fälle hab' ich nämlich auch ;)
    Also eine List(Of DataGridViewRowSelection) wäre mir am liebsten. Die wird für jedes DGV funktionieren.

    Edit:
    Hab das Monstrum hier im Netz gefunden, damit geht's
    Dim lstRow As List(Of DataGridViewRow) = (From c As DataGridViewRow In dgv.SelectedRows.Cast(Of DataGridViewRow)() Select c Order By c.Index).Cast(Of DataGridViewRow)().ToList
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

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

    Hmm - Geht nicht einfach:

    VB.NET-Quellcode

    1. Dim lstRow = (From c In dgv.SelectedRows.Cast(Of DataGridViewRow)() Order By c.Index).ToList
    und c ist natürlich auch ein Krimineller Name, weil es sind doch Rows.

    Wird ein wenig schwieriger, nicht jedes DGV hat eine Datum-Spalte.
    versteh ich nicht - hat ja mit einer DatumSpalte nix zu tun.



    in der Export2Excel dann den lstRow-Paramater nicht optional, aber dafür kannste das dgv weglassen.
    sollen alle dgvRows exportiert werden, dann muss der Aufrufer eben dgv.Rows übergeben.

    Übrigens: die DataTable.Add()-Methode gibt etwas zurück - etwas sehr nützliches (glaubich wenigstens, jetzt extra nachgugge ich nich).

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

    ErfinderDesRades schrieb:

    DataTable.Add()-Methode gibt etwas zurück - etwas sehr nützliches

    nö:


    ErfinderDesRades schrieb:

    Hmm - Geht nicht einfach:

    jo geht, hab' das ma sinnvoller benamt:
    Dim lstRow = (From rw In dgv.SelectedRows.Cast(Of DataGridViewRow)() Order By rw.Index).ToList

    nochwas Anderes (wenn ich nen neuen Fred aufmachen soll, dann sagt bitte Bescheid):
    Ich markier' mir alle Datumse und kopier die (mit den Methoden aus dem Thread hier)

    die gehen aber jetzt nicht nach Excel, sondern in die Zwischenablage mit:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. ''' <summary> wandelt den Inhalt einer DateTable in das tabellarische Zwischenablage-Format um </summary>
    2. Private Function setClipboardContent(dt As DataTable, header As Boolean) As String
    3. Dim tab = Convert.ToChar(Keys.Tab)
    4. Dim colString = ""
    5. Dim rowString = ""
    6. If dt.Rows.Count > 0 Then
    7. 'Header
    8. If header Then
    9. colString = ""
    10. For i = 0 To dt.Columns.Count - 1
    11. If i = 0 Then
    12. colString = $"{dt.Columns(i).ColumnName}"
    13. Else
    14. colString = $"{colString}{tab}{dt.Columns(i).ColumnName}"
    15. End If
    16. Next
    17. End If
    18. 'Rows
    19. Dim lstRowString As New List(Of String)
    20. For Each rw As DataRow In dt.Rows
    21. rowString = ""
    22. For i = 0 To dt.Columns.Count - 1
    23. If i = 0 Then
    24. rowString = $"{rw.Item(i)}"
    25. Else
    26. rowString = $"{rowString}{tab}{rw.Item(i)}"
    27. End If
    28. Next
    29. lstRowString.Add(rowString)
    30. Next
    31. 'Rows sauber anordnen
    32. Dim rwString = ""
    33. For i = 0 To lstRowString.Count - 1
    34. If i = 0 Then
    35. rwString = $"{lstRowString(i)}{Environment.NewLine}"
    36. Else
    37. rwString = $"{rwString}{lstRowString(i)}{Environment.NewLine}"
    38. End If
    39. Next
    40. 'Ausgabe
    41. If header Then
    42. Return $"{colString}{Environment.NewLine}{rwString}"
    43. Else
    44. Return rwString
    45. End If
    46. Else
    47. Return ""
    48. End If
    49. End Function


    er macht mir aber dummerweise immer 2 Leerzeilen dazwischen, (nach den ersten beiden Rows, danach nicht mehr)
    habich was übersehn? Macht er aber nur wenn ich die letzte Spalte "Bemerkung" mit reinnehme - macht irgendwie keinen Sinn, denn der entsprechende Bemerkungstext wird mit übergeben

    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

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

    tragl schrieb:

    nö:
    guckma im ObjectBrowser - da siehste dann auch die Überladung.

    Aber das steht da doch auch in deim Bildle:

    As DataRow steht da. In direktem Widerspruch zur Aussage darunter. :/
    Wie gesagt: ObjectBrowser. Nur tooltip-gestützt programmieren ist'n bischen wie tunnelblick.

    zum neuen Problem: Ich vermute dass tab was anneres ist als was du denkst.
    Ich nehme als Tab immer Microsoft.Visualbasic.ControlChars.Tab.
    Auf Microsoft.Visualbasic.ControlChars mach ich inne Projekteinstellungen einen GeneralImport, dann kannich überall im Code einfach Tab schreiben und ist Tab.
    Ebenso mit NewLine, das heisst dann einfach CrLf.

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

    ErfinderDesRades schrieb:

    Ich nehme als Tab immer Microsoft.Visualbasic.ControlChars.Tab.

    macht hier in dem Fall keine Änderung. - außerdem will ich den "bösen" visual basic-import nach Möglichkeit garnet erst nutzen :P
    Immer wenn ich die Spalte "Bemerkung" mit reinnehme macht er leerzeilen nach den ersten beiden Rows und auch eine am Ende nach den ganzen Rows...
    ich verzweifel hier noch - ich kann die Zwischenablage-Kopie so nicht in die geschützte Excel-Datei pasten X(

    ErfinderDesRades schrieb:

    Aber das steht da doch auch in deim Bildle:

    hab ich im objectbrowser net gefunden - aber dennoch: was soll mir das sagen?

    Edit: Ich glaub' ich hab's.
    Die dusselige Bemerkung-Spalte is vom Typ String - in meinen Dialogen hab ich aber ne Richtextbox drinne, wenn da jemand mal nix reingeschrieben hat, aber dafür "enter" gedrückt hat dann wird da schon eine "environment.newline" drin sein, richtig?
    Hätte die Spalte in dem Fall dann trotzdem "" als Wert, sodass ich die alle mit String.Empty abfahren könnte?

    Also If rwDatenabfrage.Bemerkung = "" Then rwDatenabfrage.Bemerkung = String.Empty ?

    Edit: Es liegt definitv daran, dass schon ein Zeilenumbruch in dem leeren String enthalten ist. Wie bekomm' ich den wieder raus?
    mit o.g. Methode geht's nicht und mit rw.Bemerkung.Replace(Environment.NewLine, "") leider auch nicht :(
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „tragl“ ()

    mach doch so unnütze Leerzeichen mit .Trim({" "c, Tab, CR, LF}) weg.

    tragl schrieb:

    visual basic-import nach Möglichkeit garnet erst nutzen
    Nicht alles daraus ist böse (nur 99,9%). Da sollteman ich finde das arme 0,1% nicht mit-bestrafen.
    Genau lesen: Ich generalImportiere nicht Microsoft.Visualbasic, sondern Microsoft.Visualbasic.ControlChars.



    tragl schrieb:

    hab ich im objectbrowser net gefunden - aber dennoch: was soll mir das sagen?
    Man kann bisserl eleganter coden:

    VB.NET-Quellcode

    1. dim dtRw = dtExport.Rows.Add()
    2. For Each cell As DataGridViewCell In rw.Cells
    3. If cell.OwningColumn.Visible = True Then
    4. Dim colHeader = cell.OwningColumn.HeaderText
    5. If colExist(dtExport, colHeader) Then dtRw(colHeader) = cell.FormattedValue
    6. End If
    7. Next

    Ich find If colExist(dtExport, colHeader) Then dtExport.Rows(dtExport.Rows.Count - 1)(colHeader) = CStr(cell.FormattedValue) unnötig unleserlich.

    ErfinderDesRades schrieb:

    mach doch so unnütze Leerzeichen mit .Trim({" "c, Tab, CR, LF}) weg.


    probier ich morgen. normale "space" können aber da bleiben.

    ErfinderDesRades schrieb:

    Man kann bisserl eleganter coden:


    stimmt wohl - muss ich erstnoch lernen ;)

    ErfinderDesRades schrieb:

    Genau lesen:


    hab ich gemacht, ich meinte auch nicht den generellen visualbasic-import - dennoch will ich komplett davon wegbleiben wenn's nur eben geht
    Dim tab = Convert.ToChar(Keys.Tab) erfüllt seinen zweck und ist weg vom ursprünglichen VisualBasic :D
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup: