Understanding cross-threading Controls access C# - c#

I'm having some trouble accessing the UI from an another thread.
I understand the basics on cross-threading limitations, but I can't seem to write the code that will work. More specifically, I can't access the ListView from a static method (thread).
I'm trying to make it work with backgroundWorker.
Here's my code:
private void start_Click(object sender, EventArgs e)
{
ServicePointManager.DefaultConnectionLimit = 20;
var tasks = new List<Task<int>>();
foreach (ListViewItem item in listView1.Items)
{
string currentUrl = item.SubItems[1].Text;
int i = item.Index;
tasks.Add(Task.Factory.StartNew(() => { return GetWebResponse(currentUrl, i); }));
}
Task.WaitAll(tasks.ToArray());
}
private static int GetWebResponse(string url, int itemIndex)
{
int statusCode = 0;
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
Task<HttpWebResponse> responseTask = Task.Factory.FromAsync<HttpWebResponse>(httpWebRequest.BeginGetResponse, asyncResult => (HttpWebResponse)httpWebRequest.EndGetResponse(asyncResult), null);
backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
backgroundWorker.RunWorkerAsync();
return statusCode;
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listView1.Items[0].ImageKey = "green";
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (!e.Cancel)
{
Thread.Sleep(5000);
backgroundWorker.ReportProgress(0);
}
}
This code doesn't work because backgroundWorker_DoWork and backgroundWorker_ProgressChanged are not static, but if I make them static then I can't access listView1
EDIT: Got it working. Code below for review
public delegate void delUpdateListView(int itemIndex, int statusCode);
public Form1()
{
InitializeComponent();
}
private void start_Click(object sender, EventArgs e)
{
ServicePointManager.DefaultConnectionLimit = 20;
var tasks = new List<Task<int>>();
foreach (ListViewItem item in listView1.Items)
{
string currentUrl = item.SubItems[1].Text;
int i = item.Index;
tasks.Add(Task.Factory.StartNew(() => {
//return GetWebResponse(currentUrl, i);
int statusCode = 0;
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(currentUrl);
Task<HttpWebResponse> responseTask = Task.Factory.FromAsync<HttpWebResponse>(httpWebRequest.BeginGetResponse, asyncResult => (HttpWebResponse)httpWebRequest.EndGetResponse(asyncResult), null);
statusCode = (int)responseTask.Result.StatusCode;
object[] invParams = new object[2];
invParams[0] = i;
invParams[1] = statusCode;
if (InvokeRequired)
{
BeginInvoke(new delUpdateListView(UpdateListView), invParams);
}
else
{
Invoke(new delUpdateListView(UpdateListView), invParams);
}
return statusCode;
}));
}
Task.WaitAll(tasks.ToArray());
}
public void UpdateListView(int itemIndex, int statusCode) {
listView1.Items[itemIndex].ImageKey = "green";
}

I see several problems here:
1) I don't see why GetWebResponse needs to be static. The easiest solution would be to make it an instance method.
2) Why are you using the background worker anyway?
3) It doesn't make much sense to use Tasks and then wait for them to finish right after you spawn them. This blocks your application where it should be responsive.
As for 3): To keep the UI responsive and updatable, disable everything the user may not click before spawning the tasks, add a continuation action to each task that re-enables the UI components. The task may update the list using the usual Invoke calls.

Related

Using GetActiveTcpConnections to continuously check for new IP

I"m new to C# and am looking to see if the way I'm using this code to check for a new connection in a background worker is safe and or a better why to do it. My end goal is to run this as a service but, I'm using the background worker as a way to get real-time updates for testing. The code works but I don't know if its safe/stable or the best way to do it?
Any help would be appreciated.
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerAsync(2000);
MessageBox.Show("Started backgroung worker");
}
private void button2_Click(object sender, EventArgs e)
{
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
}
MessageBox.Show("Stopped backgroung worker");
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker helperBW = sender as BackgroundWorker;
e.Result = BackgroundProcessLogicMethod(helperBW, arg);
if (helperBW.CancellationPending)
{
e.Cancel = true;
}
}
private int BackgroundProcessLogicMethod(BackgroundWorker bw, int a)
{
while (true)
{
IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] connections = properties.GetActiveTcpConnections();
foreach (TcpConnectionInformation c in connections)
{
if (c.RemoteEndPoint.ToString() == "192.168.4.14:443")
richTextBox1.Invoke(new Action( () => richTextBox1.AppendText(c.RemoteEndPoint.ToString() + "\n\r") ) );
}
}
}
Consider throttling the while(true)... loop but other than that you seem to be doing the right things in your post. This kind of thing shows up a lot in my own production code so I wanted to offer the tried-and-true approach that works for me. It's just another perspective, however.
Your code references RichTextBox which makes me infer that a WinForms example will fly here, but the general approach is valid regardless of platform anyway.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
checkBoxRun.CheckedChanged += onRunChanged;
}
CancellationTokenSource _cts = null;
private async void onRunChanged(object sender, EventArgs e)
{
if (checkBoxRun.Checked)
{
_cts = new CancellationTokenSource();
while (checkBoxRun.Checked)
{
richTextBox1.AppendTextOnUIThread(DateTime.Now.ToString());
IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] connections = properties.GetActiveTcpConnections();
foreach (TcpConnectionInformation c in connections)
{
var endpoint = c.RemoteEndPoint.ToString();
switch (endpoint)
{
case "192.168.4.14:443":
richTextBox1.AppendTextOnUIThread(endpoint, color: Color.Red);
break;
case "127.0.0.1:49813":
richTextBox1.AppendTextOnUIThread(endpoint, color: Color.Blue);
break;
case "127.0.0.1:55846":
richTextBox1.AppendTextOnUIThread(endpoint, color: Color.Green);
break;
default:
continue;
}
}
richTextBox1.AppendTextOnUIThread();
// Throttle the polling to a reasonable repeat rate.
var task = Task.Delay(TimeSpan.FromSeconds(1), _cts.Token);
try{ await task; } catch (TaskCanceledException){ Debug.WriteLine("Cancelled"); }
}
}
else _cts.Cancel();
}
}
With the following extension for RichTextBox:
static class Extensions
{
public static void AppendTextOnUIThread(this RichTextBox #this, string text = "", bool newLine = true, Color? color = null)
{
if (!#this.Disposing)
{
if (newLine) text = $"{text}{Environment.NewLine}";
#this.BeginInvoke(new Action(() =>
{
if (color == null)
{
#this.AppendText(text);
}
else
{
var colorB4 = #this.ForeColor;
#this.SelectionColor = (Color)color;
#this.AppendText(text);
#this.SelectionColor = colorB4;
}
}));
}
}
}

Multi threading approach

I have developed the below code using TPL and all works fine except how to restart the task. Currently when I click button1 the thread starts in textBox1 and similarly with button3 and textBox2. When I click button3 and button4 both the threads get's cancelled.
Now I want to restart the thread where left off on the click event of button1 and button3. I tried many approaches but could not get any idea on how to do this.
Here's the code:
public partial class Form1 : Form
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token;
CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource();
CancellationToken token1;
public Form1()
{
InitializeComponent();
token = cancellationTokenSource.Token;
token1 = cancellationTokenSource1.Token;
}
private void button1_Click(object sender, EventArgs e)
{
Task t = Task.Factory.StartNew(() =>
{
var run = true;
while (run)
{
for (int i = 0; i < 100; i++)
{
if (token.IsCancellationRequested)
{
run = false;
break;
//token.ThrowIfCancellationRequested();
}
// your code
System.Threading.Thread.Sleep(1000);
Action act = () => textBox1.Text = Convert.ToString(i);
textBox1.Invoke(act);
}
}
});
}
private void button2_Click(object sender, EventArgs e)
{
cancellationTokenSource.Cancel();
}
private void button3_Click(object sender, EventArgs e)
{
Task t1 = Task.Factory.StartNew(() =>
{
var run = true;
while (run)
{
for (int i = 0; i < 100; i++)
{
if (token1.IsCancellationRequested)
{
run = false;
break;
//token1.ThrowIfCancellationRequested();
}
// your code
System.Threading.Thread.Sleep(1000);
Action act = () => textBox2.Text = Convert.ToString(i);
textBox2.Invoke(act);
}
}
});
}
private void button4_Click(object sender, EventArgs e)
{
cancellationTokenSource1.Cancel();
}
}
You need to sync thread using signaling, generally they are achieved using Event Wait Handles like AutoResetEvent, ManualReset and CountdownEvent. You can achieve this with ManualReset.
Declare ManualResetEvent:
CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource();
CancellationToken token1;
private ManualResetEvent mre = new ManualResetEvent(false);
Modify on CancellationRequest
if (token.IsCancellationRequested) {
run = false;
mre.WaitOne();
//token.ThrowIfCancellationRequested();
}
OnStart/OnResume: mre.Set();

Cancelling task in C#

I have a Form with two buttons (Start , Stop).
When I press Start Button a Task is initialized and a function is called that keeps running until Stop button is pressed.
But When I press Stop button Form freezes. why?
I have copied snippet from StackOverflow but it still freezes Form.
So tell me how to Cancel Task properly?
public partial class Form1 : Form
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private Task _task;
public Form1()
{
InitializeComponent();
}
//Funtion that runs when Task is initialized.
private void EventLoop(CancellationToken token)
{
//Code here..
while (!token.IsCancellationRequested)
{
//Code here..
}
if (token.IsCancellationRequested)
{
MessageBox.Show("Operation Complete..!!");
}
}
//Function for Start Button.
private void Start_Click(object sender, EventArgs e)
{
_task = Task.Factory.StartNew(() => EventLoop(_cts.Token), _cts.Token);
}
//Function for Stop button.
private void Stop_Click(object sender, EventArgs e)
{
_cts.Cancel();
}
}
Similar Example from MSDN:
var compute = Task.Factory.StartNew(() =>
{
return SumRootN(j);
}, tokenSource.Token);`
Form After Stop button is pressed.
token.IsCancellationRequested is true .
Full EventLoop() Function.
private void EventLoop(CancellationToken token)
{
SerialPort sp = new SerialPort();
string text, batch, part, courseName;
text = batch = part = courseName = "";
int courseId = 0;
this.Invoke((MethodInvoker)delegate()
{
text = portCB.SelectedItem.ToString();
batch = comboBox2.SelectedItem.ToString();
part = comboBox3.SelectedItem.ToString();
courseName = comboBox1.SelectedItem.ToString();
progressBar1.Value = 20;
using (Model1 db = new Model1())
{
courseId = db.Courses.Where(c => c.Course_name.ToUpper() == courseName.ToUpper()).Select(c => c.Course_Id).Single();
}
});
sp.PortName = text;
sp.BaudRate = 9600;
sp.Open();
while (!token.IsCancellationRequested)
{
text = sp.ReadLine();
if (text.Contains("Found ID #"))
{
this.Invoke((MethodInvoker)delegate()
{
textBox2.Clear();
textBox2.Text = "Getting Registation ID.\n";
progressBar1.Value = 60;
});
string splitText = text.Split('#')[1];
int end = splitText.IndexOf(' ');
int id = int.Parse(splitText.Substring(0, end));
using (Model1 db = new Model1())
{
var result = db.Students.Where(s => s.Reg_Id == id && s.Batch == batch && s.Class == part).Select(s => s).SingleOrDefault();
if (result != null)
{
Attendance a = new Attendance();
a.Course_Id = courseId;
a.Student_Id = id;
a.Status = "P";
a.Date = DateTime.Today.Date;
a.Batch = batch;
a.Part = part;
db.Attendances.Add(a);
string message = "";
if (db.SaveChanges() != 0)
{
message = "Attendance Uploaded..!!\n";
}
else
{
message = "Attendance Not Uploaded ..!!\n";
}
this.Invoke((MethodInvoker)delegate()
{
progressBar1.Value = 100;
textBox2.AppendText(message);
});
}
else
{
this.BeginInvoke((MethodInvoker)delegate()
{
textBox2.AppendText("Student Does not belong to Specified Batch Or Part..\n");
});
}
}
}
else
{
this.Invoke((MethodInvoker)delegate()
{
textBox2.AppendText("No Match Found..!! \n");
});
}
this.Invoke((MethodInvoker)delegate()
{
textBox1.AppendText(text + "\n");
});
}
sp.Close();
// This exception will be handled by the Task
// and will not cause the program to crash
if (token.IsCancellationRequested)
{
//MessageBox.Show("Operation Comptele..!!");
}
}
You call MessageBox.Show("Operation Complete..!!"); in the progress of cancellation. This is highly not recommended, not talking about that you are calling an UI operation from other than the UI thread.
Comment the MessageBox.Show("Operation Complete..!!"); line out
* Edit *
Question author's comment on his original question, found the mistake, which line was removed from the post. Here are my conclusion:
Always try to isolate an issue, and reproduce in its purest form. During that process you will probably diagnose and find the issues itself :-).
So if the code with issue is long to post, it is definitely not the way just deleting lines then post it. The way is deleting lines and see if the issue exist, with other words: Isolating the issue in its purest reproducable form
please use async call on your task
private async void Start_Click(object sender, EventArgs e)
{
_task = Task.Factory.StartNew(() => EventLoop(_cts.Token), _cts.Token);
await task;
}

TargetInvokationException in Application.Run(new Form1());

I am trying to iterate through a for loop by pressing start button and stop it by pressing stop button. I am using await Task.Run(() => it works in the expected manner. But when I press start button again, I get TargetInvokationException in Application.Run(new Form1());.
My code below
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CancellationTest
{
public partial class Form1 : Form
{
private readonly SynchronizationContext synchronizationContext;
private DateTime previousTime = DateTime.Now;
CancellationTokenSource cts = new CancellationTokenSource();
public Form1()
{
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
}
private async void ButtonClickHandlerAsync(object sender, EventArgs e)
{
button1.Enabled = false;
var count = 0;
CancellationToken token = cts.Token;
await Task.Run(() =>
{
try
{
for (var i = 0; i <= 5000000; i++)
{
token.ThrowIfCancellationRequested();
UpdateUI(i);
count = i;
}
}
catch (System.OperationCanceledException)
{
MessageBox.Show("Canceled");
}
}, token);
label1.Text = #"Counter " + count;
button1.Enabled = true;
}
public void UpdateUI(int value)
{
var timeNow = DateTime.Now;
if ((DateTime.Now - previousTime).Milliseconds <= 50) return;
synchronizationContext.Post(new SendOrPostCallback(o =>
{
label1.Text = #"Counter " + (int)o;
}), value);
previousTime = timeNow;
}
private void button2_Click(object sender, EventArgs e)
{
cts.Cancel();
}
}
}
Can anyone explain why this happens and how to resolve this.
Can anyone explain why this happens
TargetInvokationException is a wrapper type exception and the main information is contained in InnerException property which you didn't show. Looking at the code, most likely it is OperationCanceledException and is caused by passing an already canceled token to Task.Run.
In general you should not reuse the CancellationTokenSource instance - see The general pattern for implementing the cooperative cancellation model in the Remarks section of the documentation.
Also you should protect with try/catch the whole block, not only the Task.Run body. And initialize all the involved state members at the beginning, and do the necessary cleanup at the end.
So the correct code could be like this:
private DateTime previousTime;
private CancellationTokenSource cts;
private async void ButtonClickHandlerAsync(object sender, EventArgs e)
{
button1.Enabled = false;
var count = 0;
previousTime = DateTime.Now;
cts = new CancellationTokenSource();
try
{
CancellationToken token = cts.Token;
await Task.Run(() =>
{
for (var i = 0; i <= 5000000; i++)
{
token.ThrowIfCancellationRequested();
UpdateUI(i);
count = i;
}
}, token);
}
catch (System.OperationCanceledException)
{
MessageBox.Show("Canceled");
}
finally
{
cts.Dispose();
cts = null;
}
label1.Text = #"Counter " + count;
button1.Enabled = true;
}
and make sure Cancel button is enabled only when cts != null or check that condition inside the click handler in order to avoid NRE.

WPF C# - Update progressbar from another thread

I'm stuck trying to update a progressbar from other threads ran in a different class. To explain what I do I think a picture will be better. I want to update the progressbar in the //HERE point :
I've tried using a delegate, tried with ReportProgress and I think i've basically tried to use everything google reported in the first 100 results, without success. I'm still learning WPF and this might be silly way to proceed, i'm looking for a quick and dirty way to get the work done but feel free to tell me what I should redesign for a cleaner application.
EDIT : More code.
In ExecutorWindow.xaml.cs :
public void RunExecutor()
{
// CREATE BACKGROUNDWORKER FOR EXECUTOR
execBackground.DoWork += new DoWorkEventHandler(execBackground_DoWork);
execBackground.RunWorkerCompleted += new RunWorkerCompletedEventHandler(execBackground_RunWorkerCompleted);
execBackground.ProgressChanged += new ProgressChangedEventHandler(execBackground_ProgressChanged);
execBackground.WorkerReportsProgress = true;
execBackground.WorkerSupportsCancellation = true;
// RUN BACKGROUNDWORKER
execBackground.RunWorkerAsync();
}
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run();
}
private void execBackground_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("RunWorkerCompleted execBackground");
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
// TESTING
private void updateProgressBar(int i)
{
ExecutorProgressBar.Value += i;
}
public delegate void callback_updateProgressBar(int i);
In Executor.cs :
public void Run()
{
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
// HERE ?
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
while (CurrentRunningThreads > 0) { }
logfile.Close();
MessageBox.Show("All Tasks finished");
}
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
string[] options = (string[])e.Argument;
string machine = options[0];
string script = options[1];
// UPDATE HERE PROGRESSBAR ?
RemoteProcess myRemoteProcess = new RemoteProcess(machine, script);
string output = myRemoteProcess.TrueExec();
// UPDATE HERE PROGRESSBAR ?
this.logfile.WriteLine(output);
}
private void backgroundWorkerRemoteProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
CurrentRunningThreads--;
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//myExecWindow.ExecutorProgressBar.Value = e.ProgressPercentage; // TESTING
//ExecutorWindow.callback_updateProgressBar(1); // TESTING
}
EDIT 2 : I got it! Simple in fact, but i guess I've been looking too close to find out.
In my ExecutorWindow class :
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run(sender);
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
And in my Executor class :
private BackgroundWorker myExecutorWindow;
[...]
public void Run(object sender)
{
myExecutorWindow = sender as BackgroundWorker;
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
[...]
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
myBackgroundWorker.ReportProgress(1);
// PROCESSING MY STUFF HERE
myBackgroundWorker.ReportProgress(1);
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myExecutorWindow.ReportProgress(1);
}
Thank you !
You can run any method on the UI thread with this very basic sample
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
{
this.progressBar.Value= 20; // Do all the ui thread updates here
}));
Running commands inside the Dispatcher.Invoke(...), you can actually interact with the UI from any worker thread, where otherwise you would get an exception.
If you really need to have the ultimate control on the background threads & main (UI) thread updates, here is a fantastic tutorial on that: http://blog.decarufel.net/2009/03/good-practice-to-use-dispatcher-in-wpf.html
You should be able to use the Dispatcher.Invoke method
e.g.
Dispatcher.Invoke(
new System.Action(() => myProgressBar.Value = newValue)
);
I got it! Simple in fact, but i guess I've been looking too close to find out.
In my ExecutorWindow class :
private void execBackground_DoWork(object sender, DoWorkEventArgs e)
{
myExecutor = new Executor(arg1, arg2);
myExecutor.Run(sender);
}
private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ExecutorProgressBar.Value += 1;
}
And in my Executor class :
private BackgroundWorker myExecutorWindow;
[...]
public void Run(object sender)
{
myExecutorWindow = sender as BackgroundWorker;
string[] options = new string[2];
int i = 0;
while (LeftToRun > 0)
{
if (CurrentRunningThreads < MaxThreadsRunning)
{
BackgroundWorker myThread = new BackgroundWorker();
myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork);
myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted);
myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged);
myThread.WorkerReportsProgress = true;
myThread.WorkerSupportsCancellation = true;
myThread.RunWorkerAsync(new string[2] {opt1, opt2});
CurrentRunningThreads++;
i++;
LeftToRun--;
}
}
[...]
private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker myBackgroundWorker = sender as BackgroundWorker;
myBackgroundWorker.ReportProgress(1);
// PROCESSING MY STUFF HERE
myBackgroundWorker.ReportProgress(1);
}
private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myExecutorWindow.ReportProgress(1);
}
I found a really simple solution to create a thread to run any block of code as well as handle Invocation back to the main thread to change the control's properties. It works out of the box with .NET 4.5 and the lambda call on the Dispatcher could be adapted to work with earlier versions of .NET. The main benefit is it's just so blissfully simple and perfect when you just need a quick thread for some really basic bit of code.
So presuming you have a progress bar somewhere on your dialog in scope do this:
progBar.Minimum = 0;
progBar.Maximum = theMaxValue;
progBar.Value = 0;
Dispatcher disp = Dispatcher.CurrentDispatcher;
new Thread(() => {
// Code executing in other thread
while (progBar.Value < theMaxValue)
{
// Your application logic here
// Invoke Main Thread UI updates
disp.Invoke(
() =>
{
progBar.Value++;
}
);
}
}).Start();
You also need to ensure you have a reference to WindowsBase.dll
If you want a more reusable snippet of code running as the thread start you could use a method as the delegate but I find the inline lambda so easy for simple tasks and you don't need to deal with events as with the Background Worker approaches.

Categories

Resources