I have a Windows Form application and managed DLL in one solution. DLL contains some time consuming functions during which I wish to update the Form contents (callback from the DLL to the Form with progess updates). I have the following code:
Form code, where I initialize the DLL and give it a callback function in the Initialize method. I also start a separate Thread to periodicly check the message_queue for new messages from the DLL. The DLL function is also called in a separate Thread (non blocking for the UI).
private LibraryDLL library_dll;
private ConcurrentQueue<string> message_queue;
public MainForm()
{
InitializeComponent();
library_dll = new LibraryDLL();
message_queue = new ConcurrentQueue<string>();
library_dll.Initialize(ProcessMessage);
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
string message;
if (message_queue.TryDequeue(out message))
{
PrintMessage(message);
}
}).Start();
}
private void ProcessMessage(string message)
{
message_queue.Enqueue(message);
}
private void PrintMessage(string message)
{
this.Invoke((MethodInvoker)delegate
{
listBox_rows.Items.Add(message);
});
}
private void button_send_Click(object sender, EventArgs e)
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
library_dll.DoWork();
}).Start();
}
In DLL code, I use the callback method to report progress:
private CallBack callback;
public delegate void CallBack(string message);
public LibraryDLL() { }
public void Initialize(CallBack callback)
{
this.callback = callback;
}
public void DoWork()
{
callback("working...")
Thread.Sleep(500);
callback("working...")
Thread.Sleep(500);
callback("working...")
Thread.Sleep(500);
}
My problem is, that instead of string "working" appearing every 500ms, it appears 3 times after 1500ms (only after the Thread in which the DoWork method is running ends). I also tried the Invalidate()-Update()-Refresh() sequence in the Form's PrintMessage function, but without any effect.
Thanks for the advice!
EDIT1:
I modified the code to use the BackgroundWorker, however, the problem remains (nothing for 1500ms, than all 3 strings at once).
BackgroundWorker bck_worker;
public MainForm()
{
InitializeComponent();
library_dll = new LibraryDLL();
library_dll.Initialize(bck_worker);
bck_worker = new BackgroundWorker();
bck_worker.ProgressChanged += new ProgressChangedEventHandler(bckWorker_ProgressChanged);
bck_worker.WorkerReportsProgress = true;
bck_worker.WorkerSupportsCancellation = true;
}
private void bckWorker_DoWork(object sender, DoWorkEventArgs e)
{
library_dll.DoWork();
}
private void bckWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
PrintMessage((string)e.UserState);
}
private void button_send_Click(object sender, EventArgs e)
{
bck_worker.DoWork += new DoWorkEventHandler(bckWorker_DoWork);
bck_worker.RunWorkerAsync();
}
private void PrintMessage(string message)
{
listBox_rows.Items.Add(message);
}
And the DLL:
private BackgroundWorker bck_worker;
public LibraryDLL() { }
public void Initialize(BackgroundWorker bck_worker)
{
this.bck_worker = bck_worker;
}
public void DoWork()
{
bck_worker.ReportProgress(25, "working...");
Thread.Sleep(500);
bck_worker.ReportProgress(50, "working...");
Thread.Sleep(500);
bck_worker.ReportProgress(75, "working...");
Thread.Sleep(500);
}
EDIT2:
OK, I now tried to add the Invalidate-Update-Refresh sequence at the end of the PrintMessage function and it finaly works (with the BackgroundWorker approach)!
Use background worker and workers's report progress to update your UI: background worker doc
Related
In my program I have two methods that takes a while to complete, about few minutes each. While these methods are being executed, I display a Progress Bar in a separate window which shows the progress of each method. My two methods are in a static Utility class. They look like the following:
public static class Utility
{
public static bool TimeConsumingMethodOne(object sender)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
(sender as BackgroundWorker).ReportProgress(i);
}
return true;
}
public static bool TimeConsumingMethodTwo(object sender)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(50);
(sender as BackgroundWorker).ReportProgress(i);
}
return true;
}
}
Reading through similar questions in SO I learned that I should use BackgroundWorker and used the RunWorkerCompleted() to see when the worker completes its work. So in my Main() I used BackgroundWorer() and subscribed to the RunWorkerCompleted() method. My goal here is to run the TimeConsumingMethodOne() first (and display progress while running), then once finished, run TimeConsumingMethodTwo() and show progress again, and when that's completed output the message box (which simulates some other work in my program). My Main() looks like the following:
public partial class MainWindow : Window
{
public enum MethodType
{
One,
Two
}
private BackgroundWorker worker = null;
private AutoResetEvent _resetEventOne = new AutoResetEvent(false);
private AutoResetEvent _resetEventTwo = new AutoResetEvent(false);
private ProgressBarWindow pbWindowOne = null;
private ProgressBarWindow pbWindowTwo = null;
public MainWindow()
{
InitializeComponent();
}
private void btnRun_Click(object sender, RoutedEventArgs e)
{
RunMethodCallers(sender, MethodType.One);
_resetEventOne.WaitOne();
RunMethodCallers(sender, MethodType.Two);
_resetEventTwo.WaitOne();
MessageBox.Show("COMPLETED!");
}
private void RunMethodCallers(object sender, MethodType type)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
switch (type)
{
case MethodType.One:
worker.DoWork += MethodOneCaller;
worker.ProgressChanged += worker_ProgressChangedOne;
worker.RunWorkerCompleted += worker_RunWorkerCompletedOne;
break;
case MethodType.Two:
worker.DoWork += MethodTwoCaller;
worker.ProgressChanged += worker_ProgressChangedTwo;
worker.RunWorkerCompleted += worker_RunWorkerCompletedTwo;
break;
}
worker.RunWorkerAsync();
}
private void MethodOneCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowOne = new ProgressBarWindow("Running Method One");
pbWindowOne.Owner = this;
pbWindowOne.Show();
});
Utility.TimeConsumingMethodOne(sender);
}
private void MethodTwoCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowTwo = new ProgressBarWindow("Running Method Two");
pbWindowTwo.Owner = this;
pbWindowTwo.Show();
});
Utility.TimeConsumingMethodTwo(sender);
}
private void worker_RunWorkerCompletedOne(object sender, RunWorkerCompletedEventArgs e)
{
_resetEventOne.Set();
}
private void worker_RunWorkerCompletedTwo(object sender, RunWorkerCompletedEventArgs e)
{
_resetEventTwo.Set();
}
private void worker_ProgressChangedOne(object sender, ProgressChangedEventArgs e)
{
pbWindowOne.SetProgressUpdate(e.ProgressPercentage);
}
private void worker_ProgressChangedTwo(object sender, ProgressChangedEventArgs e)
{
pbWindowTwo.SetProgressUpdate(e.ProgressPercentage);
}
}
Now the problem I have is, when I use _resetEventOne.WaitOne(); the UI hangs. If I removed those two waits, both methods run asynchronously and the execution moves on and outputs the MessageBox even before those two methods complete.
What am I doing wrong? How do I get the program to finish my first BackgroundWorker and then move onto the next, and then when that's done, output the MessageBox?
Now the problem I have is, when I use _resetEventOne.WaitOne(); the UI hangs. If I removed those two waits, both methods run asynchronously and the execution moves on and outputs the MessageBox even before those two methods complete.
What am I doing wrong?
When you call WaitOne(), you are blocking the UI thread, causing the UI to hang. If you remove that call, then of course you start both workers at once.
There are several different ways to approach your question. One is to stick as closely to your current implementation, and just fix the barest minimum to get it to work. Doing that, what you'll need to do is perform the actual next statement in the RunWorkerCompleted handler, instead of using an event to wait for the handler to execute.
That looks like this:
public partial class MainWindow : Window
{
public enum MethodType
{
One,
Two
}
private BackgroundWorker worker = null;
private ProgressBarWindow pbWindowOne = null;
private ProgressBarWindow pbWindowTwo = null;
public MainWindow()
{
InitializeComponent();
}
private void btnRun_Click(object sender, RoutedEventArgs e)
{
RunMethodCallers(sender, MethodType.One);
}
private void RunMethodCallers(object sender, MethodType type)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
switch (type)
{
case MethodType.One:
worker.DoWork += MethodOneCaller;
worker.ProgressChanged += worker_ProgressChangedOne;
worker.RunWorkerCompleted += worker_RunWorkerCompletedOne;
break;
case MethodType.Two:
worker.DoWork += MethodTwoCaller;
worker.ProgressChanged += worker_ProgressChangedTwo;
worker.RunWorkerCompleted += worker_RunWorkerCompletedTwo;
break;
}
worker.RunWorkerAsync();
}
private void MethodOneCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowOne = new ProgressBarWindow("Running Method One");
pbWindowOne.Owner = this;
pbWindowOne.Show();
});
Utility.TimeConsumingMethodOne(sender);
}
private void MethodTwoCaller(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(() =>
{
pbWindowTwo = new ProgressBarWindow("Running Method Two");
pbWindowTwo.Owner = this;
pbWindowTwo.Show();
});
Utility.TimeConsumingMethodTwo(sender);
}
private void worker_RunWorkerCompletedOne(object sender, RunWorkerCompletedEventArgs e)
{
RunMethodCallers(sender, MethodType.Two);
}
private void worker_RunWorkerCompletedTwo(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("COMPLETED!");
}
private void worker_ProgressChangedOne(object sender, ProgressChangedEventArgs e)
{
pbWindowOne.SetProgressUpdate(e.ProgressPercentage);
}
private void worker_ProgressChangedTwo(object sender, ProgressChangedEventArgs e)
{
pbWindowTwo.SetProgressUpdate(e.ProgressPercentage);
}
}
That said, BackgroundWorker has been made obsolete by the newer task-based API with async and await. With some small changes to your code, it can be adapted to use that newer idiom:
public partial class MainWindow : Window
{
public enum MethodType
{
One,
Two
}
private ProgressBarWindow pbWindowOne = null;
private ProgressBarWindow pbWindowTwo = null;
public MainWindow()
{
InitializeComponent();
}
private async void btnRun_Click(object sender, RoutedEventArgs e)
{
await RunMethodCallers(sender, MethodType.One);
await RunMethodCallers(sender, MethodType.Two);
MessageBox.Show("COMPLETED!");
}
private async Task RunMethodCallers(object sender, MethodType type)
{
IProgress<int> progress;
switch (type)
{
case MethodType.One:
progress = new Progress<int>(i => pbWindowOne.SetProgressUpdate(i));
await Task.Run(() => MethodOneCaller(progress));
break;
case MethodType.Two:
progress = new Progress<int>(i => pbWindowTwo.SetProgressUpdate(i));
await Task.Run(() => MethodTwoCaller(progress));
break;
}
}
private void MethodOneCaller(IProgress<int> progress)
{
Dispatcher.Invoke(() =>
{
pbWindowOne = new ProgressBarWindow("Running Method One");
pbWindowOne.Owner = this;
pbWindowOne.Show();
});
Utility.TimeConsumingMethodOne(progress);
}
private void MethodTwoCaller(IProgress<int> progress)
{
Dispatcher.Invoke(() =>
{
pbWindowTwo = new ProgressBarWindow("Running Method Two");
pbWindowTwo.Owner = this;
pbWindowTwo.Show();
});
Utility.TimeConsumingMethodTwo(progress);
}
}
To do the above does require a small adjustment to the Utility class as well:
static class Utility
{
public static bool TimeConsumingMethodOne(IProgress<int> progress)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
progress.Report(i);
}
return true;
}
public static bool TimeConsumingMethodTwo(IProgress<int> progress)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(50);
progress.Report(i);
}
return true;
}
}
That is, the Progress<T> class takes the place of the BackgroundWorker.ProgressChanged event and ReportProgress() method.
Note that with the above, the code has gotten significantly shorter, simpler, and is written in a more direct way (i.e. related statements are with each other in the same method now).
The example you gave is necessarily simplified. That's perfectly fine, but it does mean that it's not known here what the Thread.Sleep() method represents. In fact, in many cases, this sort of thing can be refactored further such that only the long-running work is done asynchronously. This can sometimes simplify the progress-reporting even further, because it can be done after await-ing each individual asynchronously-executed work component.
For example, let's suppose the work in the loop is either inherently asynchronous or is costly enough that it's reasonable to use Task.Run() to execute each loop iteration. For the purpose of the same, that can be represented using Task.Delay():
static class Utility
{
public static async Task<bool> TimeConsumingMethodOne(Action<int> progress)
{
for (int i = 1; i <= 100; i++)
{
await Task.Delay(100);
progress(i);
}
return true;
}
public static async Task<bool> TimeConsumingMethodTwo(Action<int> progress)
{
for (int i = 1; i <= 100; i++)
{
await Task.Delay(50);
progress(i);
}
return true;
}
}
In the above, I also don't use Progress<T>. Just a simple Action<int> delegate for the caller to use however they want.
And with that change, your window code gets even simpler:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void btnRun_Click(object sender, RoutedEventArgs e)
{
await MethodOneCaller();
await MethodTwoCaller();
MessageBox.Show("COMPLETED!");
}
private async Task MethodOneCaller()
{
ProgressBarWindow pbWindowOne =
new ProgressBarWindow("Running Method One") { Owner = this };
pbWindowOne.Show();
await Utility.TimeConsumingMethodOne(i => pbWindowOne.SetProgressUpdate(i));
}
private async Task MethodTwoCaller()
{
ProgressBarWindow pbWindowTwo =
new ProgressBarWindow("Running Method Two") { Owner = this };
pbWindowTwo.Show();
await Utility.TimeConsumingMethodTwo(i => pbWindowTwo.SetProgressUpdate(i));
}
}
Granted, I took the opportunity to remove the MethodType enum and just call the methods directly, which shortened the code even more. But even if all you did was avoid the use of Dispatcher.Invoke(), that still simplifies the code a lot.
In addition to all that, if you were using data binding to represent the progress state instead of setting the value directly, WPF would handle the cross-thread invocation implicitly for you, so that the Progress<T> class isn't even required even if you can't refactor the Utility class code for it itself to be async.
But, those are minor refinements compared to moving away from BackgroundWorker. I recommend doing that, but whether you invest time in those further refinements is less important.
An option i prefer is to have those 2 methods in a different thread and use a while loop to check if thread is still running and if it is use Task.Delay()
EG.
private async void BlahBahBlahAsync()
{
Thread testThread = new Thread(delegate () { });
newThread = new Thread(delegate ()
{
Timeconsuming();
});
newThread.Start();
while (testThread.IsAlive)
{
await Task.Delay(50);
}
}
private void Timeconsuming()
{
// stuff that takes a while
}
I have several textboxes in my wpf application. The LostFocus-Event of each textbox starts a backgroundworker to send the data to a connected serial port.
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data);
}
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e)
{
List<object> data = (List<object>)e.Argument;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do some things after worker completed
}
At this point, everything is working fine.
But sometimes I have to send two data-points directly after each other and there I have a problem.
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data1);
//wait until backgroundworker has finished
online_mode_send_worker.RunWorkerAsync(data2);
}
The Backgroundworker is still running and I get an exception thrown.
Is it possible to wait after the first online_mode_send_worker.RunWorkerAsync(data) until it has finished and then start the second online_mode_send_worker.RunWorkerAsync(data)?
while(online_mode_send_worker.isBusy); is not working because the main-thread is blocking and the RunWorkerCompleted() is not thrown and so the Backgroundwoker is always busy.
I have found something like this, but Application.DoEvents() is not available in wpf.
while (online_mode_send_worker.IsBusy)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
Here is a rough idea of what I mentioned in the comments.
public class Messenger {
private readonly BackgroundWorker online_mode_send_worker = new BackgroundWorker();
private readonly ConcurrentQueue<object> messages;
public Messenger() {
messages = new ConcurrentQueue<object>();
online_mode_send_worker.DoWork += online_mode_send_worker_DoWork;
online_mode_send_worker.RunWorkerCompleted += online_mode_send_worker_RunWorkerCompleted;
}
public void SendAsync(object message) {
if (online_mode_send_worker.IsBusy) {
messages.Enqueue(message);
} else {
online_mode_send_worker.RunWorkerAsync(message);
}
}
public Action<object> MessageHandler = delegate { };
private void online_mode_send_worker_DoWork(object sender, DoWorkEventArgs e) {
if (MessageHandler != null)
MessageHandler(e.Argument);
}
private void online_mode_send_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
object nextMessage = null;
if (messages.Count > 0 && messages.TryDequeue(out nextMessage)) {
online_mode_send_worker.RunWorkerAsync(nextMessage);
}
}
}
You have a queue to hold on to messages that were sent while the background worker was busy and have the worker check the queue for any pending messages when it has completed doing its work.
The messenger can be used like this.
private Messenger messenger = new Messenger();
private void Initialize() { //I would expect this to be in the constructor
messenger.MessageHandler = MessageHandler;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
messenger.SendAsync(data);
}
private void MessageHandler(object message)
{
List<object> data = (List<object>)message;
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0] + XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
It seems that I missed the serial stuff. So what you want to do is synchronize your asynchronuouscalls:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => mySerialDevice1.WriteData(data1));
Task.Run(() => mySerialDevice1.WriteData(data2));
}
public class SerialDevice
{
public Port Port { get; set; }
public object _LockWriteData = new object();
public void WriteData(string data)
{
lock(_LockWriteData)
{
Port.WriteLine(data);
}
}
}
also see:
https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx
https://msdn.microsoft.com/en-us/library/de0542zz(v=vs.110).aspx
ORIGINAL ANSWER
You can use Task instead of Backgroundworker.
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() => OnlineModeSendData(data1));
Task.Run(() => OnlineModeSendData(data2));
}
private void OnlineModeSendData(List<string> data)
{
Port.WriteLine(STARTCHARACTER + XMLSET + XML_TAG_START + data[0]+ XML_TAG_STOP + data[1] + ENDCHARACTER);
string received = Port.ReadLine();
}
I also would like to suggest that you make real objects instead of passing string arrays as arguments.
For Example send BlinkLedRequest:
public class BlinkLedRequest
{
public int LedId{get;set;}
public int DurationInMilliseconds {get;set}
}
and a corresponding method:
public void SendBlinkLed(BlickLedRequest request)
{
....
}
I think your should use RunWorkerCompleted event and add a delegate:
online_mode_send_worker.RunWorkerCompleted += (s, ev) =>
{
if (ev.Error != null)
{
//log Exception
}
//if(conditionToBrake)
// return;
online_mode_send_worker.RunWorkerAsync(data2);
};
online_mode_send_worker.RunWorkerCompleted(data1);
Make sure you put there a condition to avoid infinite loop.
I'd say that if you MUST wait until after the first "job" is done, that what you want is Task.ContinueWith() and change your interface accordingly. The msdn page is good for it IMO, but watch out that you're waiting on the "correct" task object. Hint: it's the return value of ContinueWith() that you should call Wait() on. This is a good pattern to do for launching a Task and then waiting for it later as long as you can keep the Task that is returned so you can wait on it.
For a more generic "I only want one background thread doing things in the order they're added, and I want to wait until they're ALL done and I know when I'm done adding." I would suggest using a BlockingCollection<Action> with only one thread consuming them. An example of how to do that is found in this other answer.
Update:
bw.RunWorkerAsync(data1);
//wait here
bw.RunWorkerAsync(data2);
Is not good aproach, because UI will be blocked on time of waiting. Better:
bw.RunWorkerAsync(new object[] { data1, data2 }); //or new object[] { data1 } if no data2
Original answer:
I advice not to use construction: while (bw.Busy) { ... } (it consumes cpu time), use synchronization objects, for example, ManualResetEvent
BackgroundWorker is great class, but does not support waiting. Just create addition object for waiting:
var bw = new BackgroundWorker();
bw.DoWork += Bw_DoWork;
bw.RunWorkerCompleted += Bw_RunWorkerCompleted;
bool wasError;
ManualResetEvent e = null;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e != null)
return;
wasError = false;
e = new ManualResetEvent(false); //not signaled
bw.RunWorkerAsync(data1);
e.Wait(); //much better than while(bw.Busy())
if (!wasError)
bw.RunWorkerAsync(data2);
e = null;
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//background work in another thread
}
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//catch exception here
wasError = true;
}
e.Set(); //switch to signaled
}
If you need only call twice you can do this:
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
online_mode_send_worker.RunWorkerAsync(data2);
}
But if you need to queue commands you need rewrite in another way Using Task.
One Task where inside it you will have a for-loop where you will send your data through serial port sequentially.
https://msdn.microsoft.com/pt-br/library/system.threading.tasks.task(v=vs.110).aspx
I have a listbox with filenames. When the selected index is changed I load the file.
I want something like jQuery's HoverIntent that delays the action of loading the file for a short time so the user can use the down arrow and quickly cycle through the items in the list without the application trying to load each one. Thread.Sleep pauses the whole app so a user can't select another list item until the sleep completes, this is obviously not what I want.
This will work if your using WinForms, make a call to the InitTimer method in the Form constructor.
Load the file in the _timer_Tick event handler. To change the delay set the Interval property in InitTimer to another value.
private System.Windows.Forms.Timer _timer;
private void InitTimer()
{
_timer = new Timer { Interval = 500 };
_timer.Tick += _timer_Tick;
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
_timer.Stop();
_timer.Start();
}
private void _timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
// TODO: Load file here
}
Use Threading to separate the loading from your GUI.
This should get you started:
public partial class MainWindow : Window
{
CancellationTokenSource cts;
bool loading;
private void SelectedIndexChanged(int index)
{
if (loading)
cts.Cancel();
cts = new CancellationTokenSource();
var loader = new Task.Delay(1000);
loader.ContinueWith(() => LoadFile(index))
.ContinueWith((x) => DisplayResult(x));
loader.Start();
}
private void DisplayResult(Task t)
{
// TODO: Invoke this Method to MainThread
if (!cts.IsCancellationRequested)
{
// Actually display this file
}
}
Could not test, as I'm still on .net 4 whereas Task.Delay() is .net 4.5
You may need to add another field in the form for the file content transfer from the tasks to the GUI.
Winforms:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private CancellationTokenSource _cancel;
private object _loadLock = new object();
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
lock (_loadLock)
{
handleCancellation();
var loader = new Task((chosenFileItemInListbox) =>
{
Thread.Sleep(1000);
LoadFile(chosenFileItemInListbox);
}, listBox1.SelectedItem, _cancel.Token);
}
}
private bool handleCancellation()
{
bool cancelled = false;
lock (_loadLock)
{
if (_cancel != null)
{
if (!_cancel.IsCancellationRequested)
{
_cancel.Cancel();
cancelled = true;
}
_cancel = null;
}
}
return cancelled;
}
private void LoadFile(object chosenFileItemInListbox)
{
if (handleCancellation())
{
return;
}
}
}
The code above could also be applied to WPF, but WPF contains some built in magic for handling delays and cancellation of previous updates.
<ListBox SelectedItem="{Binding Path=SelectedFile, Delay=1000}" />
I am new to C# and am having trouble figuring out how to pass an event from a thread up to the GUI form thread. Any help would be appreciated. All of the examples I find are WAY too complicated. I just want to start with one event from the treat up to the GUI and have the GUI do something (right now, anything).
namespace testEvents
{
public delegate void StuffHappenedDel( MessageEventArgs e);
public partial class Form1 : Form
{
workerThread thread;
int j = 0;
public Form1()
{
InitializeComponent();
thread = new workerThread();
thread.Start();
}
private void button1_Click(object sender, EventArgs e)
{
thread.Stop();
}
private void StuffHappenedDel(Object seder, EventArgs e)
{
j++;
}
}
public class workerThread
{
Thread worker;
private bool _quit = false;
/* I don't think this next line is correct*/
public event StuffHappenedDel StuffHappened;
protected virtual void OnStuffHappened(MessageEventArgs e)
{
if (StuffHappened != null)
StuffHappened( e);
}
public void Start()
{
ThreadStart start = new ThreadStart(Run);
worker = new Thread(start);
worker.Start();
}
private void Run()
{
int i = 0;
while (!_quit)
{
Thread.Sleep(1000);
i++;
OnStuffHappened(new MessageEventArgs(false, "it worked!"));
Console.WriteLine(string.Format("Slept {0} seconds.",i));
}
Console.WriteLine("Thread exiting");
}
}
public class MessageEventArgs : EventArgs
{
public MessageEventArgs(bool Error, string message)
{
IsError = Error;
Message = message;
}
public bool IsError { get; set; }
public string Message { get; set; }
}
}
You need to register Form1 as a listener for the event. First, add a method like the following to Form1:
private void thread_SuffHappened(MessageEventArgs e)
{
MessageBox.Show("Stuff happened!");
}
And in Form1's constructor, register as a listener like so:
public Form1()
{
InitializeComponent();
thread = new workerThread();
thread.StuffHappened += new StuffHappenedDel(thread_StuffHappened);
thread.Start();
}
Do you have to use this custom threading system, or are you able to use BackgroundWorkers? BackgroundWorkers haven an event ProgressChanged which fires on the thread that created the BackgroundWorker.
Alternatively, if you attach a handler to a background event from the UI thread, the work is still done on the background thread.
thread.StuffHappenedDel += new EventHandler<MessageEventArgs>(StuffHappenedDel);
Therefore, you need to marshall the data to the UI thread. One way is by using BeingInvoke.
private void StuffHappenedDel(object sender, MessageEventArgs e)
{
this.myControl.BeginInvoke( new Action(
() =>
{
//UI thread work (likely anything that affects UI. Heavy
//processing can continue on the bg thread outside this code block
}));
}
Also, you can use if (myControl.InvokeRequired) to check if you need to marshall data when changing a particular control.
if (this.InvokeRequired)
{
this.Invoke((Action)(() =>
{
//UI thread stuff
}
));
}
Edit to clarify
Your thread object that you've created needs to attach an event handler to the StuffHappenedDel event. To do this, you use something like this
thread.StuffHappenedDel += new EventHandler<MessageEventArgs>(StuffHappenedDel);
before you call thread.Start(). Now, this handler is called
private void StuffHappenedDel(Object seder, MessageEventArgs e)
{
j++;
}
whenever your event is fired.
If you want to make changes to any UI elements, you need to use the Invoke method described above.
Look into the Background Worker Class. Also, you can always fire an event that is handled by your GUI Class (though not on the GUI Thread) and then call Invoke
my aim is that in the function "Dummy" i can change the controls like labels etc of the form from which the thread is initiating..how to do it..please don't suggest completely different strategies or making a worker class etc...modify this if you can
Thread pt= new Thread(new ParameterizedThreadStart(Dummy2));
private void button1_Click(object sender, EventArgs e)
{
pt = new Thread(new ParameterizedThreadStart(Dummy2));
pt.IsBackground = true;
pt.Start( this );
}
public static void Dummy(........)
{
/*
what i want to do here is to access the controls on my form form where the
tread was initiated and change them directly
*/
}
private void button2_Click(object sender, EventArgs e)
{
if (t.IsAlive)
label1.Text = "Running";
else
label1.Text = "Dead";
}
private void button3_Click(object sender, EventArgs e)
{
pt.Abort();
}
}
}
what i plan is that i could do this in the "Dummy" function
Dummy( object p)
{
p.label1.Text = " New Text " ;
}
You could do this, supposing you're passing an instance of the form to the thread method using the t.Start(...) method:
private void Form_Shown(object sender)
{
Thread t = new Thread(new ParameterizedThreadStart(Dummy));
t.Start(this);
}
....
private static void Dummy(object state)
{
MyForm f = (MyForm)state;
f.Invoke((MethodInvoker)delegate()
{
f.label1.Text = " New Text ";
});
}
EDIT
Added thread start code for clarity.
You can't do this. You can only access a UI control on the same thread that created it.
See the System.Windows.Forms.Control.Invoke Method and the Control.InvokeRequired property.
Can use something like this:
private void UpdateText(string text)
{
// Check for cross thread violation, and deal with it if necessary
if (InvokeRequired)
{
Invoke(new Action<string>(UpdateText), new[] {text});
return;
}
// What the update of the UI
label.Text = text;
}
public static void Dummy(........)
{
UpdateText("New text");
}