Use explicit marshalling to update a WPF UI from a non-UI thread


One option for updating a WPF UI from a non-UI thread (including a background worker) is to perform explicit marshalling using the dispatcher. A simple example follows.

A separate blog entry details how to update a UI using a background worker’s implicit marshalling.

Lets assume there is a C# window mediator class that has a reference to a pair of WPF controls – one for user input and one for user reporting. The WPF window constructs the mediator and sets the two control properties during its construction. Two functions provide access to the data and may be called from any thread:

public partial class MainWindow : Window
{
    private WindowMediator m_mediator = null;

    public MainWindow()
    {
        InitializeComponent();
   
        m_mediator = new WindowMediator();

        // Controls declared in the window's XAML
        m_mediator.IncomingDataControl = m_xamlTextBox;
        m_mediator.ReportDataControl = m_xamlTextBlock;
        ...
    }
    ...
}

public class WindowMediator
{
    // Controls.  A TextBox to retrieve data and a TextBlock to report data
    public TextBox IncomingDataControl { private get; set; }
    public TextBlock ReportDataControl { private get; set; }

    // Access functions to retrieve and set data (also see below)
    public String GetIncomingData(bool reformat) { ... }
    public void SetReportData(String newReport) { ... }
}

When updating the values of a WPF control, the code needs to be executed on the UI thread – i.e. the thread that owns the WPF control. The control’s dispatcher provides a function CheckAccess (which is the equivalent of the Windows Forms property InvokeRequired) to determine whether the call is currently executing on the UI thread.

If not – the Invoke method of the dispatcher can be used to execute a delegate on the appropriate thread. The Action framework class can be used to generate a Delegate from the current method (or from an anonymous method) and pass the parameters across:

public void SetReportData(String newReport)
{
    if (!ReportDataControl.Dispatcher.CheckAccess())
    {
       // Switch threads and recurse
       ReportDataControl.Dispatcher.Invoke(
          System.Windows.Threading.DispatcherPriority.Normal,
          new Action<String>(SetReportData), newReport);
    }
    else
    {
        ReportDataControl.Text = newReport;
    }
}

A similar method can be used to retrieve data from a WPF control. The generic framework class Func can be used to add a return type:

public String GetIncomingData(bool reformat)
{
    String result = "";

    if (!IncomingDataControl.Dispatcher.CheckAccess())
    {
       // Switch threads and recurse
       result = (String) IncomingDataControl.Dispatcher.Invoke(
          System.Windows.Threading.DispatcherPriority.Normal,
          new Func<bool, String>(GetIncomingData), reformat);
    }
    else
    {
        if (reformat)
        {
             result = "--" + IncomingDataControl.Text;
        }
        else
        {
             result = IncomingDataControl.Text;
        }
    }

    return result;
}
Advertisements