locked
Forcing Update of UI before my function exits RRS feed

  • Question

  • I know this question has been asked before, but none of the answers given work for me.

    I have a function that performs a long operation. Before that function does all its work, I want to set a Status message in my Status bar. The problem is that UI updates don't seem to occur until the function has exited, by which time the Status Message is no longer relevant.

    What can I do to force the UI to update? I've tried calling UpdateLayout(). I've tried calling InvalidateRender(); I've tried setting the Status Bar properties within a delegate passed to Dispatcher.Invoke with DispatcherPriority.Render. None of these work.

    I have, elsewhere in my App succesfully used Background worker to perform longer running operations asynchronusly. I just don't want to go to that hassle in this case.

    The only other thing that might be relevant to this question is that the actual update of the UI will be done by Databinding. I.e. I set the value of a StatusMessage property, and this is bound to a property in my Status Bar widget. Could that be making a difference. I.e. if I set properties directly, rather than relying on DataBinding are they more likely to update straight-away?

    Thanks,

    Sam

    Thursday, July 20, 2006 1:04 PM

Answers

  • An even easier method has just occurred to me: just call this method after setting UI properties:

    void AllowUIToUpdate() {

    DispatcherFrame frame = new DispatcherFrame();

    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)

    {

    frame.Continue = false;

    return null;

    }), null);

    Dispatcher.PushFrame(frame);

    }

    If my understanding of DispatcherPriority is correct, this will allow all render level messages to be processed, and will then return.

    This feels a little like a WPF equivalent of Application.DoEvents, and I know their are dangers with that. Anybody with a deeper understanding of WPF threading got any thoughts on this?

    Wednesday, August 2, 2006 11:43 AM
  • It seems that there is a way to do this, slightly more tricky than before, but possibly more reliable. The secrect is in Dispatcher.PushFrame(...). (Note that I have only done basic testing on this idea.)

    The solution is to do something like this.

    At the call site (where the long operation starts from)

    ....

    StartLongOperation("Description", delegate() { MyLongOperation(); })

    ...

     Then StartLongOperation is implemented like this:

    void StartLongOperation(string description, NoArgsDelegate operation)

    {

    // set properties, etc, to update UI Status:

    StatusMessage = description

    // start a new Dispatcher Frame, in order to allow the UI to update before the long running operation takes place.

    DispatcherFrame frame = new DispatcherFrame();

    // chose the DispatcherPriority carefully: for example, using DispatcherPriority.Send won't work because our operation

    // would then complete before any UI updates

    DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, operation);

    dispatcherOperation.Completed += delegate(object sender, EventArgs e)

    {

    // tell the nested message loop to exit when the Long Operation has completed.

    frame.Continue = false;

    };

    // enter a nested message loop that will allow UI updates to happen, and then run our operation before returning

    Dispatcher.PushFrame(frame);

    }

    This avoids the hassles of Background threads, because the operation still happens on the UI Thread.

    For more information on this see the section on Nested Pumping in the article on the WPF Threading Model (http://windowssdk.msdn.microsoft.com/en-us/library/ms741870.aspx).

    Wednesday, August 2, 2006 10:00 AM
  • You need to update your UI using the same approach as Windows Forms. In WPF you do this using the Dispatcher API. You can basically do something like this in your long running function:

    this.statusBarItem.Dispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate(object param) { this.statusBarItem.Text = "...."; return null; }, null);

    The key of course is that your long running function cannot be on the UI thread itself. You need to queue the work up on another thread using the ThreadPool or by manually creating your own threads.

    HTH,
    Drew

    Thursday, July 20, 2006 11:59 PM

All replies

  • You need to update your UI using the same approach as Windows Forms. In WPF you do this using the Dispatcher API. You can basically do something like this in your long running function:

    this.statusBarItem.Dispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate(object param) { this.statusBarItem.Text = "...."; return null; }, null);

    The key of course is that your long running function cannot be on the UI thread itself. You need to queue the work up on another thread using the ThreadPool or by manually creating your own threads.

    HTH,
    Drew

    Thursday, July 20, 2006 11:59 PM
  • Sam:

    Just want to affirm that Drew is correct.  If your function is not UI related then definately take some time to review threading in .Net and set it on its own.  There are a variety of threading paradigms you can use. I would suggest you start with BackgroundWorker as its the simplest in my opinion.

    If it is UI related nothing can be done since the UI thread is busy doing other things (your task) and won't render anything until it is done.
    Friday, July 21, 2006 1:07 AM
  • So what you're saying is that there is no way at all to force a UI update in the middle of a function call without that function call being on a different thread?

    Is that something Microsoft will ever consider, or is it just not possible?

    Friday, July 21, 2006 8:01 AM
  • If the function is part of the UI thread, I don't believe so. WPF will not render the window until the method has exited or you are doing a something like an animation.  The thread is running your function, it can't have the rug pulled out, switch to a paint operation, and then gracefully come back to your code. =(

    What is your long running operation? It should be fairly easy to seperate into another thread. Also are you sure its your code that takes a long time? I thought a chunk of my code was causing problems, but with some timers and debug prints I discovered it was the actual window rendering that was taking the time.

    Another solution is use the event system, basically modify the UI element and attach and event to it to run on a property change.  Design-wise it feels like a hack, but you could probably get it to work..
    Friday, July 21, 2006 4:48 PM
  •      Sam, you just cannot let the UI thread do two things simultaneously, you know, when the thread context is set up for the current running function, Its just impossible to switch it off when the current function is still running, UI thread can only do one thing at one time.

     

    Sheva

    Friday, July 21, 2006 9:59 PM
  • I understand about threading and doing work in a background thread: parts of my application use this technique so that I can have a progress bar updating whilst my operation is running. I know that one thread can't do two things at once. What I'm asking for is a method that will let my UI thread go off and do the work to tell the render thread to update my window, and then come back and let me carry on with my operation

    All I'm talking about is getting a simple text box in my status bar to update with a description of the task I'm running. Such a thing is possible in Windows Forms: for example

    Create a Form with a Button and a Label on it, and put this code on the Button Click event:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    Label1.Text = "Running Operation"

    Label1.Update()

    ' Simulate a long operation

    Threading.Thread.Sleep(1000)

    Label1.Text = "Completed Operation"

    End Sub

    When you press the button, you will see  the Label text first change to "Running Operation" and then to "Completed Operation". Without the  Label1.Update() call you would only see "Completed Operation". It is the equivalent of Label1.Update that I'm after. Is there something about the architecture of WPF that makes this completely impossible, or has this feature just not been provided for in version 1.

    Please don't misunderstand me. I love WPF, and I can live without this feature for the time being; its just that it would be very nice if I could find that one line solution (an Update() method) instead of having to use Background Threads.

    Thanks for you help.

    Saturday, July 22, 2006 10:15 AM
  • This is interesting, can you provide the XAML showing the binding for the Status Message? BTW there is no reason why changing the value of a UI element on the UI thread shouldn't work. The only time you should have to use dispatcher is when updating the ui from a thread other than the UI thread.
    Saturday, July 22, 2006 9:02 PM
  • I actually have a similar problem.

    This is the code.

    void Print()
    {
      this.btnPrint.IsEnabled = False;
      ...
      prtDialog.PrintVisual(this.docToSign);
    }

    Since PrintVisual will take a little bit time, the btnPrint button won't be changed to disabled until the the function quits.

    Chong
    Sunday, July 23, 2006 3:25 AM
  • This is a snippet of the XAML from my Window

    <StatusBar DockPanel.Dock="Bottom">

    <StatusBar.Style>

    <Style>

    <Style.Triggers>

    <DataTrigger Binding="{Binding ShowBusyIndicators}" Value="False">

    <Setter Property="UIElement.Visibility" Value="Collapsed"/>

    </DataTrigger>

    </Style.Triggers>

    </Style>

    </StatusBar.Style>

    <StatusBarItem>

    <TextBlock>

    <TextBlock Text="{Binding StatusMessage}"/>

    <Run Text="..."/>

    </TextBlock>

    </StatusBarItem>

    </StatusBar>

    The DataContext of the Window is set to a ViewModel class that contains the properties ShowBusyIndicators and StatusMessage. Basically, I set ShowBusyIndicators to true only when I am performing one of the long running operations, and also Status Message to say what operations is being performed. I have confirmed that these bindings work in other contexts: its just that the UI doesn't update until the end of whatever function I set those two properties in.

    Monday, July 24, 2006 8:34 AM
  • It seems that there is a way to do this, slightly more tricky than before, but possibly more reliable. The secrect is in Dispatcher.PushFrame(...). (Note that I have only done basic testing on this idea.)

    The solution is to do something like this.

    At the call site (where the long operation starts from)

    ....

    StartLongOperation("Description", delegate() { MyLongOperation(); })

    ...

     Then StartLongOperation is implemented like this:

    void StartLongOperation(string description, NoArgsDelegate operation)

    {

    // set properties, etc, to update UI Status:

    StatusMessage = description

    // start a new Dispatcher Frame, in order to allow the UI to update before the long running operation takes place.

    DispatcherFrame frame = new DispatcherFrame();

    // chose the DispatcherPriority carefully: for example, using DispatcherPriority.Send won't work because our operation

    // would then complete before any UI updates

    DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, operation);

    dispatcherOperation.Completed += delegate(object sender, EventArgs e)

    {

    // tell the nested message loop to exit when the Long Operation has completed.

    frame.Continue = false;

    };

    // enter a nested message loop that will allow UI updates to happen, and then run our operation before returning

    Dispatcher.PushFrame(frame);

    }

    This avoids the hassles of Background threads, because the operation still happens on the UI Thread.

    For more information on this see the section on Nested Pumping in the article on the WPF Threading Model (http://windowssdk.msdn.microsoft.com/en-us/library/ms741870.aspx).

    Wednesday, August 2, 2006 10:00 AM
  • An even easier method has just occurred to me: just call this method after setting UI properties:

    void AllowUIToUpdate() {

    DispatcherFrame frame = new DispatcherFrame();

    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)

    {

    frame.Continue = false;

    return null;

    }), null);

    Dispatcher.PushFrame(frame);

    }

    If my understanding of DispatcherPriority is correct, this will allow all render level messages to be processed, and will then return.

    This feels a little like a WPF equivalent of Application.DoEvents, and I know their are dangers with that. Anybody with a deeper understanding of WPF threading got any thoughts on this?

    Wednesday, August 2, 2006 11:43 AM
  • This is a very interesting and important topic.  If someone from the WPF team at Microsoft were to verify that the technique presented by Sam does or does not have any serious drawbacks, that would be great. :)  Is there an easier way to accomplish this common task?

    Thanks in advance.

    Wednesday, August 2, 2006 2:18 PM
  • If we just need to update an UI element before a long operation in the same thread. We can do something like this

    void btnPrint_Click(object sender, RoutedEventArgs e)

    {

    btnPrint.IsEnabled = false;

    lblWaiting.Text = "Please waiting ...";

    btnPrint.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, longOperation);

    }

    longOperation is the delegate pointed to the long operation function.

    Chong

    Wednesday, August 2, 2006 5:00 PM
  • I has a similiar problem...I was loading custom user controls that took a couple of seconds to load.  Because of this, I wanted to display a message in the status bar.  By loading the control through a delegate and by calling the dispatcher, I was able to achieve the desired result.

    Tuesday, April 1, 2008 3:02 PM
  • I am making a WPF Application in which two threads run simultaneously and update the user interface....

    These two threads read data from file and update the user interface . But the problem is that at one time only one thread updates the user interface and the other thread waits....I want that the two threads update the UI together....how to achieve this???

    awais
    • Edited by awais hamid Thursday, September 4, 2008 4:58 AM rewritten the problem
    Wednesday, September 3, 2008 11:18 AM
  • The documentation for Dispatcher.PushFrame() includes sample code for a DoEvents() method that does exactly what Sam's solution does (except Sam's is more elegant because it uses an anonymous delegate).  I would certainly consider that an endorsement of this technique.


    Eric Hill
    Wednesday, September 3, 2008 12:15 PM
  • I have tested Sam's code and found it to work, as well. Here's my implementation in VB.NET, which forced me to move things around a little, because I wasn't certain how to do that inline object syntax shown in Sam's code, so, instead of passing a null argument for BeginInvoke, I actually pass the frame instance over to a 'junk' method. Tested, and so far it works great:

    Public Sub AllowUIToUpdate()

    Dim frame As New DispatcherFrame()

    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, New DispatcherOperationCallback(AddressOf JunkMethod), frame)

    Dispatcher.PushFrame(frame)

    End Sub

    Private Function JunkMethod(ByVal arg As Object) As Object

    DirectCast(arg, DispatcherFrame).Continue = False
    Return Nothing

    End Function

    Thursday, September 11, 2008 1:29 PM
  • oy. i had a similar problem, i posted about it here: (http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/8d7e765f-abea-46d5-9b56-04d2075ac04f)

    i tried the PushFrame stuff and it still didn't bother to update the child control. it did however, allow controls already on the page to be updated. for instance, i updated a TextBlock control and the display updated with the PushFrame, but not without.
    Wednesday, November 26, 2008 2:44 AM
  • I agree with Eric in taking the usage of this technique in the Dispatcher.PushFrame documentation as tacit endorsement, but if this is the "recommended" approach, it's a shame it's buried in some example code for an obscure method.

    I second Josh's comment: "If someone from the WPF team at Microsoft were to verify that the technique presented by Sam does or does not have any serious drawbacks, that would be great. :)"

    Friday, May 29, 2009 11:44 PM
  • Any update on this from Microsoft?

     

    Samuel AllowUIToUpdate method seem to work fine. Any new approach recommended since May 2009??

    Friday, January 7, 2011 12:21 AM
  • I needed to use the same method to set the cursor to "Wait" for the lengthy operation. I did not really want to set every lengthy method on a BackgroundWorker. It is ok to see the GUI frozen but the cursor should be changed.

    If I just set the cursor in the code before the execution, it's somehow not 100% guaranteed that the cursor will refresh. With the AllowUIToUpdate way it seems to be more or less working. Eventually I would like to switch to the background worker, which sounds "more right" or "the way things should be done" to me.


    tridy (Stockholm, Sweden)
    Monday, February 14, 2011 12:41 PM
  • I needed to use the same method to set the cursor to "Wait" for the lengthy operation. I did not really want to set every lengthy method on a BackgroundWorker. It is ok to see the GUI frozen but the cursor should be changed.

    If I just set the cursor in the code before the execution, it's somehow not 100% guaranteed that the cursor will refresh. With the AllowUIToUpdate way it seems to be more or less working. Eventually I would like to switch to the background worker, which sounds "more right" or "the way things should be done" to me.


    tridy (Stockholm, Sweden)


    Hi Tridy, you might want to take a look at my WaitCursorHandler class.

    I Hope it helps you.

    Thursday, February 24, 2011 8:03 PM
  • If your code is running on the main dispatcher thread, and you want to get the application to update synchronously, you need to let the current dispatcher process pending messages, and then return to your function once all messages are processed. While the Dispatcher.PushFrame solutions suggested below should work, the simplest way is just to call a synchronous Dispatcher.Invoke with a low priority do-nothing task:

    public void DoEvents()
    {
     Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, (Action)(() => { }));
    }
    

    Monday, April 4, 2011 10:00 AM
  • I think the problem I am having is the same as described here, but the approaches discussed do not appear to work for me.

    My application generates and displays data (30+ pages) in a separate window.  Takes a while; so whilst doing this, I want the current window to play a simple animation.  My application is in VB.  The code is as follows:

    Call dispWaitMsg(myStoryBoard)

     

    Dim objPrintingBase As PrintingBase = New PrintingBase()

    objPrintingBase.CreateReport()

    On their own, both dispWaitMsg and CreateReport work very well.  However, dispWaitMsg does not execute until CreateReport has completed and the its Window has been closed down.

    

    Any comments please.

    

    Any help please.

    Friday, May 6, 2011 12:14 PM
  • I think the problem I am having is the same as described here, but the approaches discussed do not appear to work for me.

    My application generates and displays data (30+ pages) in a separate window.  Takes a while; so whilst doing this, I want the current window to play a simple animation.  My application is in VB.  The code is as follows:

    Call dispWaitMsg(myStoryBoard)

     

    Dim objPrintingBase As PrintingBase = New PrintingBase()

     

    objPrintingBase.CreateReport()


    I would send objPrintingBase to the BackgroundWorker and then return it as a result after the method completes. You can declare a boolean variable isBackgroundWorkerBusy and set it to true in the beginning and start the animation. Then make the BackgroundWorker set the boolean to false when it completes. The GUI thread can check the value every 2 seconds or so and when it gets to false, remove the animation. That's how we have done it so far in our project.
    tridy (Stockholm, Sweden)
    Saturday, June 11, 2011 4:31 PM
  • One workaround I have used is to add a DispatcherTimer

    Start the timer before your operation starts, and end it when its finished

    Then on each tick, refresh the UI

    Dim t As New DispatcherTimer With {.Interval = TimeSpan.FromMilliseconds(My.Settings.AddToCartRetry)}
            AddHandler t.Tick, Sub()
                                   RefreshUI()
                               End Sub
            t.Start()
    
            'Run Long Operation
    
            t.stop()


    Tuesday, November 18, 2014 2:00 PM
  • Magic!
    Monday, December 14, 2015 8:51 AM