wpfuser-interfacemvvm

How to force a UI update during a lengthy task on the UI thread


I have a window, and the window contains it's content and a "Loading" overlay as follows

<Grid>
    <Grid>
        <!-- STUFF -->
    </Grid>
    <Rectangle Fill="White" Opacity="0.7" Visibility="{Binding VisibilityWait, Mode=TwoWay, 
    Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>

My ViewModel then implements INotifyPropertyChanged and includes the property;

    private bool visibilityWait;
    public bool VisibilityWait
    {
        get
        {
            return visibilityWait;
        }
        set
        {
            visibilityWait = value;
            OnPropertyChanged("VisibilityWait");
        }
    }

I know that this is set up correctly because if I set the VisibilityWait to true in the constructor, the window displays with the "Loading" overlay as expected and vice versa... However, if i try to do this during a method e.g.;

private void someMethod()
{
    VisibilityWait = true;
    //... Do things
    VisibilityWait = false;
}

Then during the time that the program is in the "do things" section, the UI does not update to show the loading overlay.

How do I solve this issue?

EDIT: I found my own solution to this problem. See the answer below.


Solution

  • Usually when a function is taking place, any updates to the UI are blocked until the end of the function. This is because the frame does not get pushed until the end of the function. You can force this update by calling a method like this;

        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);
            //EDIT:
            Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
                                          new Action(delegate { }));
        }
    

    EDIT: I added in an extra line to the AllowUIToUpdate function, and now everything functions as expected!