Check out my comprehensive research of this very delicate topic. If there's nothing you can do to improve the actual performance, you have the following options to display a waiting message:
Option #1 Execute a code to display a waiting message synchronously in the same method which does the real task. Just put this line before a lengthy process:
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => { /* Your code to display a waiting message */ }));
It'll process pending messages on the main dispatcher thread at the end of the Invoke().
Note: Reason for selecting Application.Current.Dispatcher but Dispatcher.CurrentDispatcher is explained here.
Option #2 Display a “Wait” screen and update UI (process pending messages).
To do it WinForms developers executed Application.DoEvents method. WPF offers two alternatives to achieve similar results:
Option #2.1 With using DispatcherFrame class.
Check a bit bulky example from MSDN:
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
Option #2.2 Invoke an empty Action
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, (Action)(() => { }));
See discussions which one (2.1 or 2.2) is better here. IMHO option #1 is still better than #2.
Option #3 Display a waiting message in a separate window.
It comes in handy when you display not a simple waiting message, but an animation. Rendering a loading animation at the same time that we are waiting for another long rendering operation to complete is a problem. Basically, we need two rendering threads. You can't have multiple rendering threads in a single window, but you can put your loading animation in a new window with its own rendering thread and make it look like it's not a separate window.
Download WpfLoadingOverlay.zip from this github (it was a sample from article "WPF Responsiveness: Asynchronous Loading Animations During Rendering", but I can't find it on the Web anymore) or have a look at the main idea below:
public partial class LoadingOverlayWindow : Window
{
/// <summary>
/// Launches a loading window in its own UI thread and positions it over <c>overlayedElement</c>.
/// </summary>
/// <param name="overlayedElement"> An element for overlaying by the waiting form/message </param>
/// <returns> A reference to the created window </returns>
public static LoadingOverlayWindow CreateAsync(FrameworkElement overlayedElement)
{
// Get the coordinates where the loading overlay should be shown
var locationFromScreen = overlayedElement.PointToScreen(new Point(0, 0));
// Launch window in its own thread with a specific size and position
var windowThread = new Thread(() =>
{
var window = new LoadingOverlayWindow
{
Left = locationFromScreen.X,
Top = locationFromScreen.Y,
Width = overlayedElement.ActualWidth,
Height = overlayedElement.ActualHeight
};
window.Show();
window.Closed += window.OnWindowClosed;
Dispatcher.Run();
});
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.Start();
// Wait until the new thread has created the window
while (windowLauncher.Window == null) {}
// The window has been created, so return a reference to it
return windowLauncher.Window;
}
public LoadingOverlayWindow()
{
InitializeComponent();
}
private void OnWindowClosed(object sender, EventArgs args)
{
Dispatcher.InvokeShutdown();
}
}