I have started to play with threads in c#, but need now help, here is my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoCount();
}
public void DoCount()
{
for (int i = 0; i < 100; i++)
{
objTextBox.Text = i.ToString();
Thread.Sleep(100);
}
}
}
its a simple win forms with a textbox, i want to see the "counting", but as you see in my code, the textbox shows me 99, it count till 99 and then shows up.. i`ll think, i have to manage this in a new thread but dont know how!
Use a BackgroundWorker. There is a BackgroundWorker overview on MSDN.
Here is an example of how your code might look:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker backgroundWorker = (BackgroundWorker)sender;
for (int i = 0; i < 100; i++)
{
backgroundWorker.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
textBox1.Text = e.ProgressPercentage.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
}
Other notes:
Remember to set WorkerReportsProgress in the designer if you want the progress to work.
When using a BackgroundWorker it is also often useful to use the ProgressBar control.
If you want to be able to cancel the background worker, that is possible too. See CancelAsync and WorkerSupportsCancellation.
When the background worker completes it fires the RunWorkerCompleted event.
This might be what you are looking for:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DoCount();
}
public void DoCount()
{
Thread t = new Thread(new ThreadStart(delegate
{
for (int i = 0; i < 100; i++)
{
this.Invoke((Action) delegate { objTextBox.Text = i.ToString(); });
Thread.Sleep(1000);
}
}));
t.IsBackground = true;
t.Start();
}
}
Notes
Uses a basic Thread not a BackgroundWorker
Uses Invoke to update the textbox on the UI thread
Sets IsBackground to true so the program exits if the form is closed before the loop is done.
You may want to try out the SynchronizationContext to do this.
Here's a quick example I threw together a while back:
public partial class Form1 : Form
{
private SynchronizationContext c;
private Thread t;
private EventWaitHandle pause =
new EventWaitHandle(false, EventResetMode.ManualReset);
public Form1()
{
this.InitializeComponent();
this.c = SynchronizationContext.Current;
}
private void Form1Activated(object sender, EventArgs e)
{
this.t = new Thread(new ThreadStart(delegate
{
this.pause.Reset();
while (this.t.IsAlive && !this.pause.WaitOne(1000))
{
this.c.Post(
state => this.label1.Text = DateTime.Now.ToString(),
null);
}
}));
this.t.IsBackground = true;
this.t.Start();
}
private void Form1Deactivate(object sender, EventArgs e)
{
this.pause.Set();
this.t.Join();
}
/// <summary>
/// Button1s the click.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void Button1Click(object sender, EventArgs e)
{
this.Close();
}
}
You don't need a thread to do this kind of thing at all - consider changing your code to be event driven and use a System.Windows.Forms.Timer object to implement your timings. Using timers for this has a huge advantage - it doesn't cost 1MB of memory (a thread does), and you don't need to synchronize them - windows does it for you.
After Thread.Sleep, try this:
this.Update();
Don't call DoCount directly, call ThreadPool.QueueUserWorkItem(DoCount). This will run DoCount in a new thread. (There are other ways that also work, but this is the simplest that works well.)
The next problem is that you can't directly set the textbox from the thread.
To solve this, use code similar to:
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
See http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx for the full example.
My solution is virtually the same as Mark's. The only difference is I check InvokeRequired in my ProgressChanged event. Here's my sample code:
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace tester
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
if (!backgroundWorker1.IsBusy)
backgroundWorker1.RunWorkerAsync();
}
/// <summary>
/// This delegate enables asynchronous calls for setting the text property on a control.
/// </summary>
delegate void SetTextCallback(string status);
private void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e)
{
for (var i = 0; i < 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void BackgroundWorker1ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (label1.InvokeRequired)
Invoke(new SetTextCallback(SetLabelText), new object[] { e.ProgressPercentage.ToString()});
else
SetLabelText(e.ProgressPercentage.ToString());
}
private void SetLabelText(string text)
{
label1.Text = text;
}
}
}
Multithreading could solve this but for something as simple as this counter it is unnecessary.
Another user recommended this.Update(). This works to make the numbers appear because the UI will redraw itself. But it doesn't address the fact that the window is not responsive (you can't move it around).
The third solution and my recommendation for this particular program is Application.DoEvents(). What this does is tell the underlying native window to execute its ProcessMessages method on the message pool. The message pool contains event messages that Windows has sent to it when the window needed to be redrawn, mouse has moved, the form has been moved, minimized, etc. Those instructions were sent by Windows and have been queued. The problem is that the program will not process them until the UI is idle. You can force it by calling this method.
Application.DoEvents() will yield a window which responds as expected in 100 ms intervals. It may be a tad choppy (threads would be more responsive) but it is very easy to put it in and is often sufficient.
for (int i = 0; i < 100; i++)
{
objTextBox.Text = i.ToString();
Application.DoEvents();
Thread.Sleep(100);
}
Here's my shot at a simple example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Action countUp = this.CountUp;
countUp.BeginInvoke(null, null);
}
private void CountUp()
{
for (int i = 0; i < 100; i++)
{
this.Invoke(new Action<string>(UpdateTextBox), new object[] { i.ToString() });
Thread.Sleep(100);
}
}
private void UpdateTextBox(string text)
{
this.textBox1.Text = text;
}
}
Related
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;
}
}
}
After implementing the changes to deadlocked C# Windows Form Application according to the article Lock Up Unlocked I am still having the same problem as in previous code of article Locked Up!
That is, upon clicking the button few times rapidly, the application hangs up (becomes unresponsive).
Why?
And how to correct it?
using System;
using System.Windows.Forms;
using System.Threading;
namespace LockupUnlocked
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
_t = new Thread(new ParameterizedThreadStart(WorkerThread));
}
private Thread _t;
private object lockObject = new object();
private bool StopThread = false; ////changes added to avoid deadlock
private void WorkerThread(object sender)
{
Thread.Sleep(1000);
//while (true)
while (!StopThread)//changes added to avoid deadlock
{
string result = "This is a Test";
IAsyncResult aResult;////changes added to avoid deadlock
lock (lockObject)
{
Thread.Sleep(25);
//lblResult.Invoke(new MethodInvoker(delegate { lblResult.Text = result; }));
aResult = lblResult.BeginInvoke//changes to avoid deadlock
(new MethodInvoker(delegate { lblResult.Text = result; }));
}
lblResult.EndInvoke(aResult);//changes added to avoid deadlock
Thread.Sleep(500);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
StopThread = true;
}
private void Form1_Load(object sender, EventArgs e)
{
_t.Start();
}
private void button1_Click(object sender, EventArgs e)
{
lock (lockObject)//changes added to avoid deadlock
{
lblResult.Text = "Override the Test";
}
}
}
}
To me it looks like lock contention, not necessarily dead-locking.
You iterate a while loop every 25 milliseconds (not 25 seconds, that'd be 25000), clicking the button would then interrupt this asking for a lock, it may never be given that lock as the sleep is inside the lock scope.
Without clicking the button this may appear to work, however, the button click waiting on a lock will be blocking the UI thread, causing the "not responding" message to appear because the form doesn't have available time to paint itself.
You actually don't need to lock in order to update the text value. When you invoke from a Control, it simply pushes a message onto the UI message queue, which is processed synchronously on the UI thread. The worst you can do is a race condition, which won't corrupt any shared state.
Remove the locking and the code should still work as you expect.
I am hesitant to offer up any sample code as I don't know what you are trying to achieve with this sample.
The pattern to work with conflicting threads that need GUi access in Windows.Forms involves the InvokeRequired property and the Invoke function. Locking is not neccessary then.
namespace WindowsFormsApplication1
{
using System;
using System.Threading;
using System.Windows.Forms;
public partial class Form1 : Form
{
private Thread thread;
public Form1()
{
this.InitializeComponent();
this.thread = new Thread(this.WorkerThread);
}
private void WorkerThread(object sender)
{
Thread.Sleep(1000);
while (true)
{
Thread.Sleep(25);
this.SetText("from thread");
Thread.Sleep(500);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.thread.Abort();
}
private void Form1_Load(object sender, EventArgs e)
{
this.thread.Start();
}
/// <summary>
/// This is a callback for the SetText Method.
/// </summary>
/// <param name="text">The text.</param>
private delegate void SetTextCallback(string text);
/// <summary>
/// This sets a text.
/// It's thread safe, you can call this function from any thread.
/// If it's not called from the UI-thread, it will invoke itself
/// on the UI thread.
/// </summary>
/// <param name="text">The text.</param>
private void SetText(string text)
{
if (this.InvokeRequired)
{
this.Invoke(new SetTextCallback(this.SetText), text);
}
else
{
this.lblResult.Text = text;
}
}
private void Button1_Click(object sender, EventArgs e)
{
this.SetText("from button");
}
}
}
I have a following form Form3 that is opened by another form Form1, and when closed Form1 opens back up.
The problem is when I close Form3 DoSomething keeps running after form is closed.
I understand that I can make DoSomething into a thread and set IsBackground = true but is there another way to stop all processes when form closes.
This code is just example, For illustration.
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
private void DoSomething()
{
int i = 0;
while(true)
{
if (!this.IsDisposed)
{
Application.DoEvents();
i++;
Thread.Sleep(10);
label1.Text = i.ToString();
dataGridView1.Rows.Add();
}
}
}
private void button1_Click(object sender, EventArgs e)
{
DoSomething();
}
private void Form3_FormClosed(object sender, FormClosedEventArgs e)
{
this.Dispose();
Form1.Default.Show();
}
}
You never break out of the while(true). You should either break the loop when it's IsDisposed is true, change your while loop to while(!IsDisposed), or store use a class level variable that determines when to break the loop.
I would probably opt for the latter, as it gives you a little more control.
public partial class Form3 : Form
{
volatile bool clDoSomething;
public Form3()
{
InitializeComponent();
}
private void DoSomething()
{
int i = 0;
clDoSomething = true;
while(clDoSomething)
{
Application.DoEvents();
++i;
Thread.Sleep(10);
label1.Text = i.ToString();
dataGridView1.Rows.Add();
}
}
private void button1_Click(object sender, EventArgs e)
{
DoSomething();
}
private void Form3_FormClosed(object sender, FormClosedEventArgs e)
{
clDoSomething = false;
Form1.Default.Show();
}
}
Your fundamental approach is flawed.
First off, Application.DoEvents should be avoided unless you are sure that you really need it, and that you are using it correctly. You do not need it here, and you are not using it correctly.
What you really need here is a Timer.
private Timer timer = new Timer();
private int count = 0;
public Form3()
{
InitializeComponent();
timer.Tick += timer_Tick;
timer.Interval = 10;
//when the form is closed stop the timer.
FormClosed += (_, args) => timer.Stop();
}
private void button1_Click(object sender, EventArgs e)
{
count = 0;
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
count++;
label1.Text = count.ToString();
dataGridView1.Rows.Add();
}
When the Form is create the Timer is configured. The tick event is set, along with the interval. The tick event will look similar to your DoSomething method; it will involve running some bit of code every 10 seconds, from the UI thread, while keeping the UI responsive. When the form is closed simply stop the timer and it will stop firing off these events.
Also note that in this example here pressing the button multiple times simply resets the timer and the count, it doesn't end up creating two loops that each fire every 10 milliseconds.
Override this.Dispose() or this.Close() as appropriate and kill off DoSomething() manually.
Thanks to cdhowie suggestions and input of all others. Mowing DoEvents to the end and adding IsDipsosed solved my problem.
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
}
private void DoSomething()
{
int i = 0;
while ((true) && !this.IsDisposed)
{
i++;
Thread.Sleep(10);
label1.Text = i.ToString();
dataGridView1.Rows.Add();
Application.DoEvents();
}
}
private void button1_Click(object sender, EventArgs e)
{
DoSomething();
}
private void Form3_FormClosed(object sender, FormClosedEventArgs e)
{
Form1.Default.Show();
}
}
try to add this intsruction in the FormClosing event :
System.Diagnostics.Process.GetCurrentProcess().Kill();
it's a little bit like this:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
System.Diagnostics.Process.GetCurrentProcess().Kill();
}
[EDIT]
I edited my question with complete code and explanation and hope something can give me clearer explanation.
I have the following Class that has a backgroundworker to track the percentage progress of a loop and update the percentage progress on a Label on ProgressWin's XAML. The following code works perfectly. (My question is far below, after the code.)
public partial class ProgressWin : Window
{
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
public ProgressWin()
{
InitializeComponent();
InitializeBackgroundWorker();
startAsync();
}
// Set up the BackgroundWorker object by
// attaching event handlers.
private void InitializeBackgroundWorker()
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
private void startAsync()
{
backgroundWorker1.RunWorkerAsync();
}
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; i <= 10; i++)
{
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
// This event handler updates the progress.
public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Content = (e.ProgressPercentage.ToString() + "%");
}
// This event handler deals with the results of the background operation.
public void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
resultLabel.Content = "Done!";
}
}
Here comes my problem, now instead of tracking the loop within the ProgressWin, I need to track the loop in my MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
for (int i = 1; i <= 10; i++)
{
System.Threading.Thread.Sleep(500);
????.ReportProgress(i * 10);
}
}
}
And I have no idea where to go from here. I tried instantiating an object from ProgressWin and call the DoWork but I end up frozen the ProgressWin window.
Due to the fact that the question was rewritten, i also rewrote my whole answer.
To get this to work your MainWindowhas a ProgressWindow and should use it like a background worker:
public partial class MainWindow : Window
{
private ProgressWindow _Progress;
public MainWindow()
{
InitializeComponent();
_Progress = new ProgressWindow();
_Progress.ProgressChanged += OnProgressChanged;
}
private void OnProgressChanged(object sender, ProjectChangedEventArgs e)
{
//ToDo: Update whatever you like in your MainWindow
}
}
To accomplish this your ProgressWindow should simply subscribe to the worker event and rethrow it:
public partial class ProgressWin : Window
{
// Add this to your class above in your question
public event ProgressChangedEventHandler ProgressChanged;
// Change or merge this with your existing function
private void backgroundWorker1_ProgressChanged(object sender, ProjectChangedEventArgs e)
{
var temp = ProgressChanged;
if(temp !=null)
temp(this, e);
}
}
You can simply call method of another class by doing
backgroudnWorker.DoWork += new DoWorkEventHandler(SomeClass.SomeStaticMethod);
or
backgroudnWorker.DoWork += new DoWorkEventHandler(SomeClassInstance.SomeMethod);
for calling a method of MainWindow class from another class ProgressScreen you should have reference of instance of MainWindow class in ProgressScreen then using that instance you can call any public method of MainWindow from ProgressScreen class
and as Oliver Said, you will need the instance of backgroundworker to send the progress updates from other method.
hey i am new to c# plz help.
i am writing a program that sorts data in a file and it is a time consuming process so i thought that i should run it in a separate thread and since it has alot of step so i made a new class for it. the problem is that i want to show the progress in the main GUI and i know for that i have to use Invoke function but the problem is that the form control variables are not accessible it this class. what should i do ??????
sample code:
public class Sorter
{
private string _path;
public Sorter(string path)
{
_path = path;
}
public void StartSort()
{
try
{
processFiles(_path, "h4x0r"); // Just kidding
}
catch (Exception e)
{
MessageBox.Show("Error: " + e.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void processFiles(string Dir, string[] key)
{
/* sorting program */
}
and it is used as
public partial class Form1 : Form
{
Sorter sort;
public Form1()
{
InitializeComponent();
}
private void browseBtn_Click(object sender, EventArgs e)
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
textBox1.Text = folderBrowserDialog1.SelectedPath;
}
private void startBtn_Click(object sender, EventArgs e)
{
if (startBtn.Text == "Start Sorting")
{
Thread worker = new Thread(new ThreadStart(delegate() {
sort = new Sorter(textBox1.Text);
sort.StartSort(); }));
worker.start();
}
else
MessageBox.Show("Cancel");//TODO: add cancelling code here
}
}
plz help..
Add an Event to your class that is doing the multi-threaded work, that triggers when the progress changes. Have your form subscribe to this event and update the progress bar.
Note ProgressEventArgs is a little class that inherits EventArgs and has an Integer for the progress.
// delegate to update progress
public delegate void ProgressChangedEventHandler(Object sender, ProgressEventArgs e);
// Event added to your worker class.
public event ProgressChangedEventHandler ProgressUpdateEvent
// Method to raise the event
public void UpdateProgress(object sender, ProgressEventArgs e)
{
ProgressChangedEventHandler handler;
lock (progressUpdateEventLock)
{
handler = progressUpdateEvent;
}
if (handler != null)
handler(sender, e);
}
I would recommend you read up on the BackgroundWorker class. It is exactly for the problem you are trying to solve and makes things a lot easier than doing manual threading yourself.
Brief Example
public Form1()
{
InitializeComponent();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void btnStart_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i < 101; ++i)
{
if (backgroundWorker.CancellationPending)
{
e.Cancel = true;
break;
}
else
{
//Sort Logic is in here.
Thread.Sleep(250);
backgroundWorker.ReportProgress(i);
}
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy && backgroundWorker.WorkerSupportsCancellation)
backgroundWorker.CancelAsync();
}
You could do something like this:
public delegate void StatusReporter(double progressPercentage);
public class MainClass
{
public void MainMethod()
{
Worker worker = new Worker(ReportProgress);
ThreadStart start = worker.DoWork;
Thread workThread = new Thread(start);
workThread.Start();
}
private void ReportProgress(double progressPercentage)
{
//Report here!!!
}
}
public class Worker
{
private readonly StatusReporter _reportProgress;
public Worker(StatusReporter reportProgress)
{
_reportProgress = reportProgress;
}
public void DoWork()
{
for (int i = 0; i < 100; i++ )
{
// WORK, WORK, WORK
_reportProgress(i);
}
}
}
There are a few option available to solve this sort of issue. In any case, you will have to fiddle with Invoke to get the UI to update.
You could...
...add an event that fires on your new class which your UI can listen to, and Invoke as applicable - you'd still need to pass the data to your worker class (by constructor, properties, method call, etc)
...keep the method as a method on your form, and pas that to start your new thread from (after all, a new thread doesn't have to be starting in a different class)
...change the access modifiers on your controls to be (say) internal such that any class within the same assembly can Invoke changes to the controls, or read from them.
...make your worker class a child of the form it needs to access - it can then see the privates of its parent, as long as it is passed a reference to the instance.