2010-09-23 6 views
0

J'ai un GridControl que je remplis en utilisant un BackgroundWorker. Ensuite, j'utilise un autre BackgroundWorker pour effectuer des calculs sur l'ensemble de données qui est la source de données du GridControl. Comme j'essaye de faire ceci une opération croisée sur l'erreur GridControl est jetée. Je suis incapable de comprendre que, malgré le fait de ne pas effectuer d'opération sur le contrôle de la grille lui-même, comment l'erreur est générée. (J'utilise DevExpress, mais cela ne devrait pas changer le concept).Travailler avec la grille en utilisant BackgroundWorker

Je peux également utiliser un BackgroundWorker pour effectuer un travail différent, c'est-à-dire rendre ce code plus efficace.

Voici mon code: -

public partial class MainForm : XtraForm 
    { 
     private BackgroundWorker loadworker = new BackgroundWorker(); 
     private BackgroundWorker calcworker = new BackgroundWorker(); 
     private AutoResetEvent resetEvent = new AutoResetEvent(false); 
     private Database _db = EnterpriseLibraryContainer.Current.GetInstance<Database>("ConnString"); 
     private DataSet ds; 

     public MainForm() 
     { 
      InitializeComponent(); 

      loadworker.DoWork += loadworker_DoWork; 
      loadworker.RunWorkerCompleted += loadworker_RunWorkerCompleted; 
      loadworker.ProgressChanged += loadworker_ProgressChanged; 
      loadworker.WorkerReportsProgress = true; 

      calcworker.DoWork += calcworker_DoWork; 
      calcworker.RunWorkerCompleted += calcworker_RunWorkerCompleted; 
      calcworker.ProgressChanged += calcworker_ProgressChanged; 
      calcworker.WorkerReportsProgress = true; 
     } 

     private void calcworker_DoWork(object sender, DoWorkEventArgs e) 
     { 
      int _cnt = 0; 
      foreach (DataRow dr in ds.Tables[0].Rows) 
      { 
       dr["GROSS"] = (decimal)dr["BASIC"] + (decimal)dr["HRA"] + (decimal)dr["DA"]; 
       _cnt += 1; 
      } 

      for (int i = 0; i <= _cnt; i++) 
      { 
       Thread.Sleep(100); 
       calcworker.ReportProgress((100 * i)/_cnt); 
      } 
     } 

     private void calcworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
     { 
      this.SetState(true); 
      this.MainInit(); 
     } 

     private void calcworker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      this.pgb_DataProgress.Position = e.ProgressPercentage; 
     } 


     private void loadworker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      this.pgb_DataProgress.Position = e.ProgressPercentage; 
     } 

     private void loadworker_DoWork(object sender, DoWorkEventArgs e) 
     { 
      try 
      { 
       DbCommand _cmd = _db.GetSqlStringCommand("SELECT Z.EMP_CODE,Z.BASIC,Z.DA,Z.HRA,CAST(0 AS DECIMAL) GROSS FROM Z000000001 Z"); 
       DataSet _data = _db.ExecuteDataSet(_cmd); 

       for (int i = 0; i <= 10; i++) 
       { 
        Thread.Sleep(500); 
        loadworker.ReportProgress((100 * i)/10); 
       } 

       e.Result = _data; 
      } 
      catch (Exception ex) 
      { 
       e.Cancel = true; 
      } 
     } 

     private void loadworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
     { 
      this.ds = (DataSet)e.Result; 
      this.gridControl1.DataSource = ds.Tables[0]; 
      this.SetState(true); 
      this.MainInit(); 
     } 

     private void btn_FetchData_Click(object sender, EventArgs e) 
     { 
      this.gridControl1.DataSource = null; 
      this.SetState(false); 
      loadworker.RunWorkerAsync(); 
     } 

     private void SetState(bool _state) 
     { 
      this.btn_Calculate.Enabled = _state; 
      this.btn_ClearGrid.Enabled = _state; 
      this.btn_FetchData.Enabled = _state; 
     } 

     private void MainInit() 
     { 
      this.pgb_DataProgress.Position = 0; 
     } 

     private void btn_ClearGrid_Click(object sender, EventArgs e) 
     { 
      this.gridControl1.DataSource = null; 
     } 

     private void btn_Calculate_Click(object sender, EventArgs e) 
     { 
      if (this.gridControl1.DataSource == null) 
      { 
       DevExpress.XtraEditors.XtraMessageBox.Show("Data Not loaded", "Message"); 
       return; 
      } 
      else 
      { 
       this.SetState(false); 
       calcworker.RunWorkerAsync(); 
      } 
     } 

    } 
+1

Vous modifiez le DataRow depuis 'calcworker_DoWork'. Grâce à la notification de changement, votre grille sera informée et ceci est effectué à partir du même fil que le changement de ligne. C'est pourquoi le code jette une exception. Une possibilité serait de définir la ligne dans loadworker_ProgressChanged. Vous pouvez transférer les données via UserState de la méthode ReportProgress. – HCL

+0

@HCL: Alors quelle devrait être l'approche. Dois-je mettre la source de données GridControl à zéro, puis effectuer les calculs, puis définir à nouveau la source de données. Cette méthode fonctionne mais est-ce un bon moyen ou suggérez-vous le contraire. –

+0

La façon dont vous dites est le moyen le plus rapide car il n'y a pas de communication croisée. Cependant, l'expérience de l'utilisateur peut ne pas être si bonne si c'est une opération à long terme. Une possibilité est d'utiliser Control.Invoke comme écrit par VinayC ou d'afficher une barre de progression ou de définir les données de la ligne dans l'événement ProgressChanged. – HCL

Répondre

0

En bref, vous ne pouvez pas accéder à des commandes sur un thread autre que le fil interface utilisateur sur lequel ils sont créés. Ainsi, tout appel de méthode/propriété de contrôle doit être marshall sur le thread UI en utilisant la méthode Control.Invoke. Par exemple, dans votre cas, le gestionnaire d'événements loadworker_RunWorkerCompleted sera appelé sur un thread de travail et l'accès à la propriété de contrôle produira une erreur. Vous devez modifier le gestionnaire d'événements en tant que

private void loadworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     System.Action a =() => { 
      this.ds = (DataSet)e.Result; 
      this.gridControl1.DataSource = ds.Tables[0]; 
      this.SetState(true); 
      this.MainInit(); 
     }; 
     this.gridControl1.Invoke(a); 
    } 
+0

@VinayC: Quelle est la meilleure façon de résoudre ce problème. –

+0

@Soham, édité pour montrer comment utiliser invoke lors d'une opération cross-thread. Vous devez utiliser ce code à chaque méthode d'accès aux contrôles qui peuvent être invoqués sur un thread différent. – VinayC

+2

-1, c'est faux! L'événement Completed est exécuté sur le thread principal de l'interface graphique, ce qui fait que cette fonctionnalité Invoke est totalement inutile. Et certainement pas résoudre le problème. –

1

Après avoir attaché la table en tant que DataSource, elle appartient à l'interface graphique. Supposons que votre utilisateur modifie/supprime une ligne pendant que votre thread Calc est en cours d'exécution. Toutes sortes de conditions de course peuvent arriver.

+0

Merci pour l'info. –

Questions connexes