1

Inside a class I have got a TextBox:

public class TextBoxAdapter {

    private System.Windows.Forms.TextBox textBox;

    //...some code that initializes the textBox...

    public string getTextFromBox() {
        if( textBox.InvokeRequired )
            return (string)textBox.Invoke( (Func<string>)delegate { return textBox.Text; } );
        else
            return textBox.Text;
    }
}

To access this TextBox safely from another Thread I would like to use the Invoke mechanism. But the function getTextFromBox() fails at the line where Invoke() is used. I verified this by putting a breakpoint at this line and pressing F10 (Step over). It fails without an exception. Is there a mistake at my way of invoking?

Edit

Why do I need to access a text box from another thread? I am trying to create a new thread on every button click to prevent my UI from freezing. E.g. on a user login window when the login button is pressed, a new thread is started that notifys and observer. The observer then wants to read the the values of the username- and password-textbox to check if the logintry is valid.

The strange thing about this: Writing to a textbox works without any problem. The code I use:

        if ( textBox.InvokeRequired ) {
            MethodInvoker setText = new MethodInvoker( () => {
                textBox.Text = text;
            } );
            textBox.BeginInvoke( setText );
        }
        else {
            textBox.Text = text;
        }
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Blackbriar
  • 477
  • 4
  • 14
  • 2
    Maybe there's an XY problem in your case. Do you really need to access UI elements form background threads? What are you actually trying to achieve? – dymanoid Nov 29 '18 at 10:52
  • It may be useful to see more of your code - I have created a Winforms application with a textbox & tested with a thread & I get the current textbox text returned. – PaulF Nov 29 '18 at 11:57
  • Thank you for your comments and sorry for my late answer. For further information about my problem, see the edit in my question. @paulF Strange, will try that, too. – Blackbriar Dec 03 '18 at 07:56
  • @dymanoid What is an XY problem? – Blackbriar Dec 03 '18 at 07:56
  • See [XY problem](http://xyproblem.info). And in fact, it seems you have one. Creating threads to keep the UI responsive seems a very bad way to go. I've _always_ managed to keep my UI responsive, solely with the help of the [`ThreadPool`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.threadpool) and [`BackgroundWorker`](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker). – Uwe Keim Dec 03 '18 at 08:04
  • @UweKeim Thanks for your answer. The description of a backgroundworker on the microsoft page is _"Executes an operation on a separate thread"_. It seems like a Backgroundworker is just a wrapper around my solution. And why is it a bad was to go, to keep my UI responsive? – Blackbriar Dec 03 '18 at 08:14
  • "[Thread vs ThreadPool](https://stackoverflow.com/q/230003/107625)". Grüezi – Uwe Keim Dec 03 '18 at 08:29

1 Answers1

1

Generally speaking, it shouldn't be necessary to access the UI elements from worker threads.

You should change your approach. I suppose you're on .NET Framework 4.5 or newer, so there's a right pattern for you: TAP, more commonly known as async/await.

With this pattern, you don't care about the threads. The Framework cares about them for you. Your task is to tell the compiler, what needs to be performed as an asynchronous action.

Just an example - change the event handler of your Button's Click event to something like this:

async void LoginButton_Click(object sender, EventArgs e)
{
    // This runs on the UI thread
    string login = loginTextBox.Text;
    string password = pwdTextBox.Text;

    loginButton.Enabled = false;

    // This will be executed asynchronously, in your case - on a worker thread
    bool success = await Task.Run(() => myLoginProcessor.Login(login, password));

    // This runs again on the UI thread, so you can safely access your controls
    if (success)
    {
        labelResult.Text = "Successfully logged in.";
    }
    else
    {
        labelResult.Text = "Invalid credentials.";
    }

    loginButton.Enabled = true;
}
dymanoid
  • 14,771
  • 4
  • 36
  • 64