This project is read-only.

Extended SplashScreen

Dec 1, 2012 at 11:32 PM

I'm putting the final touches on my first Okra WinRT app, and implementing an extended splash screen is giving me some trouble.  What is your recommendation on this process?  How would you do it? 

I tried setting my "Home" page to be the splash screen and navigate away when I'm done loading data up, and that seems to work ok... except for one small case.  If the app isn't running, and the user brings up the search charm, then clicks on my app without typing anything into search, it crashes.  OR if they type something in, it will go to the search results page... but clicking the back button makes it crash again.  It's because the SDK Splashscreen example I followed overrides the App.OnLaunched Event to pass the LaunchActivatedEventArgs.SplashScreen object to their ExtendedSplashScreen view.  It is then used to determine size/position of the Extended splash screen.  I instead set the LaunchActivatedEventArgs.SplashScreen to a public property on the App and refer to it from my view, leaving the activation, stateloading, and navigation to okra.  However, this property is not being set when launching the app through search, and thus crashing my app out when navigating "home" (my extended splashscreen).  I see the bit of Okra code that says "if this is a new instance of the app, navigate home, then display the results" effectively putting 'home' at the top of the navigation stack.  Is there a way to override this, or am I going about this all wrong? 

Thanks again.

Dec 3, 2012 at 1:23 PM
Edited Dec 3, 2012 at 1:23 PM

Good to hear that you are getting on well with your first Okra WinRT app. Regarding extended splash screens this isn't something I've explicitly included any support for in the framework but this should be possible to do in the bootstrapper.

My thoughts about the best way to do this would be to override the OkraBootstrapper.OnActivated(...) method where you can intercept any activation events before Okra does anything itself. Here you could set the splash screen, perform any initialization you require, then call the base.OnActivated(...) method to continue to the standard home page.

Something like... (NB: I'm typing this by hand from memory but just to give you an idea!),

protected virtual async void OnActivated(CoreApplicationView sender, IActivatedEventArgs args)
{
    // Show the splash screen
    MySplashScreen splash = new MySplashScreen(args);
    Window.Current.Content = splash;

    // Do something here
    await DoInitCode();

    // Return control to Okra
    base.OnActivated(sender, args);
}

The spash screen couldn't use the Okra viewmodel support, but for a simple splash screen this shouldn't be a problem. Maybe this is something I can implement in the framework in the future.

Does this look like it might work for you?

Regards,

    Andy

Dec 3, 2012 at 3:28 PM

Thanks for the reply Andy,

With my current configuration I am using the Okra view model support, wiring my splashscreen to a viewmodel which does all loading of data based on default settings.  If there are any critcal selections not made in the settings, e.g. first time running, a combo box is displayed prompting the user to select the default setting.  I do all this on the VM, and am using Okra/MEF to bring in my ApplicationSettings class and DataService class from the container.  Since I need access to these objects and would be handling all the resolution of MEF objects after, I'm not sure that your suggested solution would work.  Unless those references are resolved prior to when I would be doing the "MySplashScreen = new MySplashScreen(args)" line?  Wiring the view to the view model is easy without okra, it's the IoC that worries me. 

In a future release, it would be cool if you can do something like your search screen/home screen where you just set a the name of your extended splash screen and Okra handles it for you based on whether or not you gave it a value, and of course knows what to do in those pesky situations that currently cause a crash :)

Thanks for the continued support!

Dec 4, 2012 at 1:22 PM

This should be fairly straightforward to do. The OkraBootstrapper class should have all it's IoC references composed by the time that the OnActivated(...) method is called so you can just let MEF do all the hard work. You could do something like,

public class AppBootstrapper : OkraBootstrapper
{
    [Import]
    public MySplashPage SplashPage { get; set; }

    [Import]
    public MySplashViewModel SplashViewModel { get; set; }

    protected virtual async void OnActivated(CoreApplicationView sender, IActivatedEventArgs args)
    {
        SplashPage.DataContext = SplashViewModel;
        Window.Current.Content = SplashPage;

        // etc.
    }
}

In the part marked "etc." you could do the same as I discussed in my previous comment (maybe awaiting on a Task on the SplashViewModel to signal completion). Note that this will always create and compose the extended splash screen - alternatively you could define the imports as Lazy<MySplashPage> and then these are composed on demand.

Regards,

    Andy

Dec 5, 2012 at 11:14 PM

Thanks for the help.  I was able to get the results I wanted with the following bit:

 [Import]
 public ExtendedSplashScreenView SplashScreenView { get; set; }

[Import]
 public ExtendedSplashScreenViewModel SplashScreenViewModel { get; set; }

 protected override async void OnActivated(CoreApplicationView sender, IActivatedEventArgs args)
 {
       if (args.SplashScreen != null)
      {
             SplashScreenView.DataContext = SplashScreenViewModel;

             Window.Current.Content = SplashScreenView;
             Window.Current.Activate();

             await SplashScreenViewModel.DoWork();
       }

        base.OnActivated(sender, args);
}
Dec 6, 2012 at 1:13 PM

Great! Good to hear that works okay for you.

I'll add support for custom splash screens to my list of features to add to Okra.

Andy

Jan 20, 2013 at 10:02 PM

Hi - from what class to you inherit your SplashScreenView?
I tried the same, but the Extended SplashScreen doesn't Show up...

Thanks
Andi

Jan 21, 2013 at 2:25 PM
Edited Jan 21, 2013 at 2:30 PM

My SplashScreenView is just a Common.LayoutAwarePage.  I got the view code from the extended splash screen sample in the sdk samples then took out what I didn't need.   In order to get it working, I had to make a splashScreen property which I set in the OnActivated in the bootstrapper which the view can then use. Here is the full example code:

SplashScreenView

namespace MyApplication.Views
{
    [Export]
    public sealed partial class SplashScreenView : MyApplication.Common.LayoutAwarePage
    {
        internal Rect splashImageRect; // Rect to store splash screen image coordinates.
        private SplashScreen splash; // Variable to hold the splash screen object.

        public SplashScreenView()
        {
            InitializeComponent();

            // Listen for window resize events to reposition the extended splash screen image accordingly.
            // This is important to ensure that the extended splash screen is formatted properly in response to snapping, unsnapping, rotation, etc...
            Loaded += SplashScreenView_Loaded;            
        }

        void SplashScreenView_Loaded(object sender, RoutedEventArgs e)
        {
            Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);
            // I stored a SplashScreen object in App.xaml.cs so I could access it from this view and the bootstrapper.
            this.splash = ((App)Application.Current).splashScreen;
	   splashImageRect = splash.ImageLocation;
            PositionImage();
        }

        // Position the extended splash screen image in the same location as the system splash screen image.
        void PositionImage()
        {
            extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
            extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);
            extendedSplashImage.Height = splashImageRect.Height;
            extendedSplashImage.Width = splashImageRect.Width;
        }

        void ExtendedSplash_OnResize(Object sender, WindowSizeChangedEventArgs e)
        {
            // Safely update the extended splash screen image coordinates. This function will be fired in response to snapping, unsnapping, rotation, etc...
            if (splash != null)
            {
                // Update the coordinates of the splash screen image.
                splashImageRect = splash.ImageLocation;
                PositionImage();
            }
        }
    }
}
App.xaml.cs Add a Splash Screen property (noted above)
sealed partial class App : Application { public AppBootstrapper bootstrapper; public SplashScreen splashScreen; public App() { InitializeComponent(); bootstrapper = new AppBootstrapper(); bootstrapper.Initialize(); } }

AppBootstrapper Same as above, but now setting the splashScreen property from args

protected override async void OnActivated(CoreApplicationView sender, IActivatedEventArgs args) { if (args.SplashScreen != null) { (Application.Current as App).splashScreen = args.SplashScreen; SplashScreenView.DataContext = SplashScreenViewModel; Window.Current.Content = SplashScreenView; Window.Current.Activate(); await SplashScreenViewModel.DoWork(); } base.OnActivated(sender, args); }

The only missing piece is your viewmodel... it should be wired up with the above code. Just fill in the DoWork() bit and you should be good to go!