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();
}
}
}
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.
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);
}
Related
I have a label that I want to update every 5 seconds. It should change from 1921 to 1922 onward till 1992. I have tried using a timer but it gave me an error about being accessed on the wrong thread. The code I used was:
public partial class Form1 : Form
{
int x = 1921;
public Form1()
{
InitializeComponent();
}
System.Timers.Timer myTimer = new System.Timers.Timer(1000);
private void UpdateLabel(object sender, ElapsedEventArgs e)
{
label1.Text = x.ToString();
x += 1;
}
private void Form1_Load(object sender, EventArgs e)
{
myTimer.Elapsed += UpdateLabel;
myTimer.Start();
}
}
Try this:
private void UpdateLabel(object sender, ElapsedEventArgs e)
{
//Invoke makes the UI thread call the delegate.
Invoke((MethodInvoker)delegate {label1.Text = x.ToString(); });
x += 1;
}
try this
private readonly object y = new object();
int x = 1921;
public Form1()
{
InitializeComponent();
}
System.Timers.Timer myTimer = new System.Timers.Timer(1000);
private void UpdateLabel(object sender, ElapsedEventArgs e)
{
Invoke((MethodInvoker)(() => { lock (y) { label1.Text = x.ToString(); x++; } }));
}
private void Form1_Load(object sender, EventArgs e)
{
myTimer.Elapsed += UpdateLabel;
myTimer.Start();
}
I'm tryig to make a program that can auto press space.
but i can only enable it. after the program crashes.
so i cant turn it off. pleace help
here is my code:
using System.Windows.Forms;
namespace Auto_Abillty
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public bool startFlag = false;
public bool stopFlag = false;
private void Preiststop_Click(object sender, EventArgs e)
{
stopFlag = true;
}
private void Preist_Click(object sender, EventArgs e)
{
startFlag = true;
while (startFlag)
{
SendKeys.Send(" ");
Thread.Sleep(5000);
if (stopFlag)
startFlag = false;
}
}
}
}
I would suggest using System.Windows.Forms.Timer with 5000 ms interval instead of your approach. Then by clicking on the button on a form you may start or stop it.
Another approach is to move your while loop into another thread that will make UI thread still be responsive
You must abort the thread as well.
public bool x = false;
public bool j = false;
private void Preiststop_Click(object sender, EventArgs e)
{
j = true;
}
private void Preist_Click(object sender, EventArgs e)
{
x = true;
Thread newThread = new Thread(delegate ()
{
DoPriestWork();
});
newThread.Start();
//loop to wait for the response from DoPriestWork thread
while (x)
{
Thread.Sleep(5000);
if (j)
x = false;
}
newThread.Abort();
}
public void DoPriestWork()
{
//x = true;
//while (x == true)
//{
SendKeys.Send(" ");
//Thread.Sleep(5000);
// if (j == true)
// x = false;
//}
}
Seems like all you need is a thread so another button can be pushed to change the condition of "j". If there is only one thread, the UI will not be updated until that thread is stopped hence the need to make another thread, like below example.
public Form1()
{
InitializeComponent();
}
public bool x = false;
public bool j = false;
private void Preiststop_Click(object sender, EventArgs e)
{
j = true;
}
private void Preist_Click(object sender, EventArgs e)
{
Thread newThread = new Thread(DoPriestWork);
newThread.Start();
}
public void DoPriestWork()
{
x = true;
while (x == true)
{
SendKeys.Send(" ");
Thread.Sleep(5000);
if (j == true)
x = false;
}
}
Your Preist_Click call blocks the UI: it executes in the same thread, instead of starting its own.
Try this:
private bool keepRunning = true;
private void Preiststop_Click(object sender, EventArgs e)
{
keepRunning = false;
}
private async void Preist_Click(object sender, EventArgs e)
{
keepRunning = true;
await Task.Run(() => {
while (keepRunning)
{
SendKeys.Send(" ");
Thread.Sleep(5000);
}
});
}
Currently im trying to update my progress bar if the background worker reports something, heres my code
Form1.cs
namespace YTD
{
public partial class Form1 : Form
{
private Main app;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
int n;
bool isNumeric = int.TryParse(numberBox.Text, out n);
if (!String.IsNullOrWhiteSpace(emailBox.Text) && !String.IsNullOrWhiteSpace(passBox.Text) && !String.IsNullOrWhiteSpace(numberBox.Text) && isNumeric)
{
this.app = new Main(emailBox.Text, passBox.Text, n, logBox, statusBar, backgroundWorker1);
this.app.startMule();
}
else
{
MessageBox.Show("Please fill out all the form fields", "MuleMaker error");
}
}
}
}
And my Main.cs
namespace YTD.classes
{
public class Main
{
private String email;
private String password;
private int number;
private RichTextBox logBox;
private ProgressBar statusBar;
private BackgroundWorker threadWorker;
public Main(String email, String password, int number, RichTextBox logBox, ProgressBar statusBar, BackgroundWorker threadWorker)
{
// Define some variables
this.email = email;
this.password = password;
this.number = number;
this.logBox = logBox;
this.statusBar = statusBar;
this.threadWorker = threadWorker;
}
public void startMule()
{
// Set progressbar 100% value
statusBar.Maximum = this.number;
if (!threadWorker.IsBusy)
{
threadWorker.RunWorkerAsync();
}
}
private void threadWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 10; i++)
{
// Perform a time consuming operation and report progress.
MessageBox.Show("ye");
System.Threading.Thread.Sleep(500);
threadWorker.ReportProgress(i * 10);
}
}
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Increment(1);
}
}
}
Currently I get no errors but the progress bar value is not beeing changed.
Without the background worker i can update my progress bar fine but not while doing an expensive action.
Your posted Code does not reveal, if you registered your functions to the BackgroundWorker Events.
Creating a new BackgrounWorker isn't enough.
Here is an example:
public Class Main
{
public Main( ... )
{
BackgroundWorker worker = new BackgroundWorker()
worker.WorkerReportsProgress = true;
// Register to BackgroundWorker-Events
worker.DoWork += threadWorker_DoWork;
worker.ProgressChanged += threadWorker_ProgressChanged;
}
}
in addition you should tell your ProgressBar to rerender.
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Increment(1);
statusBar.Invalidate(true);
}
at least you might want to use the value you have set calling ReportProgress(i * 10).
private void threadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
statusBar.Value = e.ProgressPercentage;
statusBar.Invalidate(true);
}
I am trying to display a list of 10 items within a list box and once it reaches that limit the listbox clears itself and the threading starts again. Here is the code that I have so far:
List<string> MyList { get; set; }
public Form1()
{
InitializeComponent();
List<string> MyList = new List<string>();
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => MethodToDoWork();
bw.RunWorkerCompleted += (sender, args) => MethodToUpdateControl();
bw.RunWorkerAsync();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
frm2.Show();
this.Hide();
}
private void Form1_Load(object sender, EventArgs e)
{
lbxPosition.Items.Clear();
}
private void MethodToDoWork()
{
for (int i = 0; i < 10; i++)
{
MyList.Add(string.Format("Temprature {0}", i));
Thread.Sleep(100);
}
}
private void MethodToUpdateControl()
{
lbxPosition.Items.AddRange(MyList.ToArray());
}
Only thing is I am getting an error at line MyList.Add(string.Format("Temprature {0}", i));. My error is An exception of type 'System.NullReferenceException' occurred in s00107997.exe but was not handled in user code. Can anyone see where I am going wrong?
You have to assign your global variable inside your Form without re-writing your variable's name. In addition, do not use Thread.Sleep.
List<string> MyList { get; set; }
public Form1()
{
InitializeComponent();
MyList = new List<string>(); //Here was wrong.
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => MethodToDoWork();
bw.RunWorkerCompleted += (sender, args) => MethodToUpdateControl();
bw.RunWorkerAsync();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
frm2.Show();
this.Hide();
}
private void Form1_Load(object sender, EventArgs e)
{
lbxPosition.Items.Clear();
}
private void MethodToDoWork()
{
for (int i = 0; i < 10; i++)
{
MyList.Add(string.Format("Temprature {0}", i));
}
}
private void MethodToUpdateControl()
{
lbxPosition.Items.AddRange(MyList.ToArray());
}
If you want to perform such as task inside of a form, I would recommend you to use the Timer. It is better supported in WinForms as your own threads. You can get a number of problems when accessing a WinForm from another thread.
Ok so I have the following code:
public partial class Form1 : Form
{
private void FileWatcher_Created(object sender, System.IO.FileSystemEventArgs e)
{
ListViewItem newFile = new ListViewItem(new string[] { e.FullPath.ToString(), e.ChangeType.ToString() }, -1);
newFile.Tag = e.FullPath.ToString();
FileList.Items.Add(newFile);
}
private void CopyButton_Click(object sender, EventArgs e)
{
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int TotalFiles = FileList.CheckedItems.Count;;
int CurrentFile = 1;
foreach (ListViewItem CheckedFile in FileList.CheckedItems)
{
backgroundWorker1.ReportProgress((CurrentFile / TotalFiles) * 100);
string FileBuilder = Settings.Default.Destination + Path.GetFileName(CheckedFile.Tag.ToString());
if (File.Exists(FileBuilder) == false)
{
File.Copy(CheckedFile.Tag.ToString(), FileBuilder);
}
CurrentFile++;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
CopyProgressBar.Value = e.ProgressPercentage;
}
}
It keeps telling me that the report progress method in the DoWork event doesn't exist in the current context, anyone know why? Please forgive me if this is a noob error, im new.
backgroundWorker1 is a local variable.
It doesn't exist outside of CopyButton_Click.
You can either put it in a class field, or cast it from the sender parameter.
private void CopyButton_Click(object sender, EventArgs e)
{
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
}
you BackgroundWorker is init on start method and dispose when finish
try to declare BackgroundWorker in class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using FileWatchDog.Properties;
using System.Text.RegularExpressions;
namespace FileWatchDog
{
public partial class Form1 : Form
{
BackgroundWorker backgroundWorker1;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void FileWatcher_Created(object sender, System.IO.FileSystemEventArgs e)
{
ListViewItem newFile = new ListViewItem(new string[] { e.FullPath.ToString(), e.ChangeType.ToString() }, -1);
newFile.Tag = e.FullPath.ToString();
FileList.Items.Add(newFile);
}
private void CopyButton_Click(object sender, EventArgs e)
{
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int TotalFiles = FileList.CheckedItems.Count;;
int CurrentFile = 1;
foreach (ListViewItem CheckedFile in FileList.CheckedItems)
{
backgroundWorker1.ReportProgress((CurrentFile / TotalFiles) * 100);
string FileBuilder = Settings.Default.Destination + Path.GetFileName(CheckedFile.Tag.ToString());
if (File.Exists(FileBuilder) == false)
{
File.Copy(CheckedFile.Tag.ToString(), FileBuilder);
}
CurrentFile++;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
CopyProgressBar.Value = e.ProgressPercentage;
}
}
}