1

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.

Mike G
  • 4,232
  • 9
  • 40
  • 66
Adam
  • 637
  • 1
  • 8
  • 21
  • 2
    Since you are processing on the UI thread the UI does not get updated until the ClickHandler is complete. At the completion the value is true. – paparazzo Jul 08 '13 at 18:20
  • 2
    It will be *less* work for you, in the long run, to do it properly. Trying to save time by doing it wrong is like walking to the other side of town because getting into your car is too much work. – Servy Jul 08 '13 at 18:21
  • @Servy - to say nothing of the coworker later who curses your name for doing it the "quick and dirty" way. – Adam V Jul 08 '13 at 18:25
  • Servy, yes and in the long run I hope they will leave me room for that rewrite (See my lengthier answer). But this had to be fixed on the spot. – Adam Jul 09 '13 at 15:06
  • Adam, your answer is perfectly reasonable. But in fact, that would depend on the what the code looked liked when it fell in my lap. And it would also depend on the general standard of coding within the company. – Adam Jul 09 '13 at 15:08

3 Answers3

2

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;
                        });
                });
    }
Will Custode
  • 4,576
  • 3
  • 26
  • 51
  • 1
    Well, the UI *is* changed right when he makes the change, the problem is that nothing *else* can be done until the method finishes; that "else" includes things like redrawing the relevant sections of the screen, responding to click/keyboard events, etc. Minor wording change, although the end result is still essentially correct. – Servy Jul 08 '13 at 18:23
  • Thanks William. That would have been nice but the powers that be have left us stuck with VS2008. So No fancy.NET 4 stuff. But the thread thing fell through anyway as seen in my own answer... – Adam Jul 09 '13 at 15:03
0

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.

Adam
  • 637
  • 1
  • 8
  • 21
  • 1
    So here I am. Forced to an unfortunate solution under unfortunate circumstances. I apologize for the ugliness of it all. And still get kicked for it. Without comments. – Adam Jul 09 '13 at 15:48
  • Thanks for posting this, even though it's downvoted so much, sometimes it's necessary to get things to happen when you want them, especially when working with code you don't have full control over. Just because they haven't run across a situation in which they need it, doesn't mean it should "NEVER" be used lol. – Josh Sep 25 '14 at 20:17
  • This works wonders when you are working items that you don't have as much control as everyone here seems to think everyone else has over the code. So many don't realize sometimes there are things that must be done the "ugly" way because there is literally no other way to do it with what you are working with. This DoEvents method has been a lifesaver a time or two. – Josh Oct 02 '14 at 21:43
-1

@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".

Community
  • 1
  • 1
Ani
  • 10,826
  • 3
  • 27
  • 46