2017-02-14 2 views
0

Je travaille dans l'application WinForms et j'ai utilisé le contrôle DataGridView dans mon application. Au départ, j'ai chargé les 10000 lignes et 50 colonnes. Mon scénario est que la mise à jour de la source de données à un intervalle de temps particulier (en utilisant Timer).Comment éviter que le contrôle ne gèle lors de la mise à jour de la source de données lors de l'exécution?

Problème: La grille a été gelée/gang lors de l'exécution de l'action (cell_click, scrolling, etc.) lors de la mise à jour de la source de données.

Comment résoudre ce problème? Y a-t-il des solutions de rechange? S'il vous plaît me suggérer vos idées.

Voici mon code à ce jour:

private void timer1_Tick(object sender, EventArgs e) 
    { 
     //try 
     { 
      timer.Stop(); 
      for (int i = 0; i < 10000; i++) 
      { 
       var row = r.Next() % 10000; 
       for (int col = 1; col < 10; col++) 
       { 
        var colNum = r.Next() % 55; 
        if (table != null) 
         table.Rows[row][colNum] = "hi";// r.Next().ToString(); 
       } 
      } 
      table.AcceptChanges(); 
      timer.Start(); 
     } 
    } 

Voici un exemple de sortie:

[https://drive.google.com/open?id=0B9MOGv1FOt-TQ1BNZWtnRktxeXc]

Merci.

+0

Quelle est la fréquence de mise à jour des données? Combien de temps faut-il habituellement pour terminer? –

+0

Je crains que 'DataSet' et' DataGridView' ne soient simplement conçus pour que des dizaines de milliers de lignes soient mises à jour en temps réel. Vous souhaitez probablement une grille de données avec un rendu virtualisé, et idéalement avec une source de données qui n'est pas étroitement liée à l'interface utilisateur. – Luaan

+0

Utilisez-vous un BindingSource en tant que DataSource de DataGridView? – TnTinMn

Répondre

1

Essayez d'utiliser un BackgrounWorker.

BackgroundWorker est une classe d'aide dans l'espace de noms System.ComponentModel pour la gestion d'un thread de travail. Il peut être considéré comme une mise en œuvre à usage général du PAE (Le modèle asynchrone basé événementielle), et fournit les caractéristiques suivantes:

  • Un modèle d'annulation coopérative

  • La capacité de mettre à jour en toute sécurité WPF ou des contrôles Windows Forms lorsque le travailleur accomplit

  • Transmission des exceptions à l'événement d'achèvement
  • Un protocole pour rendre compte des progrès
  • Une mise en œuvre de IComponent lui permettant d'être situé dans le concepteur de Visual Studio

Bellow vous pouvez trouver un exemple, s'il vous plaît adapter à votre minuterie:

class Program 
{ 
    static BackgroundWorker _bw = new BackgroundWorker(); 

    static void Main() 
    { 
    _bw.DoWork += bw_DoWork; 
    _bw.RunWorkerAsync ("Message to worker"); 
    Console.ReadLine(); 
    } 

    static void bw_DoWork (object sender, DoWorkEventArgs e) 
    { 
    // This is called on the worker thread 
    Console.WriteLine (e.Argument);  // writes "Message to worker" 
    // Perform time-consuming task... 


      //update your grid 
      for (int i = 0; i < 10000; i++) 
      { 
       var row = r.Next() % 10000; 
       for (int col = 1; col < 10; col++) 
       { 
        var colNum = r.Next() % 55; 
        if (table != null) 
         table.Rows[row][colNum] = "hi";r.Next().ToString(); 
       } 
      } 
      table.AcceptChanges(); 

    } 
} 
+3

Je suis assez sûr que cela va causer des problèmes, car il semble que le DataGrid est databound à la table de données. Donc, vous allez obtenir des mises à jour de l'interface utilisateur sur un thread non-interface utilisateur. – Luaan

+0

C'est pourquoi vous devez utiliser BackgroundWorker, pour exécuter une opération sur un thread séparé, alors que vous pouvez toujours réutiliser le thread principal pour mettre à jour votre interface utilisateur. Vous pouvez trouver un bon exemple sur le msdn https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx – Zinov

+1

Dans l'exemple de msdn, l'interface utilisateur Les éléments sont réellement mis à jour à partir du thread UI. C'est pourquoi la progression est mise à jour via la fonction ReportProgress qui appelle le thread UI. Vérifiez-le en mettant un point d'arrêt dans le gestionnaire d'événements ProgressChanged et vérifiez quel thread est en cours d'exécution. Vous verrez, c'est le fil conducteur. Ainsi, votre exemple provoquera une exception InvalidOperationException. – sbecker

1

L'une des solutions consiste à appeler le Application.DoEvents() pendant une telle opération de longue durée. Voici l'exemple

private void timer1_Tick(object sender, EventArgs e) 
{ 
    //try 
    { 
     timer.Stop(); 
     for (int i = 0; i < 10000; i++) 
     { 
      var row = r.Next() % 10000; 
      for (int col = 1; col < 10; col++) 
      { 
       var colNum = r.Next() % 55; 
       if (table != null) 
        table.Rows[row][colNum] = "hi";// r.Next().ToString(); 
      } 
      Application.DoEvents(); //add this line 
     } 
     table.AcceptChanges(); 
     timer.Start(); 
    } 
} 

Une autre solution consiste à déplacer votre tâche longue en un thread séparé.

+0

Merci. Comment Application.DoEvents() fonctionne? parce que ma grille n'a pas été mise à jour si je l'utilise. mais parfois les valeurs de la grille ne sont modifiées que pour quelques colonnes. – Prithiv

+0

@Prithiv Il exécute une itération de la boucle de message. Le principal problème avec ceci est qu'il permet la ré-existence, ce qui est quelque peu difficile à raisonner (cela pourrait provoquer l'exécution alternée de deux ou plusieurs des gestionnaires 'Tick' en parallèle, par exemple). Et même si vous partez de cette façon et ne restreignez en aucun cas la renaissance, vous ne voulez vraiment pas le faire à chaque itération - cela peut devenir très lent en effet. Au lieu de cela, faites-le seulement assez souvent pour empêcher l'application de ne pas répondre pendant une période de temps significative (disons, 20-200ms). – Luaan