Using Frames to Improve Navigation and Performance in Win 8 Xaml Apps

While working on the Khan Academy app, I realized early in the development that using the navigation pattern provided by the default project templates was not optimal. If you haven’t noticed, the code in default templates which bootstraps your application and creates the frame where your pages are hosted is by default in App.xaml.cs. When the app launches, it checks if the root content is a Frame and creates a new one if not.

protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
	Frame rootFrame = Window.Current.Content as Frame;

	// Do not repeat app initialization when the Window already has content,
	// just ensure that the window is active

	if (rootFrame == null)
	{
		// Create a Frame to act as the navigation context and navigate to the first page
		rootFrame = new Frame();

		//code to restore navigation state
		//this will cause navigation for "a" page if there was anything on navigation stack

		// Place the frame in the current Window
		Window.Current.Content = rootFrame;
	}
	if (rootFrame.Content == null)
	{
		// When the navigation stack isn't restored navigate to the first page,
		// configuring the new page by passing required information as a navigation
		// parameter
		if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups"))
		{
			throw new Exception("Failed to create initial page");
		}
	}
}

Once the frame is created, it navigates to the first page. If you are implementing an extended splash screen, this code moves there but we still have a frame and all navigation happens in that frame. Here are the reasons why I think that the default navigation is not optimal. Your mileage may vary.

1. Entrance Animations for Pages: If we want to use entrance animations for pages, that doesn’t come by default. It’s doable with the default frame created in bootstrap code as you can just add theme transitions to the frame and it would work as you’d expect.

//
if (rootFrame.ContentTransitions == null) rootFrame.ContentTransitions = new TransitionCollection();
rootFrame.ContentTransitions.Clear();
rootFrame.ContentTransitions.Add(new EdgeUIThemeTransition { Edge = EdgeTransitionLocation.Right });
//

If you want to use different entrance animations for different pages, that’s when it gets ugly. Let’s say you are using one consistent entrance animation for all pages but for one special page, you want a unique entrance animation. It’s doable but you’ll have to keep changing frame content transitions in the code every time you need different transitions from what is assigned to frame right now. That’s a big “no”. A different approach is to use separate frames as we will see later in this post. I used this approach in Khan Academy app to slide in the player from bottom of the screen whereas all other pages use standard animation.

2. Maintaining User Actions Across Page Navigations: Every time you navigate away from a page, the instance of the page gets disposed. Navigating to that page again will recreate the instance and have the root frame navigate to it. If the main page of the app is complex and has lot of content with options for users to perform different actions, maintaining the state where user was when he/she navigated away and restoring that state on navigating back to the main page is tricky. For example, let’s say user performs following actions in Khan Academy app.

  • Launch the app
  • Open the subject popup
  • Navigate within the subject popup
  • Tap on a video to play

At this point, if user navigates to player screen on the root frame, navigating back to main page will cause recreation of main page and we’d lose the state for user interactions. This is because I am showing subject details in popup rather than navigating to a separate page due to deep content hierarchy. If I’d do a full page navigation every time user wants to open subject content, this problem wouldn’t exist but the experience would be annoying as the users will have to keep going back and forth all the time.

This can be solved by enabling NavigationCacheMode on the page which makes Xaml runtime use one instance of the page across navigations. This comes with additional state maintenance though. Alternatively, you can use separate frames as will see below.

3. Performance due to Page Navigations: As the page instances are getting destroyed every time you navigate from a page and getting recreated when you navigate to it, this obviously comes with performance overhead. And it’s very noticeable on Windows RT devices which use low powered ARM chips. You can use NavigationCacheMode to cache pages but that comes with additional handling to refresh them when needed. This still doesn’t avoid the work needed for “navigating” to the cached page. In some cases, this might be good enough, you should know your options and pick what works for you.

Using multiple Frames to mitigate these issues

First, we would replace the root frame which was provided by the template with a page with multiple frames in it, lets call it HostPage. So, instead of creating a root frame and assigning that to current window content, we create an instance of HostPage and assign that to window content. At this point, HostPage becomes our one stop shop for all things navigation. For Khan Academy app, here is how the HostPage Xaml looks like.

<Grid x:Name="rootGrid" Style="{StaticResource LayoutRootStyle}">
	<Image Source="../Assets/Images/footer-leaves.png" VerticalAlignment="Bottom" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0.5" >
		<Image.RenderTransform>
			<CompositeTransform ScaleY="-1"/>
		</Image.RenderTransform>
	</Image>

	<Frame x:Name="hostFrame" Foreground="{x:Null}">
		<Frame.ContentTransitions>
			<TransitionCollection>
				<ContentThemeTransition HorizontalOffset="300" />
			</TransitionCollection>
		</Frame.ContentTransitions>
	</Frame>

	<Grid Visibility="{Binding IsOffline, Converter={StaticResource VisibilityConverter}}"
		  HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,10">
		<Border Background="{StaticResource ThemeContrastColorBrush}" CornerRadius="5" Margin="-10" />
		<TextBlock Text="Offline Mode" Style="{StaticResource TitleTextStyle}" Foreground="{StaticResource LightForegroundThemeBrush}" />
	</Grid>

	<Frame x:Name="playerFrame" Foreground="{x:Null}">
		<Frame.ContentTransitions>
			<TransitionCollection>
				<PaneThemeTransition Edge="Bottom" />
			</TransitionCollection>
		</Frame.ContentTransitions>
	</Frame>

	<ProgressBar x:Name="busyProgressBar" HorizontalAlignment="Stretch" Height="10" Margin="0,5" VerticalAlignment="Top"
				 IsIndeterminate="{Binding IsLoading}" Visibility="{Binding IsLoading, Converter={StaticResource VisibilityConverter}}"/>
</Grid>

It has two frames, one for hosting all pages and a separate one for player. When user plays a video (from featured videos, subject popup, search or download page), the player page is loaded in player frame with a unique sliding entrance animation from bottom of the screen. When the player is closed, user gets back to where he/she was without causing navigation as we just remove player page from player frame . The order in which you put the frames is important.

An additional benefit of this approach is that we can put additional content in HostPage which we need to display regardless of which page user is on. For Khan Academy app, I have added a background image, a global progress indicator and a offline indicator to show status about network activity and availability. I use a separate class to monitor long running operations and network connectivity changes to control visibility for these items.

As the app can be launched due to user starting the app or initiating search, the host page would have to be smart enough to handle that. This is something we do for root frame in default implementation which moves to HostPage. In my case, I created a few properties to handle this which HostPage used for navigation. Here is the code for HostPage.

    public sealed partial class HostPage : Page
    {
        static HostPage _instance;

        public bool LoadNavigationState { get; set; }
        public Type StartupScreen { get; set; }
        public string Parameter { get; set; }
        public static HostPage Current
        {
            get
            {
                return _instance;
            }
        }

        public HostPage()
        {
            _instance = this;

            this.InitializeComponent();
            this.Loaded += OnHostPageLoaded;
            this.Unloaded += HostPageUnloaded;

            this.DataContext = GlobalStatus.Current;
        }

        async void OnHostPageLoaded(object sender, RoutedEventArgs e)
        {
            //Create a Frame to act as the navigation context and associate it with a SuspensionManager key
            SuspensionManager.RegisterFrame(hostFrame, &quot;AppFrame&quot;);

            if (LoadNavigationState)
            {
                // Restore the saved session state only when appropriate
                await SuspensionManager.RestoreAsync();
            }

            if (hostFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the main page,
                if (!hostFrame.Navigate(StartupScreen, Parameter))
                {
                    throw new Exception(&quot;Failed to create initial page&quot;);
                }
            }
        }

        #region navigation methods
        public async Task Navigate(Type sourcePageType)
        {
            //navigate on the host frame
        }

        public async Task Navigate(Type sourcePageType, object parameter)
        {
            //navigate on the host frame
        }

        public Page GetCurrentPage()
        {
            return hostFrame.Content as Page;
        }
        #endregion

        #region player methods
        // Launches player for the passed video id with first playlist which contains the video. Used from search suggestion selection in search charm
        public async Task ShowPlayer(string youTubeId)
        {
            //navigate on the player frame
        }

        // Launches player for passed playlist and video ids. Used from search and downloads screens
        public async Task ShowPlayer(string playlistId, string youTubeId)
        {
            //navigate on the host frame
        }
        #endregion
    }

To wire this up, we assign a HostPage instance to current window content instead of a frame which we saw in first code snippet above. Notice that we also handle different scenarios in which app can be activated.

	Type pageType = null; string parameter = null;

	if (_args.Kind == ActivationKind.Launch)
	{
		pageType = typeof(MainPage);
	}
	else if (_args.Kind == ActivationKind.Search)
	{
		pageType = typeof(SearchPage);

		var searchArgs = _args as SearchActivatedEventArgs;

		if (searchArgs != null) parameter = searchArgs.QueryText;
	}

	Window.Current.Content = new HostPage
	{
		LoadNavigationState = _args.PreviousExecutionState == ApplicationExecutionState.Terminated,
		StartupScreen = pageType,
		Parameter = parameter
	};

And that should be it. With this approach, I avoided a number of navigations and customized the ones which I kept to distinguish the app behavior. This approach helped with improving navigation experience and performance in Khan Academy app. Hope this helps you in developing great apps. Chime away in comments if you have any feedback or questions.

Advertisements

Wallpaper Juggler: Downloads are not available from InterfaceLIFT

After Wallpaper juggler was described as cool at lifehacker, there has been a lot of download activity on the project page at codeplex. It seems after getting the app, everyone started downloading high res wallpapers from InterfaceLIFT which created tremendous load on their site. This apparently made folks at InterfaceLIFT mad and they block access to the site after downloading 10 or so wallpapers from the tool. You get following response once you are blocked.

Your access to InterfaceLIFT has temporarily been suspended due to misuse of our servers. Please review our Terms of Service for more information. Access will be restored in a couple days. To ensure that you access is not suspended again in the future, please disable any download accelerator plugins or software you may be running.

If you believe you have not violated the Terms of Service, please contact jeff [at} interfacelift {dot] com
Error code: xx.xxx.xx.xx

Ironically, even the “Terms of Service” page doesn’t come up once you are blocked. 🙂

Personally, I agree with this. There are costs associated to hosting a site like InterfaceLIFT and ads are one good way to support that which doesn’t happen when Juggler pulls those pretty images from the site. But hey, we still have another source right in juggler and we will find more. Only until other sources don’t treat us the same way.

Enjoy!

Anand

New version of Juggler: Wallpaper Changer for Windows (Updated)

UPDATE: Wallpaper downloads are not available from InterfaceLIFT anymore. Read details here.

Even though the first basic version of Juggler pretty much did what it was supposed to do, there were few ‘nice to have’ things which were missing. So I updated it as soon as I got some free time. 🙂

Feature set:

Here is the list of features (existing + new) which are now included.

  • Its coded and tested to work on Vista and XP SP2 with .Net 3.5. It should work (not tested though) on Server 2008/2003 as they borrow UI code base from Vista/XP. No words for any other OS.
  • It runs in background and can be controlled from System Tray.

  • It can be configured to:
      • start with Windows.
      • look for wallpapers in multiple folders including/excluding sub-folders. Best results when used with matching resolution wallpapers.
      • change wallpaper once every preferred duration between 1 min and 500 hrs. It will understand “000 Hrs/Mins 000 Min” format. It can interpret M/Min/Mins/Minutes and H/Hr/Hrs/Hour as Minutes and Hours respectively.
      • confirm before changing wallpaper
      • adjust image positioning for odd size wallpapers.

  • You can also change wallpaper manually by clicking ‘Juggle Now’ menu or by double clicking the icon.
  • It allows only one instance at a time.
  • It also displays the last updated wallpaper and the time stamp when the next change will occur.
  • It displays a confirmation dialog before automatically changing wallpaper. Its configurable to show a confirmation dialog and for how long the message should be displayed.

  • “Active” option to enable/disable wallpaper juggling while program is running.
  • Last changed wallpaper is a clickable menu which opens the wallpaper in windows explorer.
  • You can download stunning wallpapers from interfaceLIFT.com and WallpaperStock.com using “Download Wallpapers” screen right within juggler.

Downloads:

This version uses .Net 3.5. Also, the downloads are posted at codeplex.com.

  1. Download and install .Net framework v3.5 if its not installed already.
  2. Navigate to Juggler release page at codeplex to download application and/or source code. The downloads are in Files section.

Uninstall

To uninstall, just remove the files from your PC. There are no special steps required to uninstall this.

Implementation:

Most of the stuff is pretty straightforward. It uses Timer class to invoke code to change wallpaper. The invoked code then uses Win APIs to do the job. But there are few things which may interest you.

  1. There are all kinds of anonymous method illustrations. You can also find basic as well as complex lambda expressions which, I think, are nothing but fancy anonymous methods invented for LINQ.
  2. It also demonstrates how to use System.Threading.Mutex to find and avoid if user is trying to run multiple instance of same application. This is the simplest and most accurate approach to create single instance application in C# (VB has some out of the box functionality). There is a catch in this approach which may disqualify this in certain cases.
  3. While implementing single instance functionality, I needed to warn user that the application is already running. Simplest way is to just display a message box. But I thought if the already running (first) instance can get a notification from second instance then it can show a balloon tip which will tell user where exactly the application is running. This may sound simple but it’s not.
    1. As both instance are separate processes, we are talking about inter process communication (IPC) here.
    2. There is no other way than remoting in .Net 1.0-2.0 for IPC. And opening a tcp(forget about http) port seems overkill to me in this situation.
    3. So I chose a self hosted simple wcf service with named pipe binding. May be I’ll post about using named pipes in WCF for IPC later some time.

As always, please use it at your own risk. Feel free to rant in comments. 🙂

Enjoy!

Juggler : Automatic Wallpaper Changer for Windows

If you have followed previous posts, you should already have a huge collection of wallpapers. If you don’t, go to previous posts to get details on how to download high quality wallpapers.

Vladstudio Wallpaper Collection
Spectacular Wallpapers from InterfaceLIFT, Tool to download them all

Now the next thing is to use these wallpapers. Going back to “Personalization” settings every time is really not something I’d prefer. So again, I coded up a small and handy app which can do this for us.

  • It runs in background and can be controlled from System Tray.
  • It can be configured to
      • start with Windows.
      • look for wallpapers in a specific directory. Best way to use this is to dump all wallpapers matching your resolution in one folder and configure to use that folder.
      • change wallpaper once in half, one or two hrs.
  • You can also manually change wallpaper by using Juggle menu or double clicking the icon.
  • You’ve got the source. So, change it if you want more features. 🙂

And that’s pretty much it. I’m using it on Vista. It will NOT work on XP (or anything other than Vista for that matter) as XP doesn’t support JPEG images as wallpapers and that’s what I am using. I may update it later if I could spare some time. You can get a tool which may work with XP at Coding4Fun. And if you want you can always change the code [link below]. Updated to work with Vista and XP.

Remember, it’s not fully tested or final app. I just coded and left it when it started doing what I wanted. So, no negative testing. 🙂

Juggler.exe.zip [Independent Executable]

Juggler.source.zip [Source Code, VS 2005]

Links disabled for outdated app. Follow the link below for updated links.

Enjoy!

UPDATE: I’ve updated this app with fixes and more features. Please navigate to the detailed post for description and download.

Spectacular Wallpapers from InterfaceLIFT, Tool to download them all

InterfaceLIFT is one of the best source for high resolution wallpapers out there. A lot of artists post their work on this site. Thousands of wallpapers are available to browse and download but it’s annoying to click on each one of them and save manually.

I though it would be good if I can automate this or some kind of tool that can do this for me. So yesterday night, without wasting anymore time, I decided to code one up.

Its simple. Choose the resolution you want, choose the location to want to save images to and hit “Search and Download Wallpapers”. You may also want to change the sorting to Ratings or Downloads to get community rated wallpapers first.

And here is how it works: On the website, interfacelift uses javascript to prepare the links for actual images. I checked the HTML source and thought I could exploit that approach.

  • It gets the HTML markup for first page.
  • Search for javascript method calls and get the unique identifiers for wallpapers on current page and prepare a list of actual image names.
  • Loop through all images on current page.
  • Download one at a time and save to local disk.
  • Get markup for the the next page.
  • Go to step 2.
  • Keep doing this until user hits Stop or we run out of pages.

And that’s it. Its pretty simple. I ran it on Vista and XP against .Net 2.0. You can download the independent executable and/or source (C#). See update below to download.

WallpaperCrawler.exe.zip [Independent Executable]

WallpaperCrawler.source.zip [Complete Source Code, VS 2005]

I know it can be enhanced or made more robust. But hey I just coded it in couple of hours for the functionality I needed. I haven’t even tested it properly but it did what I was looking for.

Enjoy!

PS. By no means its a tested and final app. I don’t take any responsibilities for the issues you may run into by using this.

Update: This program is now part of Juggler (another app I created for changing wallpapers). Please navigate to the description page to download the latest version.