When you subscribe to an event on an object from within a form, you are essentially handing over control of your callback method to the event source. You have no idea whether that event source will choose to trigger the event on a different thread.
The problem is that when the callback is invoked, you cannot assume that you can make update controls on your form because sometimes those controls will throw an exception if the event callback was called on a thread different than the thread the form was run on.
Load: This event occurs before a form is displayed for the first time. VisibleChanged: This event occurs when the Visible property value changes. Activated: This event occurs when the form is activated in code or by the user. Shown: This event occurs whenever the form is first displayed.
In C#, you register a callback method for a given event by using the += operator: object. event += callback; Substitute object with any EFL object and event with the identifier of the event (such as PollHighEvt or TickEvt ). Set callback to the method to be invoked when the event occurs.
To simplify Simon's code a bit, you could use the built in generic Action delegate. It saves peppering your code with a bunch of delegate types you don't really need. Also, in .NET 3.5 they added a params parameter to the Invoke method so you don't have to define a temporary array.
void SomethingHappened(object sender, EventArgs ea) { if (InvokeRequired) { Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea); return; } textBox1.Text = "Something happened"; }
Here are the salient points:
So, if you have a separate "engine" thread doing some work and have some UI watching for state changes which can be reflected in the UI (such as a progress bar or whatever), you have a problem. The engine fire's an object changed event which has been hooked by the Form. But the callback delegate that the Form registered with the engine gets called on the engine's thread… not on the Form's thread. And so you can't update any controls from that callback. Doh!
BeginInvoke comes to the rescue. Just use this simple coding model in all your callback methods and you can be sure that things are going to be okay:
private delegate void EventArgsDelegate(object sender, EventArgs ea); void SomethingHappened(object sender, EventArgs ea) { // // Make sure this callback is on the correct thread // if (this.InvokeRequired) { this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea }); return; } // // Do something with the event such as update a control // textBox1.Text = "Something happened"; }
It's quite simple really.
This is a very compact way of addressing this problem and making your Forms safe from multi-threaded event callbacks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With