views:

80

answers:

2

Right now, I have a windows application (C#) that spits out long running reports. Each report grabs some information from the UI to constrain the reports. Of course, the report creation happens on a background thread and I need to properly invoke the various controls to get things like selected indexes and values. But I don't think my code looks good and was hoping there was a better pattern. Because there are so many requests to controls, I made generic delegates for each type that would be returned:

private delegate string StringDelegate();
private delegate int IntDelegate();

Further down, there are various instantiations of these delegates:

private StringDelegate GetYearSelectedItem = new StringDelegate(cmbYearAsync);

YearAsync looks like this:

private string cmbYearAsync() {
    return cmbYear.SelectedItem.ToString();
}

And finally, in code that is on a background thread, this is how I get the values:

cmbYear.Invoke(GetCmbYearSelectedItem);

Is there a cleaner way of getting these values from a separate thread?

+3  A: 

short answer

Don't do that ;-)

long answer

gather the report parameters from the UI controls up front (perhaps in a small class) and pass them to the reports in the background threads

This eliminates the cross-thread calls to the UI controls, and decouples the report parameters from the user-interface

Steven A. Lowe
+1  A: 

Assuming you want to go with the threaded design (perhaps you want the background threads to report partial results to the UI as they run?), if you have C# 3 you can certainly neaten it up a little. Some people find an extension method like this helpful:

public static class ControlExtensions
{
    public static T Invoke<T>(this Control ctrl, Func<T> func)
    {
        if (ctrl.InvokeRequired)
            return (T) ctrl.Invoke(func);

        return func();
    }
}

As well as checking whether the call needs to be marshalled, it also makes the delegate parameter typesafe. So you can write:

string selected = comboBox1.Invoke(() => comboBox1.SelectedItem.ToString());

No need to pre-declare all that other stuff. Just write the usual code you'd write but inside that lambda pattern.

You can write a similar extension method that takes Action instead of Func<T>, and returns void, for operations that don't need a return value.

Daniel Earwicker