DataGridView & Lecture multithread

Résolu
Polack77 Messages postés 1098 Date d'inscription mercredi 22 mars 2006 Statut Membre Dernière intervention 22 octobre 2019 - 18 janv. 2011 à 11:52
Polack77 Messages postés 1098 Date d'inscription mercredi 22 mars 2006 Statut Membre Dernière intervention 22 octobre 2019 - 21 janv. 2011 à 15:43
Bonjour,

Je fait une classe qui permet de lire une table de base de données page par page en multithreading. Problème je n'arrive pas à afficher la table durant la lecture :/.

Je n'arrive pas à expliquer rapidement et clairement mon problème. Donc le plus simple est de vous le montrer

Petit code pour comprendre mon problème (enfin c'est le plus petit que j'arrive à faire ^^):
Dans un Form (Form1) mettre un DataGridView (nommer DataGridView1 soit le nom par défaut ;))
Dans le code de cette form mettre (en principe il n'y à rien d'autre à faire ;)):
Public Class Form1
    Private Enum E_Mode
        Merge
        Rows_Add
    End Enum
    Private Class DonneesPourThread
        Public NombreDePage As Integer
        Public NombreDeLigneParPage As Integer
        Public Mode As E_Mode
    End Class

    Private ThreadRempli As System.Threading.Thread
    Private WithEvents TableDonnees As DataTable
    Private Delegate Sub Delegate_DataGridView1_Refresh()

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        'Juste pour que le DataGridView remplisse tout le form ;)
        DataGridView1.Dock = DockStyle.Fill

        'Préparation de la DataTable
        TableDonnees = New DataTable("Table de données")
        TableDonnees.Columns.Add("Id", System.Type.GetType("System.Int32"))
        TableDonnees.Columns.Add("Donnée", System.Type.GetType("System.String"))

        'Crée la liaison entre la table et le DataGridView
        DataGridView1.DataSource = TableDonnees

        'Préparation du thread
        ThreadRempli = New System.Threading.Thread(AddressOf SubThreadRemplisage)
        ThreadRempli.IsBackground = True 'Permet au system de tué le thread quand le conteneur se termine 

        'Prepare le infos pour le thread
        Dim InfoThread As New DonneesPourThread
        InfoThread.NombreDeLigneParPage = 1000
        InfoThread.NombreDePage = 100

        'Choix du mode de fonctionnement de l'exemple 
        '<----- Pour changer le mode de fonctionnement c'est ici que sa se passe ;) ----->
        InfoThread.Mode = E_Mode.Merge
        'InfoThread.Mode = E_Mode.Rows_Add

        'Lance le thread
        ThreadRempli.Start(InfoThread)
    End Sub

    Private Sub SubThreadRemplisage(ByVal Info As DonneesPourThread)
        'Le test du mode de fonctionnement est fait ici pour éviter de le refaire à chaque boucle
        Select Case Info.Mode
            Case E_Mode.Merge
                'Sa ne fonction pas (mais c'est rapide)
                For CompteurPage As Integer = 1 To Info.NombreDePage
                    Dim TablePage As DataTable = GetPageDonnees(Info.NombreDeLigneParPage)
                    'Evite que plusieurs threads accèdent à cette objet en même temps
                    SyncLock TableDonnees
                        TableDonnees.Merge(TablePage)
                    End SyncLock
                Next
            Case E_Mode.Rows_Add
                'Sa fonctionne (mais mal : les scroll ne sont jamais mit à jours sauf quand on scroll :/)
                'Et c'est TRES TRES lent (donc inacceptable pour mon code final :'( )
                For CompteurPage As Integer = 1 To Info.NombreDePage
                    Dim TablePage As DataTable = GetPageDonnees(Info.NombreDeLigneParPage)
                    For CompteurLigne As Integer = 1 To Info.NombreDeLigneParPage
                        SyncLock TableDonnees
                            TableDonnees.Rows.Add(TablePage.Rows(CompteurLigne - 1).ItemArray)
                        End SyncLock
                    Next
                Next
        End Select
    End Sub

    Private Function GetPageDonnees(ByVal NombreLigneParPage As Integer) As DataTable
        'Prépare la table contenant la page de données
        Dim TablePage As New DataTable("Page de données lut en base de données")
        TablePage.Columns.Add("Id", System.Type.GetType("System.Int32"))
        TablePage.Columns.Add("Donnée", System.Type.GetType("System.String"))
        For Compteur As Integer = 1 To NombreLigneParPage
            Dim Ligne As DataRow = TablePage.NewRow
            Ligne.Item(0) = TablePage.Rows.Count + TableDonnees.Rows.Count + 1
            Ligne.Item(1) = "Donnée de la ligne : " & Ligne.Item(0)
            TablePage.Rows.Add(Ligne)
        Next

        Return TablePage
    End Function

    Private Sub DataGridView1_Refresh()
        DataGridView1.Refresh()
    End Sub

    Private Sub TableDonnees_RowChanged(ByVal sender As Object, ByVal e As System.Data.DataRowChangeEventArgs) Handles TableDonnees.RowChanged
        'Une erreur peut survenir quand on ferme le form ;)
        Try
            'On passe dans cet événement avec mode Merge 
            'mais rien n'est mit à jour :'(
            Invoke(New Delegate_DataGridView1_Refresh(AddressOf DataGridView1_Refresh))
        Catch

        End Try
    End Sub

    Private Sub TableDonnees_TableNewRow(ByVal sender As Object, ByVal e As System.Data.DataTableNewRowEventArgs) Handles TableDonnees.TableNewRow
        'On passe dans cet événement avec mode Rows_Add 
        'Les scrolls ne sont pas mit à jour :'(
        Invoke(New Delegate_DataGridView1_Refresh(AddressOf DataGridView1_Refresh))
    End Sub
End Class


Mon but étant d'avoir une vitesse d'exécution sensiblement identique au mode "Merge" mais avec un affichage correct

Merci à ceux qui s'intéresseront au problème (je me casse les dents dessus depuis qq heures déjà )

Amicalement
1000 recherches sur Google = 1Km de voiture en CO² (réfuté par Google )
1000 recherches sur Forestle = 100 m² de forêt tropicale sauvé .
Surfez écolo

1 réponse

Polack77 Messages postés 1098 Date d'inscription mercredi 22 mars 2006 Statut Membre Dernière intervention 22 octobre 2019 1
21 janv. 2011 à 15:43
Bonjour,

Merci à Patrice Scribe sur le forum MSDN sans qui je ne me serais pas pencher sur le virtual mode tout de suite .

Voila un code qui fonctionne :
Comme dans le 1ér code il faut simplement mettre un DataGridView (DataGridView1) sur un form (Form1) d'une nouvelle application et mettre le code ci-dessous dans le code de cette form :
Public Class Form1
  Private Class DonneesPourThread
    Public NombreDePage As Integer
    Public NombreDeLigneParPage As Integer
  End Class

  Private ThreadRempli As System.Threading.Thread
  Private WithEvents TableDonnees As DataTable
  Private Delegate Sub Delegate_DataGridView1_RowCount(ByVal NewRowsCount As Integer)
  Private Delegate Sub Delegate_DataGridView1_Colums_Add(ByRef Colonne As DataGridViewColumn)

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    'Juste pour que le DataGridView remplisse tout le form ;)
    DataGridView1.Dock = DockStyle.Fill
    'Active le mode virtuelle
    DataGridView1.VirtualMode = True

    'Préparation du thread
    ThreadRempli = New System.Threading.Thread(AddressOf SubThreadRemplisage)
    ThreadRempli.IsBackground = True 'Permet au system de tué le thread quand le conteneur se termine 

    'Prepare les infos pour le thread
    Dim InfoThread As New DonneesPourThread
    InfoThread.NombreDeLigneParPage = 1000
    InfoThread.NombreDePage = 100

    'Lance le thread
    ThreadRempli.Start(InfoThread)
  End Sub

  Private Sub SubThreadRemplisage(ByVal Info As DonneesPourThread)

    'Prépare la table recevant les données
    TableDonnees = New DataTable("Table de données")
    TableDonnees.Columns.Add("Id", System.Type.GetType("System.Int32"))
    TableDonnees.Columns.Add("Donnée", System.Type.GetType("System.String"))
    'Remarque :
    'En base de données, on peut executer la requete :
    'RequeteAExecuter "SELECT * FROM (" & RequeteReel & ") a WHERE 1 0"
    'Ainsi on ne récupére que les colonnes et aucune données (ce qui peut être pratique ;))

    'On ajoute un colonne pour chaque colonne de la table
    '/!\ Ici seul des colonne de type textbox sont ajouter (il faudra tester le type de colonne de la table pour ajouter' d'autre type de colonne, tél que DataGridViewCheckBoxColumn par exemple
    For Each ColonneTable As DataColumn In TableDonnees.Columns
      Dim NewDataGridViewColumn As New DataGridViewTextBoxColumn()
      NewDataGridViewColumn.Name = ColonneTable.ColumnName
      NewDataGridViewColumn.HeaderText = ColonneTable.ColumnName
      Invoke(New Delegate_DataGridView1_Colums_Add(AddressOf DataGridView1_Colums_Add), NewDataGridViewColumn)
    Next
    'On lit les pages de données
    For CompteurPage As Integer = 1 To Info.NombreDePage
            Dim TablePage As DataTable = GetPageDonnees(Info.NombreDeLigneParPage)
            'Evite que plusieurs threads accèdent à cette objet en même temps
            SyncLock TableDonnees
                TableDonnees.Merge(TablePage)
            End SyncLock
            If DataGridView1.AllowUserToAddRows Then
                Invoke(New Delegate_DataGridView1_RowCount(AddressOf DataGridView1_RowCount), TableDonnees.Rows.Count + 1)
            Else
                Invoke(New Delegate_DataGridView1_RowCount(AddressOf DataGridView1_RowCount), TableDonnees.Rows.Count)
            End If
        Next
  End Sub

  Private Function GetPageDonnees(ByVal NombreLigneParPage As Integer) As DataTable
    'Prépare la table contenant la page de données
    Dim TablePage As New DataTable("Page de données lut en base de données")
    TablePage.Columns.Add("Id", System.Type.GetType("System.Int32"))
    TablePage.Columns.Add("Donnée", System.Type.GetType("System.String"))
    'Sumule une attente de donnée du serveur (3 secondes)
    System.Threading.Thread.Sleep(3000)
    For Compteur As Integer = 1 To NombreLigneParPage
      Dim Ligne As DataRow = TablePage.NewRow
      Ligne.Item(0) = TablePage.Rows.Count + TableDonnees.Rows.Count + 1
      Ligne.Item(1) = "Donnée de la ligne : " & Ligne.Item(0)
      TablePage.Rows.Add(Ligne)
    Next

    Return TablePage
  End Function

  Private Sub DataGridView1_RowCount(ByVal NewRowsCount As Integer)
    DataGridView1.RowCount = NewRowsCount
  End Sub

  Private Sub DataGridView1_Colums_Add(ByRef Colonne As DataGridViewColumn)
    DataGridView1.Columns.Add(Colonne)
  End Sub

  Private Sub DataGridView1_CellValueNeeded(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellValueEventArgs) Handles DataGridView1.CellValueNeeded
    'Si l'ajout est autorisé
    If DataGridView1.AllowUserToAddRows Then
      'Si cette ligne est la ligne d'ajout
      If e.RowIndex = Me.DataGridView1.RowCount - 1 Then
        'On sort de la fonction
        Return
      End If
    End If
    SyncLock TableDonnees
      e.Value = TableDonnees.Rows(e.RowIndex).Item(e.ColumnIndex)
    End SyncLock
  End Sub
End Class


Tout n'est pas fait dans ce code, seul l'affichage de texte est fait (pas d'ajout, de suppression, modification, de données, ni de colonne de type ChekBox ou autre). Mais bon je pense que sa reste un bonne exemple . Pour faire le reste il faut se référer à la rubrique d'aide que Patrice Scribe m'a indiquer : http://msdn.microsoft.com/fr-fr/library/ms171622.aspx

REMARQUE IMPORTANTE :
DataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells

Est impérativement à éviter cela provoque de TRÈS GROS ralentissement

Amicalement
1000 recherches sur Google = 1Km de voiture en CO² (réfuté par Google )
1000 recherches sur Forestle = 100 m² de forêt tropicale sauvé .
Surfez écolo
3
Rejoignez-nous