SplashScreen

How to create an Extended Splashscreen for Universal Apps

Recently, I started to create a real world app to demonstrate the usage of my WordPressUniversal library. While I was working on it, I also decided to extend the Splahscreen which is mandatory for universal apps.There are several reasons to extend the splash screen, the most obvious is to hide initial data loading.

The only sample I found had a separate solution for the Windows and the Windows Phone application, and I decided to put them together to use the advantages of the Shared project that comes with every universal app.

The first step is to add your Splashscreen images to both projects. To do so, open their Package.appxmanifest files and go to the ‘Visual Assets’ tab. Add your images:

Screenshot (371)

Now add a UserControl to your shared project and rename the control type to “Grid” in the XAML file. After that, let us design the UI that is shown to the user when he starts our app:

<Grid.RowDefinitions>
    <RowDefinition/>
    <RowDefinition Height="180"/>
</Grid.RowDefinitions>

<!--Windows needs a Canvas-->
<Canvas x:Name="Win_Splash_Img" 
        Grid.Row="0" 
        Grid.RowSpan="2" 
        Visibility="Collapsed">
    <Image x:Name="extendedSplashImage_Win" 
           Source="Assets/SplashScreen.png"/>
</Canvas>

<!--Windows Phone needs a Viewbox-->
<Viewbox 
    x:Name="WP_Splash_Img"
    Grid.Row="0" Grid.RowSpan="2" 
    Visibility="Collapsed">
    <Image x:Name="extendedSplashImage_WP" 
           Source="Assets/SplashWindowsPhone.png"/>
</Viewbox>

<!--this StackPanel holds our ProgressRing that is telling the user we are doing some work-->
<StackPanel 
    Grid.Row="1" 
    HorizontalAlignment="Center">
    <ProgressRing x:Name="progressRing" 
                  IsActive="True" Margin="0,0,0,12" 
                  Foreground="White" 
                  Background="Black">            
    </ProgressRing>
    <TextBlock x:Name="progressText" 
               Style="{StaticResource TitleTextBlockStyle}" 
               TextAlignment="Center" 
               HorizontalAlignment="Center">
 </TextBlock>
</StackPanel>

This XAML code is the base for our extended Splashscreen. It defines a row height of 180 to place the StackPanel that holds the ProgressRing at the bottom of our control. The main difference here is that Windows Apps use a Canvas while Windows Phone uses Viewbox to hold our Splashscreen Image. We need to set the Visibility on both to collapsed as our code decides which one will be displayed on Launch.

In our code page, we have a bit more work to do. Let’s go through it step by step:

Make sure you have added the following Namespaces:

using Windows.ApplicationModel.Activation;
using Windows.UI.Core;
using ExtendedSplash.Common;

Note: if you do not have a Common folder in your Shared Project, you can add it manually and copy the classes from the demo project attached to this project. Don’t forget to change the Namespaces of the classes to your project name after you added them.

The next step is to declare some objects that we need for the extended Splashscreen:

internal Rect splashImageRect; 
internal bool dismissed = false; 
internal Frame rootFrame;
private SplashScreen splash;

The Rect is used to place the Splashscreen Image in it and will be sized by our code which we will add later. The dismissed bool is used to determine when the system Splashscreen gets dismissed. The rootFrame is used for the navigation to our first application page, while the splash is used to read the needed values for placing our image into the Rect.

After we have declared those objects, we need to overload the main class of our control:

public ExtendedSplash(SplashScreen splashcreen, bool loadstate)

The splashcreen tells us the coordinates and the height of the splash image. You can use the loadstate bool to determine data that needs to be restored by the SuspensionManager, if needed.

As users are snapping our apps or rotating the device, we need to handle this. To do so, we need to add the following line to our constructor:

Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);

Add this code to handle the resizing:

if (splash != null)
{
    splashImageRect = splash.ImageLocation;
    PositionImage();
}

If the user causes the WindowSizeChanged event to fire, the splash will be loaded again and submits new coordinates to our splash object. To properly handle the positioning, we are using the PositionImage() method:

        void PositionImage()
        {
#if WINDOWS_PHONE_APP
            extendedSplashImage_WP.SetValue(Viewbox.HeightProperty, splashImageRect.Height);
            extendedSplashImage_WP.SetValue(Viewbox.WidthProperty, splashImageRect.Width);
#else
            extendedSplashImage_Win.SetValue(Canvas.LeftProperty, splashImageRect.X);
            extendedSplashImage_Win.SetValue(Canvas.TopProperty, splashImageRect.Y);
            extendedSplashImage_Win.Height = splashImageRect.Height;
            extendedSplashImage_Win.Width = splashImageRect.Width;
#endif
        }

As you can see, we use the preprocessor directive WINDOWS_PHONE_APP to declare the height and width of the Viewbox. If it is not running on Windows Phone, we are setting the canvas coordinates and size via this method. This is not the only point where we need this method, as you will see later in this post.

Let’s go back to our constructor and add this line to load the system’s Splashscreen values into our object:

splash = splashcreen;

Now that we have these values, we are finally able to receive the coordinates and declare which Splashscreen will be used:

            if (splash != null)
            {
                //handle the dismissing of the system's splash 
                splash.Dismissed += new TypedEventHandler<SplashScreen, Object>(DismissedEventHandler);

                //get the system's splashscreen values
                splashImageRect = splash.ImageLocation;

                //decide which control is used to display the splashscreen image and size the progressRing
#if WINDOWS_PHONE_APP
                Win_Splash_Img.Visibility = Visibility.Collapsed;
                WP_Splash_Img.Visibility = Visibility.Visible;
                progressRing.Height = 50;
                progressRing.Width = 50;

#else
                Win_Splash_Img.Visibility = Visibility.Visible;
                WP_Splash_Img.Visibility = Visibility.Collapsed;
                progressRing.Height = 70;
                progressRing.Width = 70;
#endif
                //position and size the image properly
                PositionImage();

            }

 

Let me explain what I have done here. First we need to check if our splash object has a value. Then, we need to handle the Dismissing of the system’s Splashscreen with this TypedEventHandler:

private void DismissedEventHandler(SplashScreen sender, object args)
{
    dismissed = true;
}

After that, I am declaring when to show Windows Phone’s Viewbox and Windows’ Canvas as well as setting the size of the ProgressRing. The last step is to position and size the image again with our PositionImage() method. This will cause the app to switch to our extended Splashscreen.

Now we have to declare a new Frame instance and begin to load our data:

// this frame acts as navigation context
rootFrame = new Frame();
//start loading your data:
LoadData();

For this demo, I used a delay to keep the ProgressRing spinning:

async void LoadData()
{
    progressText.Text = "sleeping, please wait until I wake up...";
    //using a delay to keep the progress ring spinning for this demo
    await Task.Delay(TimeSpan.FromSeconds(5));
    //Navigate to the first application page after all work is done
    rootFrame.Navigate(typeof(MainPage));
    Window.Current.Content = rootFrame;
}

That’s all of the code we need inside our ExtendSplash control. If you now build the project, it will compile, but nothing will happen. other than going directly to the MainPage.

We need to add some additional code to our App.xaml.cs in the OnLaunched event:

//only show the splash if the app wasn't running before
if (e.PreviousExecutionState != ApplicationExecutionState.Running)
{
    //check the loadstate if necessary
    bool loadState = (e.PreviousExecutionState == ApplicationExecutionState.Terminated);
    //create a new instance of our ExtendedSplash
    ExtendedSplash extendedSplash = new ExtendedSplash(e.SplashScreen, loadState);
    //declare our extendedSplash as root content
    rootFrame.Content = extendedSplash;
}

If you hit the debug button, you should have similar results to these:

Screenshot (374)

Screenshot (375)

For your convenience, I created a small demo project. Download it here.

As always, I hope this blog post is helpful for some of you. If you have questions or feedback, feel free to leave a comment below.

Posted by msicc in Dev Stories, windev, 8 comments
WPDEV: smooth SplashScreen transition for your Windows Phone app

WPDEV: smooth SplashScreen transition for your Windows Phone app

Today I finished an update for one of my apps with adding a CustomControl as SplashScreen. I had to do it this way because my app is localized in English, German and Italian. Most annoying at this part was that there is no smooth transition between the SplashScreen and the MainPage. By the way, there is also no smooth transition with the “normal” SplashScreenImage.jpg that is included in all app templates of Visual Studio 2010.

I often use the transitions that are built into the Silverlight for Windows Phone Toolkit (download here or use the Nuget-installer of Visual Studio). These transitions can be modified and used for custom transitions. I will show you how to create a fade out effect for your custom SplashScreen control.

First we have to create a project. I called the sample SplashScreenTransition.

The first thing we have to do is to create two classes: CustomTransition.cs and SplashScreenTransition.cs.

Let´s have a look on the first class, CustomTransition.cs:

namespace SplashScreenTransition
{
    public class CustomTransition : ITransition

    {
        Storyboard storyboard;

        public CustomTransition(Storyboard sb)
        {
            storyboard = sb;
        }

        // these are used by the transition

        public void Begin()
        {
            storyboard.Begin();
        }

        public event EventHandler Completed
        {
            add
            {
                storyboard.Completed += value;
            }
            remove
            {
                storyboard.Completed -= value;
            }
        }

        // not used for SplashScreen, but needed to create the whole animation
        // if you don´t add these, you will not be able to build your project

        public void Stop()
        {
            storyboard.Stop();
        }

        public void Pause()
        {
            storyboard.Pause();
        }

        public void Resume()
        {
            storyboard.Resume();
        }

        public void SkipToFill()
        {
            storyboard.SkipToFill();
        }

        public void Seek(TimeSpan offset)
        {
            storyboard.SeekAlignedToLastTick(offset);
        }

        public void SeekAlignedToLastTick(TimeSpan offset)
        {
            storyboard.SeekAlignedToLastTick(offset);
        }

        public ClockState GetCurrentState()
        {
            return storyboard.GetCurrentState();
        }

        public TimeSpan GetCurrentTime()
        {
            return storyboard.GetCurrentTime();
        }
    }
}

As you can see, we create a new Storyboard which will hold our transition. We use the ITransition interface that comes with the Toolkit.

When I was creating my first custom ITransition, I was running into several issues. We are effectively using the Begin()-Method and the Eventhandler Completed. But we have to implement all other Methods to get this class working.

Now let´s have a look to our second class, SplashScreenTransition.cs:

namespace SplashScreenTransition
{
    public class SplashScreenTransition : TransitionElement
    {
        public override ITransition GetTransition(UIElement element)
        {
            Storyboard FadingStoryBoard = CreateStoryBoard(1.0, 0.0);
            Storyboard.SetTarget(FadingStoryBoard, element);
            return new CustomTransition(FadingStoryBoard);
        }

        private Storyboard CreateStoryBoard(double from, double to)
        {
            Storyboard result = new Storyboard();
            DoubleAnimation animation = new DoubleAnimation();
            animation.From = from;
            animation.To = to;
            Storyboard.SetTargetProperty(animation, 
                new PropertyPath(UIElement.OpacityProperty));
            result.Children.Add(animation);
            return result;
        }

    }
}

With the code above we are connecting the animation itself to an UI-Element. To get this working, we need to create a simple opacity fading Storyboard which we connect to an UIElement. We connect the Storyboard to the GetTransition-Method by overriding it. What we get is our CustomTransition.

Now the only thing we have to do is to display the animation, which gets a bit tricky.

Create a new User Control. I added a a TextBlock and an Image to the sample.

Important:

  • set the value of d:DesignHeight to 800 (to cover the whole screen)
  • put your Image/TextBlock into a Stackpanel and set the Background. If you do not, the Background will be transparent and your MainPage will be visible

To display the custom SplashScreen and its animation, we have to create a Popup and a BackgroundWorker:

  • declare the names of both in your PhoneApplicationPage:
public partial class MainPage : PhoneApplicationPage
    {
        // declaration of BackgroundWorker and Popup
        BackgroundWorker bgWorker;
        Popup SplashPopup;
  •  next step is to add a new Popup which holds our SplashScreen-UserControl and start our BackgroundWorker:
// Constructor
        public MainPage()
        {
            InitializeComponent();

            //calling Popup, using our UserControl

            SplashPopup = new Popup() { IsOpen = true, Child = new SplashScreenControl() };
            bgWorker = new BackgroundWorker();
            RunBackgroundWorker();
        }
  •  Last but not least we have to get our BackgroundWorker to do some work and display the transition:
private void RunBackgroundWorker()
        {
            bgWorker.DoWork += ((s, args) =>
            {
                Thread.Sleep(5000);
            });

            bgWorker.RunWorkerCompleted += ((s, args) =>
            {
                this.Dispatcher.BeginInvoke(() =>
                {
                    SplashScreenTransition SplashScreenTrans = new SplashScreenTransition();
                    ITransition transition = SplashScreenTrans.GetTransition(this.SplashPopup.Child);
                    transition.Completed += delegate { this.SplashPopup.IsOpen = false; };
                    transition.Begin();
                 }
            );
            });
            bgWorker.RunWorkerAsync();
        }

If you would display only the SplashPopup, you would only set IsOpen to false. But we want to get the Fading out effect, so we call a new SplashScreenTransition.  Then we connect our custom transition to ITransition of the Toolkit and let the transition begin.

Essential is the delegation of the Completed Eventhandler. If you do not set the IsOpen of SplashPopup to false, the Popup remains open. That would result in a non-responding app.

The result of the work done above will look like this:

I hope this article was helpful for creating a smooth transition of your SplashScreen.

Download the sample code here.

Posted by msicc in Dev Stories, windev, 0 comments