WPF Datagridview infinite scrolling? Wie?

  • WPF

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von Artur.

    WPF Datagridview infinite scrolling? Wie?

    Hallo in die Runde,

    zuerst einmal möchte ich mich ganz herzlich bei diesem Forum und all seinen Mitgliedern für die vielen Fragen und Antworten bedanken :-). Diese Plattform hat mir schon oft geholfen um Probleme zu lösen.
    Finde ich echt super! Vielleicht klappt es dieses mal auch wieder ;)

    Ich habe folgendes Problem:

    Ich lade (select Abfrage) aus einer DB über eine ODBC Schnittstelle Daten.
    Sobald die DB einen Rückgabe-Wert zurückgibt, soll die Rückgabe in eine "verschachtelte" Liste (List of....mehrere Datentypen...) geladen werden.
    Weil das Resultat etwas größer ist, ( kann mal über 40000 Zeilen sein) kann es etwas dauern bis die Antwort vom Server vollständig erfolgt ist.
    Deswegen habe ich mir überlegt, den Vorgang des Abfragens und das schreiben in die Resultat-Liste in einen eigenen Thread zu packen. Um zu "wissen" was der Thread gerade macht, habe ich ein paar Status-Marker gesetzt.
    Sobald der Status-Marker "Server sendet Antwort" erreicht ist, heißt es für mich, dass die ersten Resultate in der Resultate_Liste geschrieben werden.

    Ab dem Zeitpunkt an dem die ersten Daten empfangen werden kann ich auf die Resultate_Liste zugreifen und die ersten 500 Werte in eine Datagridview schreiben.
    Des Weiteren habe ich eine kleine Funktion geschrieben die in der Lage ist die Resultate_Liste in "Seiten" mit 500 Einträgen zu unterteilen. Dadurch wir die Datagridview nicht mit Daten überladen und das Laden der Daten geht auch deutlich schneller.

    Bis hierhin ist alles gut und es funktioniert alles. Allerdings würde ich gerne von den Seiten weggehen und ein Infinite-Scrolling integrieren?
    Das würde ich gerne so gestalten, dass die ersten 500 Werte geladen werden und den Benutzer angezeigt werden. Sobald der Benutzer "runterscrollt" sollen die nächsten 500 Werte geladen werden und so weiter.

    Mein Problem ist, dass ich nicht weis wo ich am besten ansetzen kann? Mit dem MouseWheel Event will ich eigentlich nichts machen, weil ich in diesem Event nicht genau sagen kann ob der User gerade ganz unten in der Liste ist (dort würde es Sinn machen Daten nachzuladen) oder ob der User das "erste mal scrollt". Am liebsten würde ich einfach den Scrollbalken der Datagridview abfragen. Leider finde ich keine Möglichkeit den Scrollbalken abzufragen.

    Hat jemand eine Idee?

    Aus VB-Paradise Guide verschoben ~ EaranMaleasi

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

    Hallo und Willkommen im Forum,

    das "problem" hatte ich auch mal.
    Erstmal muss ich aber Fragen wie genau du die Daten abrufst? Zu Fuß mit ADO.Net oder hast du einen O/R Mapper wie EntityFramework im Einsatz?
    UND - gehts hier wirklich um WPF weil du schreibst was von DataGridView. Meinst du DataGrid?

    Ich hatte mir mal ein DataGrid gebaut welches genau das macht und man einstellen kann wieviele Datensätze geladen werden. Das nachladen erfolgte beim scrollen kurz bevor man ans ende gescrollt hat, sodass bereits nachgeladen ist bevor man ganz unten ist.

    Man muss aber auch an andere Dinge denken. Was soll passieren wenn der User sortiert? Was wenn er filtert (falls ein Filterfeld im Windiw vorhanden).
    Das passiert in meinem Beispiel über Linq mit Take und Skip kann aber leicht umgebaut werden.

    Ich kann dir meinen Code von damals gerne raussuchen, wird aber erst morgen was da ich heute voll im stress bin.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

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

    Hi Sascha,

    danke für deine Nachricht.

    Ja, ich meine natürlich DataGrid ^^ . Bin noch neu in der WPF Welt und verknüpfe noch zu viel mit Win-Forms.
    Und eigentlich bin ich auch noch relativ neu in der Programmierwelt (etwa mehr als 1 Jahr). Bin eigentlich ein Netzwerker ;) mit einem Faible zum Programmieren.

    Danke für den Hinweis mit dem sortieren.... Daran habe ich tatsächlich noch nicht nachgedacht.

    Ich benutze ADO.Net Klassen (OdbcConnection und alles andere was dazu gehört)
    Hier mein Code den ich geschrieben habe, um bequem auf die DB zuzugreifen. Den Code kann man super wiederverwerten. Man muss halt in diesem Code damit leben, dass die Connect Daten unverschlüsselt geladen werden.
    Könnte man aber leicht abändern. Ist vielleicht nicht das beste aber für meine zwecke OK.

    VB.NET-Quellcode

    1. Imports System.Data
    2. Imports System.Data.Odbc
    3. Public Module SQL_ODBC
    4. Dim connection As OdbcConnection
    5. Public cmd_Reader As OdbcDataReader
    6. Dim ProgrammPath As String = System.AppDomain.CurrentDomain.BaseDirectory.ToString
    7. Dim SQL_INI As String = ProgrammPath & "\ODBCSQL.ini"
    8. Public Function conn(ByVal ODBC_Name, ByVal Username, ByVal Passwort)
    9. Dim ConnectString As String = "Dsn=" & ODBC_Name & ";" & "Uid=" & Username & ";" & "Pwd=" & Passwort
    10. connection = New OdbcConnection(ConnectString)
    11. If connection.State = ConnectionState.Closed Then
    12. connection.Open()
    13. End If
    14. Return True
    15. End Function
    16. Public Function RunSQL(ByVal sql As String, ByVal ODBC_Name As String, ByVal Username As String, ByVal Passwort As String) 'write
    17. Dim cmd As New OdbcCommand
    18. conn(ODBC_Name, Username, Passwort)
    19. cmd.Connection = connection
    20. cmd.CommandType = CommandType.Text
    21. cmd.CommandText = sql
    22. cmd_Reader = cmd.ExecuteReader()
    23. cmd.Dispose()
    24. connection.Close()
    25. Return True
    26. End Function
    27. Public Function RunSQL_with_autologin_from_file(ByVal sql As String)
    28. Try
    29. Dim Username As String = ReadIni(SQL_INI, "Einstellungen", "Username", "")
    30. Dim Passwort As String = ReadIni(SQL_INI, "Einstellungen", "Passwort", "")
    31. Dim ODBC_Name As String = ReadIni(SQL_INI, "Einstellungen", "ODBC_Name", "")
    32. Dim cmd As New OdbcCommand
    33. conn(ODBC_Name, Username, Passwort)
    34. cmd.Connection = connection
    35. cmd.CommandType = CommandType.Text
    36. cmd.CommandText = sql
    37. cmd_Reader = cmd.ExecuteReader()
    38. cmd.Dispose()
    39. connection.Close()
    40. Return True
    41. Catch ex As Exception
    42. Return False
    43. End Try
    44. End Function
    45. Private Declare Unicode Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringW" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal lpString As String, ByVal lpFileName As String) As Int32
    46. Private Declare Unicode Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringW" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Int32, ByVal lpFileName As String) As Int32
    47. Private Sub writeIni(ByVal iniFileName As String, ByVal Section As String, ByVal ParamName As String, ByVal ParamVal As String)
    48. Dim Result As Integer = WritePrivateProfileString(Section, ParamName, ParamVal, iniFileName)
    49. End Sub
    50. Private Function ReadIni(ByVal IniFileName As String, ByVal Section As String, ByVal ParamName As String, ByVal ParamDefault As String) As String
    51. Dim ParamVal As String = Space$(1024)
    52. Dim LenParamVal As Long = GetPrivateProfileString(Section, ParamName, ParamDefault, ParamVal, Len(ParamVal), IniFileName)
    53. ReadIni = Left$(ParamVal, LenParamVal)
    54. End Function
    55. End Module


    Hier das Module, dass die Daten von der DB abfragt und in die Resultate_Liste schreibt.

    VB.NET-Quellcode

    1. Imports DB_Connector
    2. Module modFDSucheV10
    3. Public Result_List As New List(Of DB_Result)
    4. Dim neuerthread As System.Threading.Thread
    5. Private SQL_String_Temp As String = Nothing
    6. Public Suche_Status As String = Nothing
    7. Public Structure DB_Result
    8. Public Feld_ID As String
    9. Public Feld_Auftrag As String
    10. Public Feld_Name As String
    11. Public Feld_Material As String
    12. Public Feld_Farbe As String
    13. Public Feld_TypeOfRestoration As String
    14. Public Feld_Einheiten As String
    15. Public Feld_Einlesedatum As String
    16. Public Feld_Chargennummer As String
    17. Public Sub New(ByVal ID As String, ByVal Auftrag As String, ByVal Name As String, ByVal Material As String, ByVal Farbe As String, ByVal TypeOfRestoration As String, ByVal Einheiten As String, ByVal Einlesedatum As String, ByVal Chargennummer As String)
    18. Me.Feld_ID = ID
    19. Me.Feld_Auftrag = Auftrag
    20. Me.Feld_Name = Name
    21. Me.Feld_Material = Material
    22. Me.Feld_Farbe = Farbe
    23. Me.Feld_TypeOfRestoration = TypeOfRestoration
    24. Me.Feld_Einheiten = Einheiten
    25. Me.Feld_Einlesedatum = Einlesedatum
    26. Me.Feld_Chargennummer = Chargennummer
    27. End Sub
    28. End Structure
    29. Public Sub Suche_Starten(SQL_String)
    30. SQL_String_Temp = SQL_String
    31. Try
    32. neuerthread.Abort()
    33. Catch ex As Exception
    34. End Try
    35. Try
    36. neuerthread = New System.Threading.Thread(AddressOf Suche_Starten_temp)
    37. neuerthread.Start() 'Vorgang starten
    38. Catch ex As Exception
    39. End Try
    40. End Sub
    41. Private Sub Suche_Starten_temp()
    42. Suche_Status = "Server wird abgefragt"
    43. DB_Connector.RunSQL_with_autologin_from_file(SQL_String_Temp)
    44. Suche_Status = "Server hat geantwortet"
    45. While DB_Connector.cmd_Reader.Read()
    46. Suche_Status = "Ergebisse vom Server werden geladen"
    47. Result_List.Add(New DB_Result(cmd_Reader(0), cmd_Reader(21), cmd_Reader(1), cmd_Reader(2), cmd_Reader(4), cmd_Reader(4), cmd_Reader(5), cmd_Reader(7), ""))
    48. End While
    49. Suche_Status = "Suche beendet"
    50. End Sub
    51. End Module


    Hier noch die Abfrage für die DB

    VB.NET-Quellcode

    1. Private Sub DB_Einträge_laden(ByVal Seite)
    2. Dim Zahl01_Temp As Integer = temp_Viewer_Einträge_pro_Seite * Seite
    3. Dim Zahl02_Temp As Integer = Zahl01_Temp + temp_Viewer_Einträge_pro_Seite
    4. If Zahl01_Temp <= 0 Then
    5. Else
    6. If Zahl02_Temp > temp_DB_Count Then
    7. Zahl02_Temp = temp_DB_Count
    8. End If
    9. lv_aufträge_erstellen()
    10. For i = Zahl01_Temp To Zahl02_Temp
    11. lv_aufträge.Items.Add(New GRIDViewData_ With {
    12. .id = Result_List.Item(i).Feld_ID,
    13. .Auftrag = Result_List.Item(i).Feld_Auftrag,
    14. .Name = Result_List.Item(i).Feld_Auftrag,
    15. .Material = Result_List.Item(i).Feld_Material,
    16. .Farbe = Result_List.Item(i).Feld_Farbe,
    17. .TypeOfRestoration = Result_List.Item(i).Feld_TypeOfRestoration,
    18. .Einheiten = Result_List.Item(i).Feld_Einheiten,
    19. .Einlesedatum = Result_List.Item(i).Feld_Einlesedatum,
    20. .Chargennummer = Result_List.Item(i).Feld_Chargennummer})
    21. Next
    22. End If
    23. End Sub


    Das sind die Hauptfunktionen für den Vorgang.

    Ich würde mich freuen wenn Du mir einen Tipp geben könntest

    Viele Grüße

    Artur