1

I am calling a login() method which handles connection to mysql server and then authenticating a user. Connecting to mysql server takes some time so I want to place a label which shows status of "Loading". I am doing in it something like this:

private void button_login_Click(object sender, EventArgs e)
{
    label_status.Text = "Loading ...";
    login();
}

But I dont see label_status text changed to "Loading ...". Instead it shows this status in label_status after login function gets back.

I dont know why is this? Is this some threading issue? Any help would be appreciated.

Cole Tobin
  • 9,206
  • 15
  • 49
  • 74
awatan
  • 1,182
  • 15
  • 33

2 Answers2

4

The UI thread will be blocked for the duration of your event handler. If you want to be able to update the UI you need to perform the long running task in a non-UI thread. The BackgroundWorker is specifically designed for doing this. Here is a tutorial on how to use one.

You should set the text before starting the BackgroundWorker, set the DoWork method to execute login and if there is any UI updates that need to happen after you login, you can call them in the Completed event.

For smaller cases that don't require a full BGW solution you can use the Task Parallel Library as it allows the simpler cases to remain simple. Here is the standard model for using the TPL:

private void button1_Click(object sender, EventArgs e)
{
    //UpdateUI with stuff to do before long running task

    Task.Factory.StartNew(() => someLongRunningNonUITask())
        .ContinueWith(task => updateUIWithResults(task)
           , TaskScheduler.FromCurrentSynchronizationContext());

}
Servy
  • 202,030
  • 26
  • 332
  • 449
  • A simple `Action` and `Dispatcher.BeginInvoke(Action act)` should do it in less code. I use that on my SpashScreen as it is easier for me to read. But for long operations like unzipping and waiting, a BackgroundWorker is more appropriate. – Cole Tobin Aug 16 '12 at 19:52
  • @ColeJohnson As a general rule it's bad practice to block the UI thread for an extended period of time. While you can occasionally get away with it (a splash screen is one of the few times where you really can) it should be avoided whenever possible. Now granted you don't *need* to use a BGW here, but you should do something to execute `login` in a background thread. – Servy Aug 16 '12 at 19:56
  • By waiting, I meant with `event`s. – Cole Tobin Aug 16 '12 at 19:58
2

The quick and dirty way to do this is to call Application.DoEvents() after you change the text and before you call the login() method. That will cause the label to re-paint before calling your login() method.

private void button_login_Click(object sender, EventArgs e) 
{ 
    label_status.Text = "Loading ..."; 
    Application.DoEvents();
    login(); 
} 
Michael Mankus
  • 4,628
  • 9
  • 37
  • 63
  • This doesn't always work. Plus it doesn't exist on WPF AFAIK. – Cole Tobin Aug 16 '12 at 20:21
  • He has stated that he is using WinForms. And and his case it would work. – Michael Mankus Aug 16 '12 at 20:22
  • The reason it looks like it was downvoted is because most n00bs will just copy paste this all over their code and wonder why it doesn't work half the time. – Cole Tobin Aug 16 '12 at 20:23
  • Oh I understand. Just was stating that in this particular case, using WinForms as he has stated, it is a workable solution for him. Plus I did say "quick and dirty." :-) – Michael Mankus Aug 16 '12 at 20:24
  • "Here is a big red button. It will solve this problem that you have, but if you keep pushing it eventually it will shoot you. But it's easy, so it's fine." I'd just as soon do it right, rather than the broken way that sometimes works. – Servy Aug 16 '12 at 20:35