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.
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();
}
In my Windows Form application I have created 2 Forms. In form 1 when I click button1, a new task will start. Inside the task I have created an instance of the form2 and show form2. I am calling the showData Method of Form2.
//Form1
public event TickHandler Tick;
public EventArgs e = null;
public delegate void TickHandler(int a1, EventArgs e);
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
Form2 form2 = new Form2();
form2.Show();
}
}
//Form2
public void showData(Form1 m)
{
m.Tick += new Form1.TickHandler(test);
}
public void test(int a1,EventArgs e)
{
Task.Factory.StartNew(() =>
{
for (int i = a1; i < 1000; i++)
{
label1.Invoke(new MethodInvoker(delegate { label1.Text = i.ToString(); }));
}
});
}
As kenny suggested i have modified the code. now it running how i am expected.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
Action act1 = (() =>
{
Form2 form2 = new Form2();
form2.StartPosition = FormStartPosition.CenterParent;
form2.Show();
});
this.BeginInvoke(act1);
});
}
}
// FORM2
private void Form2_Load(object sender, EventArgs e)
{
test(1);
}
public void test(int a1)
{
Task.Factory.StartNew(() =>
{
for (int i = a1; i < 1000; i++)
{
label1.Invoke(new MethodInvoker(delegate { label1.Text = i.ToString(); }));
}
});
}
Once again thanks Kenny
I have this situation: a class that contains a background worker that do some thing in a while cycle:
public class CControls
{
public delegate void ControlChangedEventHandler();
public event ControlChangedEventHandler ControlChangedEvent;
private readonly BackgroundWorker worker;
bool bClose = false;
public CControls(IntPtr hwnd)
{
worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bClose = true;
}
public void enable(bool bEnable)
{
if (bEnable && !worker.IsBusy)
{
worker.RunWorkerAsync();
}
else
{
bClose = true;
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (!bClose)
{
// my job
// ..............................................
//
if (ControlChangedEvent != null)
{
ControlChangedEvent();
}
}
}
}
I have my form that create an instance of this class and set the listener of ControlChangedEvent:
CControls ct = new CControls();
ct.ControlChangedEvent += ct_ControlChangedEvent;
int changes = 0;
void ct_ControlChangedEvent()
{
Dispatcher.Invoke(DispatcherPriority.Background, (Action)delegate
{
changes ++;
infoLabel.Content = string.Format("changes: {0}", changes);
});
}
but the infoLabel changes only if my program have the focus, otherwise is not fired...
any ideas?
thanks ;)
I am trying to run a function in a different class than the dispatcher through a backgroundworker and have it update the progress on every iteration. I am getting no errors and the backgroundworker is functioning properly, but my textbox never updates...
public partial class MainWindow : Window
{
public BackgroundWorker worker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(workerDoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(workerProgressChanged);
}
private void myButtonClick(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync();
}
void workerDoWork(object sender, DoWorkEventArgs e)
{
yv_usfm.convert(worker);
}
void workerProgressChanged(object sender, ProgressChangedEventArgs e)
{
myTextBox.Text = "some text";
}
}
public class yv_usfm
{
public static void convert(BackgroundWorker worker)
{
int i = 1;
while (i < 100)
{
worker.ReportProgress(i);
i++;
}
}
}
What makes you say the BackgroundWorker is functioning properly? I see no call to worker.RunWorkerAsync(), and without that it will never start.
You're not starting the worker!
worker.RunWorkerAsync();
Try This:
void DoWork(...)
{
YourMethod();
}
void YourMethod()
{
if(yourControl.InvokeRequired)
yourControl.Invoke((Action)(() => YourMethod()));
else
{
//Access controls
}
}
Hope This help.
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);
}