mickieparis said:
can someone point me to some code examples where a class library (dll)
is sending events to a form. I understand better when I read code.
It will look just like any other class raising an event. See MSDN for
example:
http://msdn.microsoft.com/en-us/library/awbftdfh.aspx
The main thing that will be "special" about your particular
implementation is that, since the event handler is trying to update a
control, if you run the processing in a thread other than the main GUI
thread that owns that control (and you should do that if it's in any way
time-consuming), _and_ you don't use BackgroundWorker to run that
process, you will need to use the Control.Invoke() method to invoke the
code that will actually update the GUI.
If you do use BackgroundWorker, and you use the ProgressChanged event on
BackgroundWorker rather than writing your own (which of course would be
the main reason to use BackgroundWorker), then the event handler will
already be executing on the correct thread, and you can update the GUI
as you normally would.
Here's a short example, showing both the use of an event in a worker
class to pass strings back to the client as well as the use of
BackgroundWorker to simplify the cross-thread issues:
class Worker
{
// A dummy task that will take some time to present a sequence
// of strings to the client code
public void TimeConsumingTask()
{
string[] rgstr = { "One", "Two", "Three", "Four", "Five" };
foreach (string str in rgstr)
{
Thread.Sleep(1000);
ReportMessage(str);
}
}
// An event in the Worker class that client code can
// subscribed to in order to receive the strings
public event Action<string> ReportMessage = delegate { };
}
class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
Worker worker = new Worker();
BackgroundWorker bw = new BackgroundWorker();
// This event handler is executed on the background thread,
// when we finally call BackgroundWorker.RunWorkerAsync()
bw.DoWork += (sender, e) =>
{
worker.TimeConsumingTask();
};
// This event handler is raised by the TimeConsumingTask()
// method and so is also executed on the background thread.
worker.ReportMessage += (str) =>
{
// The second argument passed to ReportProgress() will
// show up in the ProgressChangedEventArgs.UserState
// property.
bw.ReportProgress(0, str);
}
// This event handler is raised when the ReportProgress()
// method is called, with BackgroundWorker automatically
// ensuring that it's raised in the same thread where
// the BackgroundWorker instance was created (i.e. the
// thread that owns the Form1 instance and its controls,
// such as the "label1" Label instance).
bw.ProgressChanged += (sender, e) =>
{
label1.Text = (string)e.UserState;
}
// Start the background task running
bw.RunWorkerAsync();
// ...and then this method exits, allowing the main GUI
// thread to keep pumping messages, responding to user
// input and changes to the UI, such as changes to the
// Text property of a Label control
}
}
Note that while the above looks lengthy, most of that length is in comments.
One alternative to the above is to have the worker class manage its own
thread, in which case your "ReportMessage" event handler would need to
explicitly update the "label1.Text" property, using the Control.Invoke()
method:
worker.ReportMessage += (str) =>
{
label1.Invoke((MethodInvoker)delegate
{
label1.Text = str;
});
}
Another alternative would be for the worker thread code to actually be
in the Form1 class, rather than in a separate class. In this case, you
probably wouldn't bother with the ReportMessage event itself, instead
just directly doing the work the event handler for that event would have
done.
IMHO, the latter alternative is inferior and should be avoided. But it
can be done.
Pete