views:

76

answers:

3

I have no issues invoking actions on UI controls through BeginInvoke and such, however, that's a void method. I need to get a value from a textbox for use in a different thread.

How can I accomplish this?

A: 

You can pass a delegate to Invoke that points to a method which can set an instance variable to hold the text value, e.g.

    public Form1()
    {
        InitializeComponent();

        Invoke(new Action(SetTextboxTextVariable));
    }

    private string _text;

    private void SetTextboxTextVariable()
    {
        _text = txtBox.Text;
    } 

This covers the basic idea, synchronizing and notifying other threads is up to you. :)

Update

Or to do it in one step:

    public Form1()
    {
        InitializeComponent();

        string text = GetText();
    }

    private string GetText()
    {
        string text;

        Invoke(new Action(text = txtBox.Text));

        return text;
    }
chibacity
+3  A: 

The simplest solution is to capture a local variable in a closure.

String text;
textBox.Invoke(() => text = textBox.Text);

The compiler will generate some code that is much like chibacity's solution - the local variable becomes a field of a compiler-generated class.

UPDATE

This does not work - the lambda expression is not assignable to Delegate. This problem can be solved using an extension method.

internal void ExecuteOnOwningThread(this Control control, Action action)
{
    if (control.InvokeRequired)
    {
        control.Invoke(action);
    }
    else
    {
        action();
    }
}

The usage is then as follows.

String text;
textBox.ExecuteOnOwningThread(() => text = textBox.Text);

It is possible to stuff multiple statements into the lambda expression.

textBox.ExecuteOnOwningThread(() =>
{
    DoStuff();
    text = textBox.Text
    DoOtherStuff();
});

But as chibacity already mentioned in a comment it may be better to explicitly write a method. Beyond a certain point using lambda expressions will adversely affect the readability of the code. And using lambda expressions is of course very prone to introducing repeated code.

Daniel Brückner
Whilst having the one-liner is cute, eventually you're going to be doing more than setting the variable e.g. tell another thread there is some data ready. Having a method to stuff this into is the way to go.
chibacity
I absolutely agree with you - for one, two, or maybe three statements that are not repeated several times I would consider using a lambda expression but beyond that using a separate method yields the better code.
Daniel Brückner
+1  A: 

The Control.Invoke() method returns the value of the invoked method. Which makes it easy:

        string value = (string)this.Invoke(new Func<string>(() => textBox1.Text));

Or a bit more explicit:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
        var dlg = new Func<string>(GetText);
        string value = (string)this.Invoke(dlg);
    }
    private string GetText() {
        return textBox1.Text;
    }
Hans Passant