Reputation: 637
I have something like this:
void ClickHandler() // Called from several places
{ // Try to prevent queuing accidental extra clicks during lengthy operation
GetMainWindow().IsEnabled = false; // "GetMainWindow()" you know what I mean
DoLengthyStuffInThisThread(); // Yes, I know I shouldnt
PresentResult();
GetMainWindow().IsEnabled = true;
}
That's it basically. To clarify though:
Even though I have set IsEnabled = false it does not have the desired effect, my extra clicks go through during the operation. (It does have an effect if I return without restoring the value.) I assume that normally my thread needs to return in order for the disabling to to have effect but I'd rather not create an extra thread.
Upvotes: 1
Views: 749
Reputation: 637
Thank you all for answering. The best answer was actually in one one of the *comments, by Will Eddins. Extra thanks!*
The uggly answer to my uggly question is: System.Windows.Forms.Application.DoEvents();
Not a pretty sight but this is what I had to do. Kids, do not try this at home!
public void WrapLengthyWork(Action lengthyWork)
{
SetWaiting(true);
System.Windows.Forms.Application.DoEvents();
lengthy();
SetWaiting(false);
}
public void SetWaiting(bool wait)
{
if (wait == true)
{
Mouse.OverrideCursor = Cursors.Wait;
Application.Current.MainWindow.IsEnabled = false;
}
else
{
IsEnabled = true;
Mouse.OverrideCursor = Cursors.Arrow;
}
}
Also, to all of you that suggested that I do it properly, with a thread switch: Thanks to you as well. I am (as I mentioned) painfully aware that the above snippet is poor coding style. My problem was that "LengthyWork()
" itself is riddled with stuff that references back to the GUI and must run in the GUI thread, such as:
while(stuffToDo)
{
Fetch();
Calculate();
UpdateGUI();
}
Given the imposed time constraints (couple of hours) and the limited task ("prevent clicks during processing and show wait-cursor and touch nothing else") the proper solution was unfortunately not an option.
Upvotes: 0
Reputation: 10896
@william-custode is right, you should do the heavy work off the main application thread. However, a work-around is forcing the window's message loop to consume all currently dispatched messages before beginning the "DoLengthyStuffInThisThread".
Upvotes: -1
Reputation: 4604
You have to offload the lengthy work to another thread. The UI isn't notified of this change (and hence, doesn't have a chance to refresh it's state with a layout pass) until after the enclosing method completes.
I would imagine whatever is happening inside of the lengthy method is manipulating some data that is displayed on the UI. If you are using data binding, this operation will populate the UI in the background (if its run on a background thread) and then when that operation completes, it can tell the UI to reenable itself.
This is semi-pseudo code, but check out Task.Factory.StartNew and Dispatcher.BeginInvoke.
public void ClickHandler()
{
MainWindow.IsEnabled = false;
Task.Factory.StartNew(() =>
{
// Length work goes here
}).ContinueWith((result) =>
{
Dispatcher.BeginInvoke(() =>
{
MainWindow.IsEnabled = true;
});
});
}
Upvotes: 2