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

Covariance and contravariance in C# 4.0


C# 4.0 introduces covariance and contravariance (together variance) for generic type parameters. These two concepts are similar and allow the use of derived or base classes in a class hierarchy.

An easy way to understand the difference between the two concepts is to consider the activity of the user of the variables passed to / from the generic method (interface or delegate.)

Contravariance

If the method implementation is only using variables passed with the parameter for read activity – then the generic parameter is a candidate for contravariance. The ‘read only’ role of the parameter type can be formalised by marking it with the in keyword – i.e. it is an input to the implementation.

Any generic type parameter marked with the in keyword will be able to match to types that are derived from the named type. In this case the implementation is the user of the variables. It makes sense to allow derived classes as any read activities that are available on the base class will also be available on any derived class.

// C# 3.0 does not recognise the in keyword for parameterized types
public delegate void TypeReporter<in T>(T input);

public void DoSomeGenericContravariance(RoutedEventArgs args)
{
    TypeReporter<EventArgs> ReportMethod
                  = new TypeReporter<EventArgs>(this.SimpleReportMethod);

    // C# 3.0 and earlier will not compile the following
    TypeReporter<RoutedEventArgs> RoutedReportMethod = ReportMethod;

    RoutedReportMethod(args);
}

public void SimpleReportMethod(EventArgs args)
{
    Console.WriteLine(args.GetType().ToString());
}

Covariance

Similarly – if a method implementation is only using variables passed with the parameter for write activity – then the generic parameter is a candidate for covariance. The ‘write only’ role of the parameter type can be formalised by marking it with the out keyword – i.e. it is an output from the implementation.

Any generic type parameter marked with the out keyword will be able to match to types that are base classes of the named type. In this case the calling / client code can be considered the user of the variables – so again it makes sense. Any operation (read or write) that is available on the base class that the client code has requested will also be available on the actual (derived) type that the implementation instantiates / sends as output.

public void DoSomeGenericCovariance()
{
    // The generic IEnumerable interface is defined with the out keyword
    // on the parameter type:
    //
    // public interface IEnumerable<out T> : IEnumerable

    List<String> strings = new List<String> { "one", "two" };
    IEnumerable<String> myStrings = strings.AsEnumerable();

    // C#3.0 and earlier will not compile the following
    IEnumerable<object> myObjects = myStrings;

    foreach (object myObject in myObjects)
    {
        Console.WriteLine(myObject.ToString());
    }
}

Issues to consider

Once type parameters have been marked with the in or out keyword the compiler will validate the interface / delegate compliance with the assigned variance. E.g. if the first example is changed to return the parameter passed, then the compiler will report an error – as the parameter is not being used for input only.

// Compiler will report a variance validation error
public delegate T TypeReporter<in T>(T input);

Variance uses reference type conversion – so it will not work with value types. Even though within the type system int inherits from object, the following will not compile.

List<int> ints = new List<int> { 1, 2 };
IEnumerable<int> myInts = ints.AsEnumerable();

// Reference type conversion not available
IEnumerable<object> myIntObjects = myInts;