I want to do something that sounds extremely simple, yet I find it very hard to achieve.
Let's assume I have some content which is bound to a slow-loading operation. For example, an observable list which is retrieved from a local SQL and takes a few seconds. While this is happening, I want to overlay the content presenter (e.g. a Groupbox
) with a "Loading ..." text or any other 'please wait' type of content.
I quickly came to the conclusion that simply switching a boolean flag bound to the UI, before and after the operation doesn't work. The UI does not get refreshed until the entire operation completes. Maybe because the operation is CPU-intensive, I don't know.
I am now looking into Adorner
s, but very little information comes up with I search for it in the context of a 'busy indicator' overlay. There are just a few solutions on the Internet, from about 5 years ago and I can't get any of them to work.
The question:
As simple as it sounds - how to temporarily show something on the screen, while the View Model is working to update the bound data?
I quickly came to the conclusion that simply switching a boolean flag bound to the UI, before and after the operation doesn't work. The UI does not get refreshed until the entire operation completes. Maybe because the operation is CPU-intensive, I don't know.
Yes, it should work provided that you are actually executing the long-running operation on a background thread.
Please refer to the following simple example.
View:
<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="Window1" Height="300" Width="300">
<Window.DataContext>
<local:Window1ViewModel />
</Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid>
<TextBlock>Content...</TextBlock>
<Grid Background="Yellow" Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">Loading...</TextBlock>
</Grid>
</Grid>
</Window>
View Model:
public class Window1ViewModel : INotifyPropertyChanged
{
public Window1ViewModel()
{
IsLoading = true;
//call the long running method on a background thread...
Task.Run(() => LongRunningMethod())
.ContinueWith(task =>
{
//and set the IsLoading property back to false back on the UI thread once the task has finished
IsLoading = false;
}, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
public void LongRunningMethod()
{
System.Threading.Thread.Sleep(5000);
}
private bool _isLoading;
public bool IsLoading
{
get { return _isLoading; }
set { _isLoading = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}