Example T868679
Visible to All Users

WPF Splash Screen - Show a Cancelable Wait Indicator

This example illustrates how to start a complex operation in a background thread and display its progress and status (Loading, Finishing, etc.) in the Splash Screen. This Splash Screen also contains the Close button that allows users to cancel the operation and close the Splash Screen.

image

Use SplashScreenManagerService to operate with this manager in an MVVM way.

Implementation Details

Add the SplashScreenManagerService to the MainView. Specify the required Splash Screen UI in the SplashScreenView and assign this view to the service's ViewTemplate:

XAML
<dxmvvm:Interaction.Behaviors> <dxmvvm:DispatcherService/> <dx:SplashScreenManagerService OwnerLockMode="WindowInputOnly" StartupLocation="CenterOwner"> <dx:SplashScreenManagerService.ViewTemplate> <DataTemplate> <Views:SplashScreenView /> </DataTemplate> </dx:SplashScreenManagerService.ViewTemplate> <dx:SplashScreenManagerService.SplashScreenWindowStyle> <Style TargetType="dx:SplashScreenWindow"> <Setter Property="AllowAcrylic" Value="True" /> <Setter Property="AllowsTransparency" Value="True" /> <Setter Property="Background" Value="#B887A685" /> </Style> </dx:SplashScreenManagerService.SplashScreenWindowStyle> </dx:SplashScreenManagerService> </dxmvvm:Interaction.Behaviors>

When the Splash Screen is shown, this SplashScreenView's DataContext contains an instance of the DXSplashScreenViewModel (or its descendant) class. You can bind visual elements of the SplashScreenView to the Logo, Title, Progress, and Status properties from this class. When you change these settings in the SplashScreenManagerService.ViewModel object, SplashScreenView's elements reflect these changes.

The executed complex operation does not allow you to update the view model that is created in the main thread. To avoid this, create a DispatcherService that can update the Splash Screen's view model properties.

The main view model is a ViewModelBase class descendant. Use the approach from the Services in ViewModelBase descendants article to get access to the view services:

C#
public ISplashScreenManagerService SplashScreenManagerService { get { return this.GetService<ISplashScreenManagerService>(); } } public IDispatcherService DispatcherService { get { return this.GetService<IDispatcherService>(); } }

NOTE
Refer to the following help topics if you use other view model types:

- Services in Generated View Models
- Services in Custom ViewModels

The BackgroundWorker class allows you to execute a complex operation in a background thread. Set the WorkerSupportsCancellation property to true to cancel the operation on demand:

C#
BackgroundWorker worker; void RunBackgroundWorker() { worker = new BackgroundWorker(); worker.DoWork += Worker_DoWork; worker.WorkerSupportsCancellation = true; worker.RunWorkerAsync(); } private void Worker_DoWork(object sender, DoWorkEventArgs e) { int i = -1; while(++i < 100) { if(worker.CancellationPending) { e.Cancel = true; break; } UpdateSplashScreenContent(i); Thread.Sleep(200); } this.DispatcherService.Invoke(() => { this.SplashScreenManagerService.Close(); worker.DoWork -= Worker_DoWork; worker = null; }); }

When a user clicks the "Start a complex operation" button, initialize properties in the SplashScreenManagerService.ViewModel and show the Splash Screen:

C#
[Command(CanExecuteMethodName = "CanStart")] public void Start() { if(this.SplashScreenManagerService != null) { this.SplashScreenManagerService.ViewModel = new DXSplashScreenViewModel(); this.InitSplashScreenViewModel(this.SplashScreenManagerService.ViewModel); this.SplashScreenManagerService.Show(); this.RunBackgroundWorker(); } }

In the InitSplashScreenViewModel define the Title, SubTitle, Progress, and other settings. Set the Tag property in the Splash Screen's view model to DelegateCommand that calls the CancelOperation method from the main view model:

C#
void InitSplashScreenViewModel(DXSplashScreenViewModel vm) { vm.Title = "SOME BACKGROUND WORK"; vm.SubTitle = "This can take some time"; vm.Logo = new Uri("pack://application:,,,/logo.png"); vm.IsIndeterminate = false; vm.Tag = new DelegateCommand(CancelOperation, CanCancelOperation); } public bool CanCancelOperation() { return worker != null && worker.IsBusy; } public void CancelOperation() { if(worker != null && worker.IsBusy) worker.CancelAsync(); }

In the Splash Screen's view, bind the close button's Command property to the View Model's Tag property:

XAML
... <dx:SimpleButton Margin="20" HorizontalAlignment="Right" VerticalAlignment="Top" Command="{Binding Tag}" Glyph="{dx:DXImage GrayScaleImages/Edit/Delete_16x16.png}" ToolTip="Cancel and Close" /> ...

To update the Splash Screen during a complex operation, set the Progress and State properties in the SplashScreenManagerService.ViewModel object to the required values. To do this in the main thread, use the DispatcherService's Invoke method:

C#
void UpdateSplashScreenContent(int progressValue) { var state = string.Empty; state = progressValue < 20 ? "Starting..." : progressValue < 70 ? "Loading data.." : "Finishing"; this.DispatcherService.Invoke(() => { this.SplashScreenManagerService.ViewModel.Progress = progressValue; this.SplashScreenManagerService.ViewModel.Status = $"({progressValue} %) - {state}"; }); }

Files to Review

Documentation

More Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

SplashScreenManagerExample/Views/MainView.xaml
XAML
<UserControl x:Class="SplashScreenManagerExample.Views.MainView" 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:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ViewModels="clr-namespace:SplashScreenManagerExample.ViewModels" xmlns:Views="clr-namespace:SplashScreenManagerExample.Views" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <dxmvvm:Interaction.Behaviors> <dx:SplashScreenManagerService InputBlock="WindowContent" StartupLocation="CenterOwner"> <dx:SplashScreenManagerService.ViewTemplate> <DataTemplate> <Views:SplashScreenView /> </DataTemplate> </dx:SplashScreenManagerService.ViewTemplate> <dx:SplashScreenManagerService.SplashScreenWindowStyle> <Style TargetType="dx:SplashScreenWindow"> <Setter Property="AllowAcrylic" Value="True" /> <Setter Property="AllowsTransparency" Value="True" /> <Setter Property="Background" Value="#B887A685" /> </Style> </dx:SplashScreenManagerService.SplashScreenWindowStyle> </dx:SplashScreenManagerService> <dxmvvm:DispatcherService/> </dxmvvm:Interaction.Behaviors> <UserControl.DataContext> <ViewModels:MainViewModel /> </UserControl.DataContext> <Grid> <Button Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Command="{Binding StartCommand}" Content="Start a complex operation" /> </Grid> </UserControl>
SplashScreenManagerExample/Views/SplashScreenView.xaml
XAML
<UserControl x:Class="SplashScreenManagerExample.Views.SplashScreenView" 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:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" MinWidth="660" MinHeight="360" mc:Ignorable="d" Foreground="White"> <Grid Background="Transparent"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <dx:DXImage Margin="20" HorizontalAlignment="Left" VerticalAlignment="Top" Source="{Binding Logo}" Stretch="None" /> <dx:SimpleButton Margin="20" HorizontalAlignment="Right" VerticalAlignment="Top" Command="{Binding Tag}" Glyph="{dx:DXImage GrayScaleImages/Edit/Delete_16x16.png}" ToolTip="Cancel and Close" /> <StackPanel Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"> <TextBlock Margin="2" HorizontalAlignment="Center" FontSize="21" Text="{Binding Title}" /> <TextBlock Margin="2" HorizontalAlignment="Center" FontSize="15" Opacity="0.75" Text="{Binding Subtitle}" /> </StackPanel> <ProgressBar Grid.Row="2" Width="350" Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Value="{Binding Progress}" Background="#FFA3A3A3" BorderBrush="#FFF3F3F3" BorderThickness="1" Foreground="#FFFFFFFF" IsIndeterminate="{Binding IsIndeterminate}" /> <TextBlock Grid.Row="3" Margin="20,3,3,20" HorizontalAlignment="Left" VerticalAlignment="Bottom" FontSize="11" Text="{Binding Copyright}" /> <TextBlock Grid.Row="2" Margin="0,3,3,6" HorizontalAlignment="Center" VerticalAlignment="Bottom" FontSize="11" Text="{Binding Status}" /> </Grid> </UserControl>
SplashScreenManagerExample/ViewModels/MainViewModel.cs(vb)
C#
using System; using System.ComponentModel; using System.Threading; using System.Windows.Threading; using DevExpress.Mvvm; using DevExpress.Mvvm.DataAnnotations; namespace SplashScreenManagerExample.ViewModels { public class MainViewModel : ViewModelBase { public ISplashScreenManagerService SplashScreenManagerService { get { return this.GetService<ISplashScreenManagerService>(); } } public IDispatcherService DispatcherService { get { return this.GetService<IDispatcherService>(); } } BackgroundWorker worker; public bool CanStart() { return worker == null || !worker.IsBusy; } [Command(CanExecuteMethodName = "CanStart")] public void Start() { if(this.SplashScreenManagerService != null) { this.SplashScreenManagerService.ViewModel = new DXSplashScreenViewModel(); this.InitSplashScreenViewModel(this.SplashScreenManagerService.ViewModel); this.SplashScreenManagerService.Show(); this.RunBackgroundWorker(); } } void RunBackgroundWorker() { worker = new BackgroundWorker(); worker.DoWork += Worker_DoWork; worker.WorkerSupportsCancellation = true; worker.RunWorkerAsync(); } private void Worker_DoWork(object sender, DoWorkEventArgs e) { int i = -1; while(++i < 100) { if(worker.CancellationPending) { e.Cancel = true; break; } UpdateSplashScreenContent(i); Thread.Sleep(200); } this.DispatcherService.Invoke(() => { this.SplashScreenManagerService.Close(); worker.DoWork -= Worker_DoWork; worker = null; }); } void UpdateSplashScreenContent(int progressValue) { var state = string.Empty; state = progressValue < 20 ? "Starting..." : progressValue < 70 ? "Loading data.." : "Finishing"; this.DispatcherService .Invoke(() => { this.SplashScreenManagerService.ViewModel.Progress = progressValue; this.SplashScreenManagerService.ViewModel.Status = $"({progressValue} %) - {state}"; }); } void InitSplashScreenViewModel(DXSplashScreenViewModel vm) { vm.Title = "SOME BACKGROUND WORK"; vm.Subtitle = "This can take some time"; vm.Logo = new Uri("pack://application:,,,/logo.png"); vm.IsIndeterminate = false; vm.Tag = new DelegateCommand(CancelOperation, CanCancelOperation); } public bool CanCancelOperation() { return worker != null && worker.IsBusy; } public void CancelOperation() { if(worker != null && worker.IsBusy) worker.CancelAsync(); } } }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.