Should I put a BackgroundWorker inside a Singleton? - c#

I have an application that takes a Wireshark capture file and feeds it (all the containing packets) into a network adapter.
Currently my application is a big mess - countless global variables & every task opened within a seperate BackgroundWorker instance etc...
To clarify - the purpose of using BackgroundWorkers here (more specifically the DoWork, RunWorkerCompleted events and the WorkerReportsProgress property) is to prevent the packet feeding operations from freezing my UI. To stop an operation, I need access to these workes - for now, global variables are used to achieve this.
So the question is - should I place my BackgroundWorker objects inside a Singleton-type class and then call this object when necessary?

From a technical point of view is possible, after all the singleton pattern is a design pattern that restricts the instantiation of a class to one object
you can try something like this
public class BackWorkerSingleton
{
private BackgroundWorker _backgroundWorker;
private static readonly object myLock = new object();
private static BackWorkerSingleton _backWorkerSingleton = new BackWorkerSingleton();
public delegate void ReportProgressEventHandler(object sender,MyEventsArgs e);
public event ReportProgressEventHandler ReportProgress = delegate{ };
private BackWorkerSingleton()
{
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);
}
void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.ReportProgress( this, new MyEventsArgs(){Progress = e.ProgressPercentage});
}
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// do your work here
}
public void StartTheJob()
{
_backgroundWorker.RunWorkerAsync();
}
public static BackWorkerSingleton Worker
{
get
{
lock (myLock)
{
if (_backWorkerSingleton == null)
{
_backWorkerSingleton = new BackWorkerSingleton();
}
}
return _backWorkerSingleton;
}
}
}
class MyEventsArgs:EventArgs
{
public int Progress { get; set; }
}
and here the report progress
private void Form1_Load(object sender, EventArgs e)
{
BackWorkerSingleton.Worker.ReportProgress += new BackWorkerSingleton.ReportProgressEventHandler(Worker_ReportProgress);
}
void Worker_ReportProgress(object sender, MyEventsArgs e)
{
}
and call it like this
BackWorkerSingleton.Worker.StartJob()

Related

DispatcherTimer ticking not working when started in ThreadPool.QueueUserWorkItem() method

I've created a TimerManager class for my WPF application.
This class handles the start and stop the dispatcher timer.
Here is the class:
public static class TimerManager
{
static DispatcherTimer disTimer;
static Model m = Model.GetInstance();
static TimerManager()
{
disTimer = new DispatcherTimer();
disTimer.Tick += disTimer_tick;
disTimer.Interval = new TimeSpan(0, 0, 1);
}
public static void StartTimer()
{
disTimer.Start();
}
public static void StopTimer()
{
disTimer.Stop();
}
private static void disTimer_tick(object sender, EventArgs e)
{
m.Tick++;
}
}
And I've created a Model class that represents the ticking in the UI.
(Binding in MainWindow.xaml -> xy textbox text field "{Binding Tick}").
class Model : INotifyPropertyChanged
{
private Model()
{
}
static Model instance;
public static Model GetInstance()
{
if (instance == null)
{
instance = new Model();
}
return instance;
}
int tick;
public event PropertyChangedEventHandler PropertyChanged;
public void OnNotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
public int Tick
{
get
{
return tick;
}
set
{
tick = value;
OnNotifyPropertyChanged();
}
}
}
And here is the MainWindow class:
Model m;
public MainWindow()
{
InitializeComponent();
m = Model.GetInstance();
this.DataContext = m;
}
private void startButton_Click(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem(o =>
{
TimerManager.StartTimer();
});
//TimerManager.StartTimer();
}
private void stopButton_Click(object sender, RoutedEventArgs e)
{
TimerManager.StopTimer();
}
When I click the start button I use the ThreadPool.QueueUserWorkItem() method. In that method, I start the timer but the timer tick is not run at every one second.
When I don't use ThreadPool this works. But this solution is not good for me; ThreadPool is important for me because I use an HTTP web server (in local).
My question is: why is the ticking not working if I use ThreadPool?
The DispatcherTimer object has thread affinity. That is, it is tied to a specific thread. In particular, it is designed specifically to raise its Tick event in the thread in which it was created, using the Dispatcher for that thread.
Your ThreadManager class's static constructor will be called when the type is first used. In your non-working example, this occurs in the queued work item method, causing the static constructor to be executed in the thread pool thread used to execute that work item method. This in turn causes the DispatcherTimer object you create to be owned by that thread, and to have its Tick event raised in that thread by the Dispatcher for that thread.
Except, thread pool threads don't have Dispatchers. So there's no Dispatcher there to raise the Tick event for the DispatcherTimer object. Even if there was, without a call to Application.Run() to have the dispatcher loop executed, the Dispatcher wouldn't actually get to dispatch anything, including the Tick event.
What you need is to make sure that when you create the DispatcherTimer object, the code that creates that object is executed in the dispatcher thread, which is your main UI thread.
There are a couple of ways to do that. IMHO, the best way is to make your ThreadManager class not a static class and to create an instance of it in your MainWindow constructor. For example:
class TimerManager
{
DispatcherTimer disTimer;
Model m = Model.GetInstance();
public TimerManager()
{
disTimer = new DispatcherTimer();
disTimer.Tick += disTimer_tick;
disTimer.Interval = new TimeSpan(0, 0, 1);
}
public void StartTimer()
{
disTimer.Start();
}
public void StopTimer()
{
disTimer.Stop();
}
private void disTimer_tick(object sender, EventArgs e)
{
m.Tick++;
}
}
and:
public partial class MainWindow : Window
{
TimerManager _timerManager = new TimerManager();
public MainWindow()
{
InitializeComponent();
this.DataContext = Model.GetInstance();
}
private void startButton_Click(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem(o =>
{
_timerManager.StartTimer();
});
}
private void stopButton_Click(object sender, RoutedEventArgs e)
{
_timerManager.StopTimer();
}
}
Since you know your MainWindow object has to be created in the dispatcher thread, and you know that non-static field initialization happens at the same time the constructor is called, in that same dispatcher thread, the above ensures that your TimerManager object is created in the dispatcher thread.
This gives you complete control over the lifetime of the TimerManager object, particularly when it's created but of course also when it can be discarded. Given the nature of the DispatcherTimer object itself, it's my opinion that this is better than maintaining a statically-held instance.
This approach also gives you the option of having a manager object for each dispatcher thread (in rare cases, a program might have more than one…you should try very hard to avoid getting into that situation, but it can be useful for types to at least be compatible with such a situation).
That said, if you really want to keep the static implementation, you can do that by providing a method that can be called explicitly when you want to initialize the class, so you can make sure that the initialization happens in the right thread:
static class TimerManager
{
static DispatcherTimer disTimer;
static Model m = Model.GetInstance();
public static void Initialize()
{
disTimer = new DispatcherTimer();
disTimer.Tick += disTimer_tick;
disTimer.Interval = new TimeSpan(0, 0, 1);
}
public static void StartTimer()
{
disTimer.Start();
}
public static void StopTimer()
{
disTimer.Stop();
}
private static void disTimer_tick(object sender, EventArgs e)
{
m.Tick++;
}
}
Then in your MainWindow class:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = Model.GetInstance();
StaticTimerManager.Initialize();
}
private void startButton_Click(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem(o =>
{
StaticTimerManager.StartTimer();
});
}
private void stopButton_Click(object sender, RoutedEventArgs e)
{
StaticTimerManager.StopTimer();
}
}
All you need to do here is make sure you call the Initialize() method from the main UI thread where you actually have a running dispatcher, before you attempt to call either of the other two static methods in the class.
This approach could also be made to work with multiple threads (i.e. if you have more than one dispatcher thread), but it would be trickier, especially if you want to be able to call the StartTimer() method from a different thread that actually owns the timer object. I'd recommend against the static class approach if you really did wind up in that situation.

wpf c# backgroundworker wait until finished

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

C# - How to update Main UI from a thread in another class

I'm actually learning (the hard way) c# and been fighting for days with a problem :
I'm writing my first c# application with WPF (dotNet 4.0). When I click on a button, a BackgroundWorker thread is used and call a method from an external class, this way my UI don't freeze -> my method run as expected.
Then I tried to update a ListView control from thos external class to get some kind of progress (text) and I miserably failed.
I understand that I need to use a delegate and the dispatcher to update my control.
I tried to use the solution offered here How to update UI from another thread running in another class . (I cannot comment on it because of my low rep) and I miss some parts of the puzzle.
What the YourEventArgs(status) is referring to ? I just don't get the way to fire an event and pass the content back to my UI while my method is running inside the BGW.
So far I have this piece of code (Updated from answer):
namespace AppMain
{
public partial class MainWindow
{
BackgroundWorker AppWorker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
AppWorker.WorkerSupportsCancellation = true;
AppWorker.DoWork += AppWorker_DoWork;
AppWorker.RunWorkerCompleted += AppWorker_RunWorkerCompleted;
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
lstTest.Items.Add("Processing data...");
AppWorker.RunWorkerAsync();
}
public void AppWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
SetXmlData xml = new SetXmlData();
xml.ProgressUpdate += (s, evt) =>
{
Dispatcher.BeginInvoke((Action)(() =>
{
lstTest.Items.Add("this is a test : " + evt.myData); //how to retrieve the myData property from evt ?
}));
};
xml.FlushData();
}
public void AppWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (!(e.Cancelled))
{
lstTest.Items.Add("Done");
}
else
{
MessageBox.Show("Cancelled");
}
}
}
}
SetXmlData.cs
namespace AppMain
{
public class SetXmlData
{
public event EventHandler ProgressUpdate;
//update method
public void update(object input)
{
if (ProgressUpdate != null)
ProgressUpdate(this, new YourEventArgs { myData = (string)input });
}
//calculation method
public void FlushData()
{
MessageBox.Show("this is a test !");
update("test");
}
}
public class YourEventArgs : EventArgs
{
public string myData { get; set; }
}
}
Thanks for your help.
You can simply Invoke the ProgressUpdate event from the FlushData() method.
Simply call:
If (ProgressUpdate !=null )
{
ProgressUpdate(this,new YourEventArgs())
}
this is the source instance where the event originated from.
You could just create YourEventArgs by inheriting from EventArgs class.
public class YourEventArgs : EventArgs
{
//Put any property that you want to pass back to UI here.
}
When the event gets raised in the UI:
RaiseEvent.ProgressUpdate += (s, e) =>
{
Dispatcher.BeginInvoke((Action)(() =>
{
lstTest.Items.Add("this is a test : ");
//Add items to your UI control here...
}));
};
e will be of type YourEventArgs.
On a side note, you should never touch UI thread from a diffent thread (like background worker thread in your example). Since your event-handler already does the Dispatcher.BeginInvoke, that's safe.
Also, your ProgressUpdate event should be inside of your class SetXmlData.
try get;set; Example:
Form1:
public partial class Form1 : Form
{
static public string gettext { get; set; }
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Class1.send(); //call to class function
textBox1.Text = gettext; //items.add(gettext)
}
}
Class1:
class Class1
{
static public void send()
{
Form1.gettext = "Marko"; //Set gettext to string "Marko"
}
}

Setting a delay for an action on ListBox _SelectedIndexChanged

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}" />

How to pass a single event from thread up to GUI form thread?

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

Categories

Resources