Yet Another Splash Screen Discussion :)

Topics: Bootstrappers & IoC, Conventions, Getting Started, UI Architecture
Jul 15, 2013 at 3:08 PM
I'm new to CM and mostly loving it. I have but one gripe so far: I don't know how to use the Conductor/Screen architecture to show a progress-updating splash screen. I'm sure I'm just looking at the problem the wrong way.

Yes, I have read all the previous posts on how to orchestrate a Splash Screen in CM. None of them have resolved in a way that I consider "good guidance."

For me this is necessary only because I want to update a label on my splash image as my application loads, to give users some feedback as it is doing so. It is a large application and the startup process involves some asynchronous calls as well as some synchronous build-up logic, and I'd like the user to be informed as it progresses. It's a usability issue more than anything and if all else fails I can just show a splash image.

Though CM has simplified many things, it's not clear to me how to implement the informative splash screen idiom in a canonical way that doesn't feel like scratching my left ear with my right hand.

So here are my requirements:
  1. As the application bootstrapper starts loading, it displays a splash screen, which can receive notification messages as the shell view model is loaded (hence it depends on IEventAggregator). It consists of a background image and a label which overlays a conspicuously blank part of the image. Initially this label says "Loading...".
  2. As it is loading the ShellViewModel fires text messages about its progress that the splash screen displays.
  3. When the application loading is nearly complete the ShellView appears behind it and the SplashScreen is dismissed.
I've tried a number of different things but could use, and would appreciate, any experienced help to avoid cycling on what should be a fairly trivial thing.
Jul 15, 2013 at 4:28 PM
I'm assuming WPF in this answer but I'm guessing it should be applicable to other targets, with tweaks
I've been musing over the same issue for a while now and I think I have a couple of options which may work for people.
  1. Using context views of your Shell's ViewModel
  2. Using the decorator pattern for IShell
Here's a brief outline of how this would work in either case.

Context Views
  • The the main ShellView will have a contentcontrol bound to the current model, with a context.
  • There'll be an enum of context states for your shell. (AuthenticationRequired, LoadingProgress, Normal)
  • the Shell's VM will manage the mini life cycle internally and update the context, and the corresponding views.
Pro: uses CMs current functionality for context views, which is nice.
Con: Shell's ViewModel has many responsibilities, including supervising controller on the view's height and width etc, the management of the lifecycle could get tricky.

  • The root view model is a conductor of IShell. (the base conductor for just one active item, not with a collection of items, at least in the simple case)
  • The main application viewmodel implement's IShell
  • The splash screen view model impliements IShell
  • setup via IoC container that Splash screen VM decorates main VM
  • repeat for authentication?
The decorating IShells both are-a IShell and have-a IShell. When a particular decorator is done - it can call
(Parent as IConductor).ActivateItem(innerShell);
At the appropriate time, i.e. when the user's authenticated, the progress is 100% - etc.

Pros: each implementation if IShell is focused on what it's doing, with it's own view etc. The life cycle steps are managed externally in the bootstrapper.
Cons: intially, potentially trickier to set up, depends on your IoC container supporting decorators. Each shell implementation has to either inherit from CM.Screen or implement IChild so the main container can set up the parenting stuff; some event listening will have to be setup in the main container to get things like the correct DisplayName from the currently active shell VM - but only in one place and will work for all IShells.

I'm sure there are other issues for either route but currently I am favoring the decorator approach.
Jul 16, 2013 at 2:02 PM
Thanks for the suggestions. I'll look into them; offhand for my scenario I somewhat prefer the Context Views strategy as I may need to revert back to AuthenticationRequired if a logged in session expires, for example. It seems not so much work to manage just those three states.

In the meanwhile I have a solution that "almost works" the way I want it to. The only thing that isn't working is the updating of the label when a startup notification is received.

What I've done is this:

<Window x:Class="Rover.InkFlow.DocuMentor.Views.SplashScreenView"
        Title="" Height="585.726" Width="705">
        <Image Source="/Resources/splash_screen.jpg"></Image>
                <RowDefinition Height="262*"/>
                <RowDefinition Height="27*"/>
            <TextBlock Grid.Row="1" x:Name="Message" VerticalAlignment="Top" Foreground="Yellow"  TextAlignment="Center"
               Height="30" HorizontalAlignment="Center" />
    public class StartupNotification
        private string message = String.Empty;

        public StartupNotification( string notification )
            this.message = notification;

        public string Message
            get { return this.message; }
            set { this.message = value; }

    [Export( typeof( SplashScreenViewModel ) )]
    public class SplashScreenViewModel : Screen, IHandle<StartupNotification>
        private IEventAggregator eventAggregator;

        private string message = "Loading...";

        public SplashScreenViewModel( IEventAggregator eventAggregator )
            this.eventAggregator = eventAggregator;
            this.eventAggregator.Subscribe( this );

        public String Message
            get { return this.message; }
                this.message = value;
                NotifyOfPropertyChange( () => Message );

        public void Handle( StartupNotification notification )
            this.Message = notification.Message;
Now in my ShellViewModel.cs:
        protected override void OnViewLoaded( object view )

            base.OnViewLoaded( view );

            windowManager.ShowWindow( splash );

            System.Threading.Thread.Sleep( 1000 );

            eventAggregator.Publish( new StartupNotification( "Still loading..." ) );

            System.Threading.Thread.Sleep( 1000 );

            eventAggregator.Publish( new StartupNotification( "Done loading." ) );

            System.Threading.Thread.Sleep( 500 );

            DeactivateItem( splash, true );

            // This sets my active item to the login view. A ContentControl named ActiveItem
            // allows Caliburn to perform its magic and insert the correct view in the area
            // of my shell where the current view goes.
            ActivateItem( GetScreen( ApplicationScreen.Login ) );

All is great -- the splash screen shows up and stays visible for the allotted seconds, then it disappears and my ShellView appears with the LogIn view showing in the content area. Awesome. The messages are published and received by the SplashScreenViewModel, which updates the message internally. Should be working, right?

Unfortunately, the update to the UI does not happen. After the initial setting of the message "Loading..." via the databinding mechanism, the updates to the message are not displayed. All the while the splash screen is up, a "Busy" icon appears when the mouse hovers over it.

So clearly I'm missing something really obvious here.

Now, technically, I can live with this state of affairs in the short term. I got a splash screen for a few seconds, it looks nice, and tells the user it's loading.

But I'd like to know why that is happening, even though I will likely try your approaches which seem more in line with "the Caliburn.Micro Way".
Jul 16, 2013 at 2:20 PM
I'm guessing that's simply due to NotifyPropertyChange does it's updating on the UI thread async, and the UI thread is sleeping?

Try creating a background worker thread that sleeps and posts the events, then close the splash when that thread completes.