Updating GUI multithreading application - c#

I have main program
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Worker w1 = new Worker(1);
Worker w2 = new Worker(2);
Thread w1Thread = new Thread(new ThreadStart(w1.StartWorking));
Thread w2Thread = new Thread(new ThreadStart(w2.StartWorking));
w1Thread.Start();
w2Thread.Start();
Application.Run(new MainWindow());
if (w1Thread.IsAlive)
{
w1Thread.Abort();
}
if (w2Thread.IsAlive)
{
w2Thread.Abort();
}
}
}
and worker class:
class Worker
{
public int m_workerId;
public bool m_workerLifeBit;
public bool m_workerWork;
public Worker(int id)
{
m_workerId = id;
m_workerLifeBit = false;
}
public void StartWorking()
{
while (!m_workerWork)
{
m_workerLifeBit = false;
System.Threading.Thread.Sleep(5000);
m_workerLifeBit = true;
System.Threading.Thread.Sleep(5000);
}
}
}
I have checkBox on MainWindow form.
How to monitor state of Worker variable m_workerLifeBit and display its changes in MainWindow checkBox?
I have found this q&a How to update the GUI from another thread in C#? hovewer the answer does not show complete example, and I failed with using thread safe delegate.
I want some event mechanism that I fire in Worker.StartWorking and catch in slot in MainWindow form.

Here is a simple version using events:
class Worker
{
public event Action<bool> WorkerLifeBitChanged;
// ...
public void StartWorking()
{
// ...
m_workerLifeBit = false;
OnWorkerLifeBitChanged();
// ...
private void OnWorkerLifeBitChanged()
{
if (WorkerLifeBitChanged != null)
WorkerLifeBitChanged(m_workerLifeBit);
}
Then you wire up the event in Main:
//...
var mainWindow = new MainWindow();
w1.WorkerLifeBitChanged += mainWindow.UpdateWorkerLifeBit;
w2.WorkerLifeBitChanged += mainWindow.UpdateWorkerLifeBit;
w1Thread.Start();
w2Thread.Start();
Application.Run(mainWindow);
//...
And UpdateWorkerLifeBit implementation in MainWindow:
public void UpdateWorkerLifeBit(bool workerLifeBit)
{
if (this.checkBox.InvokeRequired)
{
this.Invoke(new Action(() => checkBox.Checked = workerLifeBit));
}
else
{
checkBox.Checked = workerLifeBit;
}
}

As mentioned in the comments, if this is a WinForms application then I'd recommend using a BackgroundWorker.
Kicking off the bg worker and subscribing to events:
BackgroundWorker worker = new BackgroundWorker();
// Subscribing to the worker method. Do all of your work here
worker.DoWork += worker_DoWork;
// Subscribing to the progress changed event where you'll want to update the UI
worker.ReportProgress = true;
worker.ProgressChanged += worker_ProgressChanged;
// Subscribing to the worker completed event. Fires when the work is complete
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
// This line starts the worker
worker.RunWorkerAsync();
You would then have your methods defined as such:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Perform some work with the object you've passed in e.g.
MyObj foo = (MyObj)e.Argument;
foo.Name = "foobar";
// Notify UI
worker.ReportProgress(100, foo);
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Update UI
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Worker has finished
}

One solution would be passing a reference of your Program class (or even a delegate in your program class, or a data reference in your worker class) to the Worker thread. You can call a function of your Program directly from the thread code then. You can also use signals, but for this small example my previous "solution" is acceptable.

Related

How to run a windows form from a backgroundworker runnig through another backgroundworker?

this is the simplified plan for a solution:
for some reasons i need to run a windows form through a backgroundworker that is runnig by another backgroundworker, when the new windows form loads, the older backgroundworker must pause. i write the code like this :
creating a class with name : temp
public class temp
{
static public BackgroundWorker backgroundWorker1 = new BackgroundWorker() { WorkerSupportsCancellation = true };
static public EventWaitHandle ew = new EventWaitHandle(false, EventResetMode.ManualReset);
static public BackgroundWorker back = new BackgroundWorker() { WorkerSupportsCancellation = true };
}
the codes for form1 are :
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
temp.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
temp.back.DoWork += new DoWorkEventHandler(back_DoWork);
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
Form2 f = new Form2();
f.Show();
}
private void button1_Click(object sender, EventArgs e)
{
temp.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
temp.back.RunWorkerAsync();
if (temp.backgroundWorker1.CancellationPending)
temp.ew.WaitOne();
}
}
}
and the codes of form2 goes here :
namespace WindowsFormsApplication1
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
temp.backgroundWorker1.CancelAsync();
temp.ew.Reset();
}
}
}
by clicking the button1 from form1 the temp.backgroundworker1 runs and then in the DoWork of temp.backgroundworker1, the temp.back runs and then FORM2 LOADS BUT THE FORM2 HANGS AND BECOMES USELESS AND YOU CANNOT USE THAT ANY MORE.
where did i wrong ?
the whole plan that i'm going to execute is :
we have a For loop that processes every row of a DataGridView.
each time in a certain point, another windowsform opens
and it stops the loop until the user inserts the information and then click on OK button, the windowsform closes and the loop keep on working. i dont know what to do.......
even if i dont cancel working of the temp.backgroundworker in form2load like the code below, the Form2 is useless
private void Form2_Load(object sender, EventArgs e)
{
}
Do not use any UI operation in the work thread (DoWork method). Maybe that's why you set the CheckForIllegalCrossThreadCalls property, but your app will not work properly just suppresses the error when the debugger is attached.
See my answer here for the correct usage of the BackgroundWorker (that is about canceling but you can see the operations in UI and worker thread).
In this particular case what you can use a similar volatile bool to sign the UI thread that the form can be shown. Or, if you want to send different messages between the threads, use a ConcurrentQueue<T> to write and read messages:
public partial class Form1 : Form
{
private enum Message
{
ShowForm2,
SuspendWork,
ResumeWork,
FinishWorker1
// ... and whatever you want
}
private Timer timer;
private ConcurrentQueue<Message> messagesToUI = new ConcurrentQueue<Message>();
private ConcurrentQueue<Message> messagesToWorker = new ConcurrentQueue<Message>();
public Form1()
{
InitializeComponent();
timer = new Timer(this);
timer.Interval = 10;
timer.Tick += PollUIMessages;
timer.Enabled = true;
}
void PollUIMessages(object sender, EventArgs e)
{
// do we have a new message?
Message message;
if (messagesToUI.TryDequeue(out message))
{
switch (message)
{
case Message.ShowForm2:
Form2 f = new Form2();
f.Show();
// todo: in Form2.Close add a Resume message to the messagesToWorker
break;
// ... process other messages
}
}
}
void back_DoWork(object sender, DoWorkEventArgs e)
{
// Here you are in the worker thread. You can send a message to the
// UI thread like this:
messagesToUI.Enqueue(Message.ShowForm2);
bool isWorking = true;
// and here you can poll the messages to the worker thread
while (true)
{
Message message;
if (!messagesToWorker.TryDequeue(out message))
{
// no message: idle or work
if (isWorking)
DoSomeWork(); // do whatever you want
else
Thread.CurrentThread.Sleep(10);
continue;
}
switch (message)
{
case Message.FinishWorker1:
// finishing the worker: jumping out
return;
case Message.SuspendWork:
isWorking = false;
break;
case Message.ResumeWork:
isWorking = true;
break;
}
}
}

NullReferenceException happens when sending message back to message dialog from BackGroundWorker

It's a C# winform .Net framework 2.0 project: The time-consuming network transferring jobs run in a BackupgroundWorker. In this backgroundwork thread, SynchronizationContext method is used to send the current job title to a message dialog in the main UI thread. However, in my code, thread racing problem happens and "NullReferenceException" is given sometime (not always, but randomly). Here is my code:
private void DoBtn_Click(object sender, EventArgs e)
{
m_msgForm = new MsgForm(); //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
m_msgForm.UpdateMsg("starting ..."); //UpdateMsg is public method to show progress information
BackgroundWorker myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.DoWork +=new DoWorkEventHandler(myBackgroundWorker_DoWork);
myBackgroundWorker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);
m_msgForm.ShowDialog(); //show as a modal dialog
}
And in the Background worker thread:
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//... some code
string msgText ="doing job: " +job.Title;
RestoreMsgForm.synContext.Send(m_msgForm.UpdateMsg, msgText); //send message to m_msgForm in the UI thread
//... some code
}
The problem is : sometime the "RestoreMsgForm.synContext.Send() in the backgroundworker thread will run before m_msgForm.ShowDialog() in the UI thead.
And in this case, NullReferenceException will be throw.
How to solve this problem? thanks.
the definition of RestoreMsgForm is
public partial class RestoreMsgForm : Form
{
public static SynchronizationContext synContext;
public RestoreMsgForm()
{
InitializeComponent();
}
private void RestoreMsgForm_Load(object sender, EventArgs e)
{
synContext = SynchronizationContext.Current;
}
public void UpdateMsg(object msg)
{
msgLabel.Text = (string)msg;
}
}
See if it works like this instead:
private void DoBtn_Click(object sender, EventArgs e)
{
m_msgForm = new MsgForm(); //m_msgForm is a member variable of the class, and MsgForm is a form class with a "public static SynchronizationContext synContext"
m_msgForm.UpdateMsg("starting ..."); //UpdateMsg is public method to show progress information
BackgroundWorker myBackgroundWorker = new BackgroundWorker();
myBackgroundWorker.DoWork += new DoWorkEventHandler(myBackgroundWorker_DoWork);
myBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myBackgroundWorker_RunWorkerCompleted);
myBackgroundWorker.WorkerReportsProgress = true;
myBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(myBackgroundWorker_ProgressChanged);
myBackgroundWorker.RunWorkerAsync(theBackgroundArgument);
}
private void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
worker.ReportProgress(-1);
//... some code
string msgText = "doing job: " + job.Title;
worker.ReportProgress(0, msgText);
//... some code
worker.ReportProgress(0, "...other text...");
//... some code
worker.ReportProgress(0, "...etc...");
}
void myBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == -1)
{
m_msgForm.ShowDialog(); //show as a modal dialog
}
else
{
m_msgForm.UpdateMsg(e.UserState.ToString);
}
}
*Note that we have to set .WorkerReportsProgress = true for the BackgroundWorker() so we can use ReportProgress() and receive the ProgressChanged() event.

using backgroundworker in Winforms (C#) with MVP pattern

I've been trying to refactor a spaghetti code of an app by using MVP pattern. But now I'm struggling with this:
A form that has button that calls a the DoWork method (of a backgroundworker) which is a long operation. My question is if I move the long operation out of the view into the Presenter then how do I send progress changes from this operation to the View? The BGW must be in the Presenter also?
Can you give me a sample of how to do this?
Thank you in advance.
This outlines the use of the BackgroundWorker:
private BackgroundWorker _backgroundWorker;
public void Setup( )
{
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.DoWork +=
new DoWorkEventHandler(BackgroundWorker_DoWork);
_backgroundWorker.ProgressChanged +=
new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);
_backgroundWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(BackgroundWorker_RunWorkerCompleted);
// Start the BackgroundWorker
_backgroundWorker.RunWorkerAsync();
}
void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// This method runs in a background thread. Do not access the UI here!
while (work not done) {
// Do your background work here!
// Send messages to the UI:
_backgroundWorker.ReportProgress(percentage_done, user_state);
// You don't need to calculate the percentage number if you don't
// need it in BackgroundWorker_ProgressChanged.
}
// You can set e.Result = to some result;
}
void BackgroundWorker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
// This method runs in the UI thread and receives messages from the backgroud thread.
// Report progress using the value e.ProgressPercentage and e.UserState
}
void BackgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
// This method runs in the UI thread.
// Work is finished! You can display the work done by using e.Result
}
UPDATE
This BackgroundWorker has to be in the presenter of cause. The idea of patterns like MVP, MVC or MVVM is to remove as much code from the view as possible. The view would only have code very specific to the view itself, like creating the view or drawing in the Paint event handler and so on. Another kind of code in the view is the code necessary to communicate with the presenter or controller. The presenting logic, however, has to be in the presenter.
You would use the BackgroundWorker_ProgressChanged method that runs in the UI thread to send changes to the view. Either by calling public methods of the view or by setting public properties of the view or by exposing public properties the view can attach to by binding its properties or the properties of its controls to it. (This is borrowed from the MVVM pattern.) The presenter must implement INotifyPropertyChanged in order to notify the view that a property has changed, if you decide to bind the view to properties of the presenter.
Note: Another thread than the UI thread is not allowed to interact with the view directly (an exception is thrown if you try to do so). Therefore the BackgroundWorker_DoWork cannot interact with the view directly and therefore calls ReportProgress, which in turn runs BackgroundWorker_ProgressChanged in the UI thread.
You can place the BackGroundWorker in the presenter and add a method to the view to show the progress.
Something like this:
//Add a method to your view interface to show progress if you need it.
public interface IView
{
void ShowProgress(int progressPercentage);
}
//Implement method in the view.
public class MyView : Form, IView
{
public MyView()
{
//Assume you have added a ProgressBar to the form in designer.
InitializeComponent();
}
public void ShowProgress(int progressPercentage)
{
//Make it thread safe.
if (progressBar1.InvokeRequired)
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = progressPercentage; }));
else
progressBar1.Value = progressPercentage;
}
}
// In your presenter class create a BackgroundWorker and handle it's do work event and put your time consuming method there.
public class MyPresenter
{
private BackgroundWorker _bw;
public MyPresenter()
{
_bw = new BackgroundWorker();
_bw.WorkerReportsProgress = true;
_bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
}
private void _bw_DoWork(object sender, DoWorkEventArgs e)
{
//Time consuming operation
while (!finished)
{
//Do the job
_bw.ReportProgress(jobProgressPercentage);
}
}
public void StartTimeConsumingJob()
{
_bw.RunWorkerAsync();
}
}
Don't forget to Dispose the BackgroundWorker when you're finished.
with your input I've managed to work this out. Please comment any flaws you may find with this approach:
* View interface *
public interface IView
{
void ShowProgress( int progressPercentage);
}
* View (a form) *
public partial class Form1 : Form, IView
{
MyPresenter p ;
public Form1()
{
InitializeComponent();
p = new MyPresenter(this);
}
private void button1_Click(object sender, EventArgs e)
{
if (p.IsBusy())
{
return;
}
p.StartTimeConsumingJob();
}
public void ShowProgress(int progressPercentage)
{
if (progressBar1.InvokeRequired)
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = progressPercentage; }));
else
progressBar1.Value = progressPercentage;
}
private void button2_Click(object sender, EventArgs e)
{
p.Cancel();
}
}
* Presenter *
public class MyPresenter
{
private BackgroundWorker _bw;
private IView _view;
public MyPresenter(IView Iview)
{
_view = Iview;
_bw = new BackgroundWorker();
_bw.WorkerReportsProgress = true;
_bw.WorkerSupportsCancellation = true;
_bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
_bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged);
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_Completed);
}
public void StartTimeConsumingJob()
{
_bw.RunWorkerAsync();
}
private void _bw_DoWork(object sender, DoWorkEventArgs e)
{
//Time consuming operation Do the job
Thread.Sleep(1000);
_bw.ReportProgress(50);
Thread.Sleep(2000);
if(_bw.CancellationPending)
{
e.Result = false;
}
}
public bool IsBusy()
{
return _bw.IsBusy;
}
public void Cancel()
{
_bw.CancelAsync();
}
private void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
_view.ShowProgress(e.ProgressPercentage);
}
private void _bw_Completed(object sender, RunWorkerCompletedEventArgs e)
{
if((bool)e.Result)
_view.ShowProgress(100);
else
_view.ShowProgress(0);
_bw.Dispose();
}
}

Run method with BackgroundWorker on second Form

Let's imagine that we have two Forms: MainForm and WaitingForm. I want to pass, from MainForm, to the WaitingForm the method to run in background using BackgroundWorker.
Now, I'm doing things that way:
MainForm.cs:
public partial class MainForm: Form
{
private void btnImport_Click(object sender, EventArgs e)
{
var waitingFrm = new WaitingForm();
waitingFrm.DoWork = (o, args) => this.LongRunningOperation(this, new DoWorkEventArgs("foo bar"));
waitingFrm.OnWorkCompleted = (o, args) => MessageBox.Show("Finished!");
waitingFrm.Show();
waitingFrm.Run(); // should execute LongRunningOperation, method below.
}
private void LongRunningOperation(object sender, DoWorkEventArgs e)
{
MessageBox.Show("Running long operation!....");
// some long running stuff here;
}
}
WaitingForm.cs
public partial class WaitingForm: Form
{
private BackgroundWorker worker = new BackgroundWorker();
public DoWorkEventHandler DoWork { get; set; }
public RunWorkerCompletedEventHandler OnWorkCompleted { get; set; }
public WaitingForm()
{
this.worker.DoWork += DoWork;
this.worker.RunWorkerCompleted += OnWorkCompleted;
InitializeComponent();
}
public void Run()
{
this.worker.RunWorkerAsync();
}
}
But after waitingFrm.Run(); my LongRunningOperation is not executed.
In your WaitingForm I'd do:
public event DoWorkEventHandler DoWork {
add { worker.DoWork += value; }
remove { worker.DoWork += value; }
}
(Instead of the get;set; property).
And then in your main window btnImport_Click handler just:
waitingFrm.DoWork += LongRunnignOperation;
And the same for completed handler. Your syntax seems overly complicated. This is just a clean way to expose an event (in this case on your waitingform) and the pass event handler through to the real handler (in this case worker.DoWork). It is equivalent to
waitingFrm.worker.DoWork += LongRunnignOperation;
which would do just as well.
I want to pass, from MainForm, to the WaitingForm the method to run
in background using BackgroundWorker
I would in this case
declare an event in WaitingForm
before Form1 shows WaitingForm subscribes to that event
when long running operation has to be runned WaitingForm raise an event, Form1 gets it and
Form1 runs its method in other thread.
Hope this helps.
In this particular case you want all of the work to happen in MainForm and it looks like WaitingForm is just a display for the user. If that's the case then I would just put the BackgroundWorker in the MainForm and use the event to call into WaitingForm
public partial class MainForm: Form
{
private void btnImport_Click(object sender, EventArgs e) {
var waitingForm = new WaitingForm();
waitingForm.Show();
var worker = new BackgroundWorker();
worker.DoWork += (o, args) => this.LogRunningOperation(o, args);
worker.OnWorkComplete += (o, args) => {
waitingForm.Close();
worker.Dispose();
};
worker.RunWorkerAsync();
}
private void LongRunningOperation(object sender, DoWorkEventArgs e) {
MessageBox.Show("Running long operation!....");
// some long running stuff here;
}
}
So, the simple answer is that. Your code is not working because the mainform is not seeing the BackgroundWorker object instance events. Instead of doing:
this.worker.DoWork += DoWork;
this.worker.RunWorkerCompleted += OnWorkCompleted;
in WaitingForm - InitializeComponent(), do this instead in mainForm like this:
waitingFrm.worker.DoWork += waitingFrm.DoWork;
waitingFrm.worker.RunWorkerCompleted += waitingFrm.OnWorkCompleted;

Communicate Between two threads

I have sth like that. It's giving me error. I cut out all unneeded parts of code. It is giving me this error
The calling thread cannot access this object because a different thread owns it.
public partial class MainWindow : Window
{
BackgroundWorker worker;
Grafik MainGrafik;
double ProgressBar
{
set { this.progressBarMain.Value = value; }
}
public MainWindow()
{
InitializeComponent();
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
MainGrafik = new Grafik();
MainGrafik.ProgressUpdate +=
new Grafik.ProgressUpdateDelegate(MainGrafik_ProgressUpdate);
worker.RunWorkerAsync();
}
void MainGrafik_ProgressUpdate(double progress)
{
ProgressBar = progress;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
MainGrafik.Refresh();
Thread.Sleep(2000);
}
}
}
class Grafik
{
public delegate void ProgressUpdateDelegate(double progress,
DateTime currTime);
public event ProgressUpdateDelegate ProgressUpdate;
public void Refresh()
{
ProgressUpdate(5); // Just for testing
}
}
You can't update UI objects from another thread. They have to be updated in the UI thread. Try adding this code to the MainGrafik_ProgressUpdate(double progress)
void MainGragfik_ProgressUpdate(double progress)
{
if (InvokeRequired)
{
BeginInvoke((MethodIvoker)(() =>
{
MainGragfik_ProgressUpdate(progress);
}));
return;
}
ProgressBar = progress;
}
The thread firing the ProgressUpdate event is your BackgroundWorker. The ProgressUpdate event handlers are likely running on that thread, and not the UI thread.
in short call this on the form in the context of your other thread's execution:
void MainGrafik_ProgressUpdate(object sender, EventArgs e) {
Action<T> yourAction =>() yourAction;
if(yourForm.InvokeRequired)
yourForm.Invoke(yourAction);
else yourAction;
}
Or with MethodInvoker (blank delegate)
void MainGrafik_ProgressUpdate(object sender, EventArgs e) {
MethodInvoker invoker = delegate(object sender, EventArgs e) {
this.ProgressBar = whatever progress;
};
}

Categories