Reputation: 101
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
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
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
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
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