1

I want to disable a button as soon as a user clicks it to stop them clicking it again. There are a number of checks that are performed when they click it, and it appears these checks are done before the UI change takes place.

I am trying to do this by using a separate thread for the button, but it still seems to only update after the checks are done.

Here's the code I am using:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Thread t = new Thread(new ThreadStart(
     delegate
     {
         Action action = () => btnStart.IsEnabled = false;
         Dispatcher.BeginInvoke(action);
     }
     ));
     t.Start();          
    // Run the main routine;
    BeginBootstrapping();
}   

How can I disable the button straight away?

KeyvanSFX
  • 113
  • 1
  • 11
Ben
  • 4,281
  • 8
  • 62
  • 103
  • First disable button as usual, then run _the rest_ on background thread. No need to disable button on background thread (it won't work even). – Evk May 12 '16 at 11:39
  • 1
    Does this answer your question? [How to run and interact with an async Task from a WPF gui](https://stackoverflow.com/questions/27089263/how-to-run-and-interact-with-an-async-task-from-a-wpf-gui) – Bizhan Apr 08 '21 at 13:39

2 Answers2

6

You may write an async Click handler that uses a Task to run some background work:

using System.Threading.Tasks;
...

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var button = (Button)sender;
    button.IsEnabled = false;

    await Task.Run(() => DoTheBootstrapping());

    button.IsEnabled = true;
}

Of course if you can make the long running method itself awaitable like

private async Task DoTheBootstrapping()
{
    var httpClient = new HttpClient();
    var content = await httpClient.GetStringAsync("http://stackoverflow.com");
}

you can call it without Task.Run like

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var button = (Button)sender;
    button.IsEnabled = false;

    await DoTheBootstrapping();

    button.IsEnabled = true;
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • If do like this - better make DoTheBootstrapping async itself instead of using Task.Run. – Evk May 12 '16 at 12:17
  • @Evk Sure, if possible. What if there isn't any awaitable call in DoTheBootstrapping? The code sample is also meant to show an alternative to manually creating a Thread. – Clemens May 12 '16 at 12:18
  • Well you are right, I should have said rather that there is such option, not that it should always be done like that. However if there is awaitable calls - better make it async. – Evk May 12 '16 at 12:22
0
private void Button_Click(object sender, RoutedEventArgs e)
{
    btnStart.IsEnabled = false
    Thread t = new Thread(new ThreadStart(
    delegate
    {
        // Run the main routine;
        BeginBootstrapping();
    }));

    t.Start();
}

Like Evk said in his comment, this disables the button right away from the UI thread main thread), then runs the rest of what has to be done on the other thread. Note I'm not using Dispatcher, because Dispatcher is actually the UI thread aswell, and using that will freeze the UI.

  • Just to note you only need the new thread if `BeginBootStrapping()` doesn't create one anyway (otherwise it's a bit wasteful). It may be worth considering `BackgroundWorker` instead of raw threads as it'll marshall progress and completion callbacks back onto the UI thread correctly, assuming you need those of course. – Paolo May 12 '16 at 11:55