Pergunta

I have a GridControl which I populate using a BackgroundWorker. Then I'm using another BackgroundWorker to perform some calculations on the dataset which is the datasource of the GridControl. As I'm trying to do this a cross thread operation on the GridControl error is thrown. I'm unable to understand that despite not performaing any operation on the gridcontrol itself how the error is generating. (I'm using DevExpress, but that should not change the concept).

Also is there any way I can use one BackgroundWorker to do different work, i.e. make this code more efficient.

Here is my 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();
            }
        }

    }
Foi útil?

Solução

In short, you cannot access controls on a thread other than UI thread on which they are created. So any control method/property call has to be marshall on the UI thread using Control.Invoke method.

For example, in your case loadworker_RunWorkerCompleted event handler will be invoked on a worker thread and accessing control property will throw an error. You need to modify event handler as

    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);
    }

Outras dicas

After you attached the Table as DataSource it belongs to the GUI. Suppose your user alters/deletes a row while your Calc thread is running. All sorts of race conditions might happen.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top