Introduction

Making a splashscreen in WPF is easy, since the .Net framework SP1 there is even a built in method to do this. But this method only accepts a static image. I would have liked a more dynamic thing. Something that says what it is doing. And something that fades in and out and has a custom shape. I’m not very demanding but I know this would be very complicated in windows forms. Possible but difficult. In WPF it seems a bit easier. So I went on a Google frenzy and combined several things I found along the way.

The static splashscreen

  • Make a nice Image.
  • Add it to the project via Add->Existing Item or Ctrl+D.
  • Click on the file and change the Build action to SplashScreen

And that’s all there is to it. It does have fade-in and fade-out effect.

The dynamic splashscreen

This was very easy since Google’s first hit was this MSDN forum post.

And someone even handed us the VB.Net sample.

So I just had to add a window called splash.xaml

That had these properties.

ResizeMode="NoResize" 
ShowInTaskbar="False" 
WindowStartupLocation="CenterScreen" 
WindowStyle="None" 
Topmost="True" 
ShowActivated="False" 

And this in the code behind.

Namespace View.Forms
    Public Class Splash
        Public Splash()
        Private Shared InitCompleted As AsyncCallback
        Private Shared result As IAsyncResult
        Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
            InitCompleted = AddressOf _initCompleted
            result = My.Application.dLoadSplash.BeginInvoke(Me, InitCompleted, Nothing)
            lblProgress.Content = "Loading..."
        End Sub

        Private Sub _initCompleted()
            My.Application.dLoadSplash.EndInvoke(result)
            Dispatcher.BeginInvoke(New Action(AddressOf _closeWindow), Windows.Threading.DispatcherPriority.Normal, Nothing)
        End Sub

        Private Sub _closeWindow()
            Close()
        End Sub

        Public Sub SetProgress(ByVal Message As String)
            Dispatcher.BeginInvoke(New Action(Of String)(AddressOf _SetProgress), Windows.Threading.DispatcherPriority.Normal, Message)
        End Sub

        Private Sub _SetProgress(ByVal Message As String)
            lblProgress.Content = Message
        End Sub

End Class
End Namespace```
I changed it so that you can show messages to the user and I set the progressbar to continuous. 

Then we just make this form the startup form. And we add a little bit of code to the codebehind of Application to make it all work.

```vbnet
Imports TDB2009.View.WPFMenu.View.Forms
Imports TDB2009.Utils.IoC
Imports TDB2009.View.WPFMenu.Startup.Interfaces
Imports System.Windows.Threading

Public Class Application

    Public dLoadSplash As New _dLoadSplash(AddressOf LoadSplash)
    
    Public Delegate Sub _dLoadSplash(ByVal w As Splash)

    Private Sub LoadSplash(ByVal w As Splash)
        w.SetProgress("Configuring Container")
        IoCUtil.InsertConfigOfContainingType(Of IoC.Configure)()
        Dim _StartUp As IStartUp = Container.Resolve(Of IStartUp)()
        w.SetProgress("Starting Mainwindow")
        Dispatcher.BeginInvoke(New Action(AddressOf CreateMainWindow), DispatcherPriority.Normal, Nothing)
    End Sub

    Private Sub CreateMainWindow()
        MainWindow = Container.Resolve(Of View.Forms.Interfaces.IMenu)()
        MainWindow.Show()
    End Sub
    
    Private Sub Application_Exit(ByVal sender As Object, ByVal e As System.Windows.ExitEventArgs) Handles Me.Exit
        Container.DisposeAll()
    End Sub
End Class

Edit: the above without my IoC interfering.

Imports TDB2009.View.WPFMenu.View.Forms
Imports TDB2009.Utils.IoC
Imports TDB2009.View.WPFMenu.Startup.Interfaces
Imports System.Windows.Threading

Public Class Application

    Public dLoadSplash As New _dLoadSplash(AddressOf LoadSplash)
    
    Public Delegate Sub _dLoadSplash(ByVal Screen As Splash)

    Private Sub LoadSplash(ByVal Screen As Splash)
        Screen.SetProgress("Starting Mainwindow")
        Dispatcher.BeginInvoke(New Action(AddressOf CreateMainWindow), DispatcherPriority.Normal, Nothing)
    End Sub

    Private Sub CreateMainWindow()
        MainWindow = new MainWindow()
        MainWindow.Show()
    End Sub
    
    Private Sub Application_Exit(ByVal sender As Object, ByVal e As System.Windows.ExitEventArgs) Handles Me.Exit
        Container.DisposeAll()
    End Sub
End Class

That was simple and I didn’t change much from what I saw in the post. You can just copy/paste it but you have to understand what it is doing.

The fade in and fade out

But the above code is still not much better than what we can do in winforms. So now it is time to add some fade in and fade out to it. Again I directed my attention to Google and found some inspiration in a Stackoverflow article.

But there are some problems with what they said, the solution is not really copy-pastable as is.

Getting a fade in is easy. you just add a trigger to the window, like so.

<Window.Triggers>
        <EventTrigger RoutedEvent="Window.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:2" FillBehavior="HoldEnd" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Window.Triggers>```
That works, but it had one minor glitch that I will cover in a little bit.

Then you need to add the fadeout. This is more work and needs some code behind.

First you add some xaml.

```xml
<Window.Resources>
        <Storyboard x:Key="closeStoryBoard" Completed="closeStoryBoard_Completed">
            <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:2" FillBehavior="HoldEnd" />
        </Storyboard>
    </Window.Resources>```
And then you add a few bits to the codebehind.

```vbnet
Private closeStoryBoardCompleted As Boolean
        
        Private Sub Splash_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Closing
            If Not closeStoryBoardCompleted Then
                Me.Resources("closeStoryBoard").Begin(Me)
                e.Cancel = True
            End If
        End Sub

        Private Sub closeStoryBoard_Completed(ByVal sender As System.Object, ByVal e As System.EventArgs)
            closeStoryBoardCompleted = True
            Me.Close()
        End Sub

This has a slight bug in it. Which can only be seen if your splash screen is not going to be shown long enough (less than 4 seconds to be precise). So I had to make a few adaptations so that I would still get a fade in and out even if the loading of the form takes less than 4 seconds.

First we add an event completed to the beginstoryboard. like so.

xml <Window.Triggers> <EventTrigger RoutedEvent="Window.Loaded"> <BeginStoryboard> <Storyboard Completed="beginStoryBoard_Completed"> <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:2" FillBehavior="HoldEnd" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Window.Triggers> And then we change our codebehind to this.

```vbnet Private closeStoryBoardCompleted As Boolean Private beginStoryBoardCompleted As Boolean

    Private Sub Splash_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Closing
        If Not closeStoryBoardCompleted AndAlso beginStoryBoardCompleted Then
            Me.Resources("closeStoryBoard").Begin(Me)
            e.Cancel = True
        End If
    End Sub

    Private Sub closeStoryBoard_Completed(ByVal sender As System.Object, ByVal e As System.EventArgs)
        closeStoryBoardCompleted = True
        Me.Close()
    End Sub

    Private Sub beginStoryBoard_Completed(ByVal sender As System.Object, ByVal e As System.EventArgs)
        beginStoryBoardCompleted = True
    End Sub```

I added the beginstoryboardcompleted and made sure it only it was set to true, meaning the fadein is done, before starting the fadeout. Simple really, if you understand what you are copy pasting.

The custom shape

To make a form a custom shape is just as easy. First you set your window properties like so.

xml Background="Transparent" AllowsTransparency="True" Then you add an image to form. and that’s it.

And just for fun I made the ugliest splashscreen you can think of, but it works. This is the design view.

See how the image is outside of the bounds of the window?

Now see that when you run it it will chop of those parts.

So now your window is completely transparent and as ugly as I could make it ;-).

Conclusion

Making a dynamic splashscreen in WPF is easy and well worth the trouble. Your users will love you. Getting to grips is easy too, you can use Google and find just about anything you want or can think of. Some examples are just good and some examples need a little work. Some examples need to be ignored. But remember don’t just copy paste the code, try to understand it first. You know you understood it when you can combine and change different pieces and make them work together.