October 7, 2008

Invoke/BeginInvoke On A GUI Thread

Good article on BeginInvoke and Invoke differences
Here is a code template for it. On the GUI class (form or control derived object):
  private delegate void DataChanged(Object Sender, BindingEventArgs Args);

  private void HandleDataChanged(Object
Sender, BindingEventArgs Args)
  {
    // If this delegate is invoked on another thread
    if (this.InvokeRequired)
    {
      // Asynchronously call THIS method later
      // but this time on our own GUI thread
      // BeginInvoke translates to a PostMessage call on
      // our window.
      BeginInvoke(new DataChangedDelegate(HandleDataChanged),
 new object[] { Sender, Args });
    }
    else // when the method is invoked on the GUI thread
    { // Update our forms/controls as required
      UpdateGui(Args);
    }
  }
Simpler form of the InvokeRequired/BeginInvoke or Invoke pattern using the MethodInvoker
private void OnHandleDataChanged()
{
  // If this delegate is invoked on another thread
  if (this.InvokeRequired)
  {
    // Asynchronously call THIS method later
    // but this time on our own GUI thread
    // BeginInvoke translates to a PostMessage call on
    // our window.
    // Invoke maps to a SendMessage call, it will BLOCK 
    // the calling thread until finished
    BeginInvoke/Invoke(new MethodInvoker(HandleDataChanged));
  }
  else // when the method is invoked on the GUI thread
  { 
    // Update our forms/controls as required
  }
}
Simplest InvokeRequired Pattern, again this is within the GUI object:
// Define the delegate
private delegate void SomeDelegate(string arg1, int arg2);
// or (easier) use Action<string, int>, see below

private void HandleDataChanged(string arg1, int arg2)
{
  // IF this delegate is invoked on another thread
  if (this.InvokeRequired)
  { // THEN Call it again asynchronously on the GUI thread 
    this.BeginInvoke(
     new Action<string, int>(HandleDataChanged),
     // Make sure the number of args here matches the number 
     // of args in the delegate!
     new object[] { arg1, arg2 });
    return;
  }
  // From this point onwards we are definitely on the GUI thread again
  
  // Update our forms/controls as required
  ...    
}
As a rule use BeginInvoke unless Synchronization is critical when marshalling to the GUI thread. Note that BeginInvoke() in this context (marshalling to a GUI thread) is a fire and forget thing, no need to call EndInvoke() and all that shenanigans.

No comments: