TonySutt2
asked on
WPF UI Not Updating
Given a WPF form with a Button (Button1) set to default and a TextBox (TextBox1)
I would like to disable the button as soon as it is clicked, and then execute some long running code.
Normal WPF behaviour would not refresh the disabled button straight away, rather the interface will wait for the code in the button click handler to finish executing and then disable the button for a split second and re-enable it.
Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEvent Args) Handles Button1.Click
‘disable the button
Button1.IsEnabled = False
‘Now perform a long running code
System.Threading.Thread.Sl eep(5000)
‘re-enable the button
Button1.IsEnabled = True
End Sub
Of all the solutions I have seen posted on the internet, majority of them involve forcing the Dispatcher to update the UI before running the long running code. This works for most scenarios, however if the focus is on the TextBox, and the button click event is executed by clicking the ‘Enter’ button on your keyboard (Button is default button on form), the solution mentioned earlier would fail almost all of the time.
Does anyone out there have a solution to this problem where if I was to execute the button click event by pressing Enter on keyboard whilst the focus was on the TextBox, the button would disable straight away before executing the sleep?
I would like to disable the button as soon as it is clicked, and then execute some long running code.
Normal WPF behaviour would not refresh the disabled button straight away, rather the interface will wait for the code in the button click handler to finish executing and then disable the button for a split second and re-enable it.
Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEvent
‘disable the button
Button1.IsEnabled = False
‘Now perform a long running code
System.Threading.Thread.Sl
‘re-enable the button
Button1.IsEnabled = True
End Sub
Of all the solutions I have seen posted on the internet, majority of them involve forcing the Dispatcher to update the UI before running the long running code. This works for most scenarios, however if the focus is on the TextBox, and the button click event is executed by clicking the ‘Enter’ button on your keyboard (Button is default button on form), the solution mentioned earlier would fail almost all of the time.
Does anyone out there have a solution to this problem where if I was to execute the button click event by pressing Enter on keyboard whilst the focus was on the TextBox, the button would disable straight away before executing the sleep?
ASKER
No the long running code is not on its own thread and is probably too long to paste in here.
However the code in the question is attempting to simulate the long running code with the system.threading.thread.sl eep(5000) which I believe if we could get that working then it would solve my issue as well.
However the code in the question is attempting to simulate the long running code with the system.threading.thread.sl
Hi TonySutt2;
Here is a solution using a BackgroundWorker component to run the long running code in the button click event.
Fernando
Here is a solution using a BackgroundWorker component to run the long running code in the button click event.
Imports System.Threading
Imports System.Windows.Threading
Imports System.ComponentModel
Class MainWindow
' BackgroundWorker Component
Private bgWorker As BackgroundWorker
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
' Create the background worker
bgWorker = New BackgroundWorker()
' Add Events
AddHandler bgWorker.DoWork, AddressOf bgWorker_DoWork
AddHandler bgWorker.RunWorkerCompleted, AddressOf bgWorker_RunWorkerCompleted
' Start the background worker thread
bgWorker.RunWorkerAsync()
' Disable the button so user can not click it again until
' long running process is done
Button1.IsEnabled = False
End Sub
Private Sub bgWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
' This code runs in a different thread
' Now perform a long running code which is currently in your button event
Thread.Sleep(5000)
End Sub
Private Sub bgWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' Long running process is completed re-enable the button so that it can be clicked again
' re-enable the button
Button1.IsEnabled = True
' Remove the Events
RemoveHandler bgWorker.DoWork, AddressOf bgWorker_DoWork
RemoveHandler bgWorker.RunWorkerCompleted, AddressOf bgWorker_RunWorkerCompleted
' Clean up
bgWorker.Dispose()
bgWorker = Nothing
End Sub
End Class
Fernando
ASKER
I would like to keep the long running code in the main thread as it needs access to a data context created on the main thread.
A background worker would not allow access to this unless you detach and attach the data context but that would be a long messy solution just to disable a button. I wonder if you have any thoughts on this?
A background worker would not allow access to this unless you detach and attach the data context but that would be a long messy solution just to disable a button. I wonder if you have any thoughts on this?
Hi TonySutt2;
To your statement, "I would like to keep the long running code in the main thread as it needs access to a data context created on the main thread.", Data stored in a class is accessible from another thread. The only issue is modifying the control created by another thread and the BackgroundWorker component takes care of that if the updates to the forms controls are modified from the BackgroundWork ReportProgress event handler.
To your statement, "A background worker would not allow access to this unless you detach and attach the data context but that would be a long messy solution just to disable a button. I wonder if you have any thoughts on this?", The entities will not need to be detached and the re-attached. As I stated in the above the data of the class will be available to the thread.
Fernando
To your statement, "I would like to keep the long running code in the main thread as it needs access to a data context created on the main thread.", Data stored in a class is accessible from another thread. The only issue is modifying the control created by another thread and the BackgroundWorker component takes care of that if the updates to the forms controls are modified from the BackgroundWork ReportProgress event handler.
To your statement, "A background worker would not allow access to this unless you detach and attach the data context but that would be a long messy solution just to disable a button. I wonder if you have any thoughts on this?", The entities will not need to be detached and the re-attached. As I stated in the above the data of the class will be available to the thread.
Fernando
ASKER
Hi
Thanks for your quick response.
The data context that I am using is a linq data context and the only concern I have about accessing this from a different thread is taken from a Microsoft statement below:
"Any public static (Shared in Visual Basic) members of this type are thread safe. any instance members are not guaranteed to be thread safe."
The above statement can be found at http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx
Thanks for your quick response.
The data context that I am using is a linq data context and the only concern I have about accessing this from a different thread is taken from a Microsoft statement below:
"Any public static (Shared in Visual Basic) members of this type are thread safe. any instance members are not guaranteed to be thread safe."
The above statement can be found at http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx
Hi TonySutt2;
What does it mean to be thread safe, "A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads.".
Unless you have more then one thread accessing the same data simultaneously you do not have a problem.
Basic Instincts Thread Synchronization:
http://msdn.microsoft.com/en-us/magazine/cc163929.aspx
Fernando
What does it mean to be thread safe, "A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads.".
Unless you have more then one thread accessing the same data simultaneously you do not have a problem.
Basic Instincts Thread Synchronization:
http://msdn.microsoft.com/en-us/magazine/cc163929.aspx
Fernando
ASKER
Hi Fernando
Appreciate your speedy responses.
Have just tried to implement your code into the actual project but the BackgroundWorker thread method is opening up a can of worms in that the "long running routine" interacts with label controls which in turn need to refesh (render) immediately whilst the long routine is running. So there becomes the issue of manipulating a control that was created on a different thread and nested BackgroundWorker threads to force other controls to refresh (render).
Is there any other way to force these controls to refresh (render) immediately without resorting to the BackgroundWorker method?
Tony
Appreciate your speedy responses.
Have just tried to implement your code into the actual project but the BackgroundWorker thread method is opening up a can of worms in that the "long running routine" interacts with label controls which in turn need to refesh (render) immediately whilst the long routine is running. So there becomes the issue of manipulating a control that was created on a different thread and nested BackgroundWorker threads to force other controls to refresh (render).
Is there any other way to force these controls to refresh (render) immediately without resorting to the BackgroundWorker method?
Tony
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hi Fernando
Your example code does work perfectly. Just before I award you the points do you know of any other way to achieve this as it will be a long hard slog to incorporate that method into our existing project.
Tony
Your example code does work perfectly. Just before I award you the points do you know of any other way to achieve this as it will be a long hard slog to incorporate that method into our existing project.
Tony
Hi Tony;
You state the following, "Your example code does work perfectly.", can you be more specific?
As you know WPF has not implemented the DoEvents function. Therefore in order to keep the GUI responsive you need to implement another thread and off load the long running code there.so that the GUI can respond to events.This is because the GUI is a sequentially processing single thread and so when it is in the button click event nothing else is being processing from the message pump but only the code in the button click event.
Fernando
You state the following, "Your example code does work perfectly.", can you be more specific?
As you know WPF has not implemented the DoEvents function. Therefore in order to keep the GUI responsive you need to implement another thread and off load the long running code there.so that the GUI can respond to events.This is because the GUI is a sequentially processing single thread and so when it is in the button click event nothing else is being processing from the message pump but only the code in the button click event.
Fernando
Can you show the code that you are using?