afpinto
afpinto

Reputation: 101

WPF C# lock/unlock button and execute a thread

Can somebody help me understand why this piece of code don't work?

What I want to do is : Click the button, change the button content and lock it, execute a external thread and finally, unlock the button and change the content again

private void button3_Click(object sender, RoutedEventArgs e)
{
    button3.Content = "Printing...";
    button3.IsEnabled = false;

    Thread.Sleep(1000);

    button3.IsEnabled = true;
    button3.Content = "Print";
}

Upvotes: 2

Views: 5946

Answers (4)

majocha
majocha

Reputation: 1191

Making the handler asynchronous takes care of the problem:

private async void button3_Click(object sender, RoutedEventArgs e)
{
    button3.Content = "Printing...";
    button3.IsEnabled = false;

    await Task.Delay(1000);

    button3.IsEnabled = true;
    button3.Content = "Print";
}

Upvotes: 0

Rohit Vats
Rohit Vats

Reputation: 81323

You are sleeping on UI thread which freezes your UI thread and hence you are not seeing any update on UI.

Redrawing of controls is done by UI dispatcher but dispatcher performs the operation based on priority set for the task. Redrawing of controls are done on dispatcher priority set to Render. Setting content of button is queued on dispatcher with priority render but it will get execute only once all tasks with higher priority gets completed.

As other's have suggested you should move your long running task on separate thread but there's another workaroud to refresh GUI before sleeping on UI thread. Like i mentioned UI will be redrawn once all tasks higher than DispatcherPriority.Render gets completed. So, what you can do is before sleeping on thread enqueue an empty delegate with render priority synchronously which will force dispatcher to perform all tasks above and with priority render before moving to next task. This is what i meant -

private void button3_Click(object sender, RoutedEventArgs e)
{
    button3.Content = "Printing...";
    button3.IsEnabled = false;

    Dispatcher.Invoke((Action)(() => { }), DispatcherPriority.Render); <-- HERE
    // button3.Refresh(); <-- After having extension method on UIElement.
    Thread.Sleep(1000);

    button3.IsEnabled = true;
    button3.Content = "Print";
}

Also you can have this method as extension method on UIElement -

public static void Refresh(this UIElement uiElement)
{
    uiElement.Dispatcher.Invoke((Action)(() => { }), DispatcherPriority.Render);
}

And call simply button3.Refresh() before sleeping on UI thread.

But, this has a downside too since it won't only refresh your button3 but all other controls which are pending for refresh on UI because dispatcher will complete all tasks with priority render or higher before moving to next task.

But keep in mind always, never sleep on UI thread.

Upvotes: 2

Dan Puzey
Dan Puzey

Reputation: 34218

The reason you're seeing this behaviour is because your method (which handles the button's Click event) is running on the same thread as the UI. This concept is a common source of problems when working with UI code.

Put simply, the UI cannot be re-drawn until your method has completed. During the method you disable your button, wait for 1 second, then re-enable the button - and then the UI is redrawn, with the button enabled. For this reason, you never see the button disabled on-screen (and your application will be freezing up for 1 second while the Sleep happens).

What you need to do instead is investigate one of the methods available for running a task on a background thread. Your method should create and start this thread, set the button to disabled, and then complete - allowing the UI thread to draw the button as disabled. When your task is complete you should set the button to enabled again (note that typically in WPF this will mean using Dispatcher.Invoke to ensure that button.Enabled is set from the UI thread).

To create your background task, you could look at the System.Threading namepaces (specifically the Thread.Start method), or the Task Parallel Library, or the BackgroundWorker class. I suspect the TPL will be your easiest starting point.

Upvotes: 2

Rashedul.Rubel
Rashedul.Rubel

Reputation: 3584

you are disabling the button when it is clicked but not enabling again. what can you do is,

1. initiate a timer

when button is clicked then,
1. change content, disable the button
2. start the timer
3. after a certain duration when the external thread is executed enable the button again and change it's content.

Hope this helps Thanks.

Upvotes: -2

Related Questions