Ask a questionAsk a question
 

AnswerForcing Update of UI before my function exits

  • Thursday, July 20, 2006 1:04 PMSamuel Jack Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

Answers

  • Wednesday, August 02, 2006 11:43 AMSamuel Jack Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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?

  • Thursday, July 20, 2006 11:59 PMDrew MarshModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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

  • Wednesday, August 02, 2006 10:00 AMSamuel Jack Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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).

All Replies

  • Thursday, July 20, 2006 11:59 PMDrew MarshModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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

  • Friday, July 21, 2006 1:07 AMwadnerk Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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 8:01 AMSamuel Jack Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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 4:48 PMwadnerk Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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 9:59 PMZhou Yong Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

         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

  • Saturday, July 22, 2006 10:15 AMSamuel Jack Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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 9:02 PMBrownie PointsMVP, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Sunday, July 23, 2006 3:25 AMchongqing Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Monday, July 24, 2006 8:34 AMSamuel Jack Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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.

  • Wednesday, August 02, 2006 10:00 AMSamuel Jack Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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 02, 2006 11:43 AMSamuel Jack Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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 02, 2006 2:18 PMJosh SmithMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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 02, 2006 5:00 PMchongqing Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Tuesday, April 01, 2008 3:02 PMJeremy114_LUA Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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.

  • Wednesday, September 03, 2008 11:18 AMawais hamid Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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 byawais hamid Thursday, September 04, 2008 4:58 AMrewritten the problem
    •  
  • Wednesday, September 03, 2008 12:15 PMsmall_mountain_0705 Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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
  • Thursday, September 11, 2008 1:29 PMdeeshubby Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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

  • Wednesday, November 26, 2008 2:44 AMbryanc Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Friday, May 29, 2009 11:44 PMmatte303 Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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. :)"