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.

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

TFS : The Real Picture

For every technology, there is the theory you learn and then you have to implement it in real world. You can find books for theory but for real implementation, there are hardly any. For TFS though, there is a guide on CodePlex which describes how TFS should be used in real projects. Excerpts from the site:

This guide shows you how to make the most of Team Foundation Server. It starts with the end in mind, but shows you how to incrementally adopt TFS for your organization. It’s a collaborative effort between patterns & practices, Team System team members, and industry experts

patterns & practices: Team Development with Visual Studio Team Foundation Server

Trigger Gotchas

While working with triggers, I noticed a couple of unexpected behaviors in SQL Server.

1. Let’s say you have an INSERT statement something like this:

INSERT INTO SomeTable VALUES (Col1, Col2)
SELECT Val1, Val2 FROM SourceTable WHERE 1=0

The select statement is never going to return any rows due to the where clause (always false) and nothing would be inserted into SomeTable. So, if there is an AFTER INSERT trigger on SomeTable, what do you think about it. Should it fire? Remember, there is not going to be any insert.  If your answer is NO ( as it was mine and few others too), think again. Because it DOES fire.

2. Another situation. Lets have the same INSERT statement but without any WHERE clause. So the query will be something like this:

INSERT INTO SomeTable VALUES (Col1, Col2)
SELECT Val1, Val2 FROM SourceTable

In this case if SourceTable has 100 records and there is an AFTER INSERT trigger on SomeTable, how many times that trigger should fire. Remember, there are going to be 100 new rows in SomeTable. If you answer is 100 (as it was mine too, again), check Books Online once more. The trigger is going to fire ONLY once.

One thing to note in both situations is that the LOGICAL tables have the correct information. That said, INSERTED logical table is going to be empty in first example and it will have 100 rows in second one. So, these logical table are your best bet. This behavior is same for DELETE and UPDATE after trigger too. Means, if you execute a delete or update statement which will not affect any row, triggers will fire regardless and it will fire once per statement NOT per affected row.

Hope this will helps. Enjoy!

Coder to Developer

I read something which was fun, reality and worth reading. Joel Spolsky wrote foreword to Mike Gunderloy’s book, “Coder to Developer”. Here it follows as is:

You know what drives me crazy?

“Everything?” you ask. Well, OK, some of you know me a bit too well by now.

But seriously, folks, what drives me crazy is that most software developers don’t realize just how little they know about software development.

Take, for example, me.

When I was a teenager, as soon as I finished reading Peter Norton’s famous guide to programming the IBM-PC in Assembler, I was convinced that I knew everything there was to know about software development in general. Heck, I was ready to start a software company to make a word processor, you see, and it was going to be really good. My imaginary software company was going to have coffee breaks with free donuts every hour. A lot of my daydreams in those days involved donuts.

When I got out of the army, I headed off to college and got a degree in Computer Science. Now I really knew everything. I knew more than everything, because I had learned a bunch of computer-scientific junk about linear algebra and NP completeness and frigging lambda calculus which was obviously useless, so I thought they must have run out of useful things to teach us and were scraping the bottom of the barrel.

Nope. At my first job I noticed how many things there are that many Computer Science departments are too snooty to actually teach you. Things like software teamwork. Practical advice about user interface design. Professional tools like source code control, bug tracking databases, debuggers and profilers. Business things. Computer Science departments in the most prestigious institutions just won’t teach you this stuff because they consider it “vocational,” not academic; the kind of thing that high school dropouts learn at the local technical institute so they can have a career as an auto mechanic, or an air-conditioner repairman, or a (holding nose between thumb and forefinger) “software developer.”

I can sort of understand that attitude. After all, many prestigious undergraduate institutions see their goal as preparing you for life, not teaching you a career, least of all a career in a field that changes so rapidly any technologies you learn now will be obsolete in a decade.

Over the next decade I proceeded to learn an incredible amount about software development and all the things it takes to produce software. I worked at Microsoft on the Excel team, at Viacom on the web team, and at Juno on their email client. And, you know what? At every point in the learning cycle, I was completely convinced that I knew everything there was to know about software development.

“Maybe you’re just an arrogant sod?” you ask, possibly using an even spicier word than “sod.” I beg your pardon: this is my foreword; if you want to be rude write your own damn foreword, tear mine out of the book, and put yours in instead.

There’s something weird about software development, some mystical quality, that makes all kinds of people think they know how to do it. I’ve worked at dotcom-type companies full of liberal arts majors with no software experience or training who nevertheless were convinced that they knew how to manage software teams and design user interfaces. This is weird, because nobody thinks they know how to remove a burst appendix, or rebuild a car engine, unless they actually know how to do it, but for some reason there are all these people floating around who think they know everything there is to know about software development.
Anyway, the responsibility is going to fall on your shoulders. You’re probably going to have to learn how to do software development on your own. If you’re really lucky, you’ve had some experience working directly with top notch software developers who can teach you this stuff, but most people don’t have that opportunity.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
He praises the book here in one paragraph and then continues on the topic :).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Being a software developer means you can take a concept, build a team, set up state of the art development processes, design a software product, the right software product, and produce it. Not just any software product: a high quality software product that solves a problem and delights your users. With documentation. A web page. A setup program. Test cases. Norwegian versions. Bokmål and Nynorsk. Appetizers, dessert, and twenty seven eight-by-ten color glossy photographs with circles and arrows and a paragraph on the back of each one explaining what each one was. (Apologies to Arlo Guthrie.)

And then, one day, finally, perhaps when it’s too late, you’ll wake up and say, “Hmm. Maybe I really don’t know what it really takes to develop software.” And on that day only, and not one minute before, but on that day and from that day forward, you will have earned the right to call yourself a software developer. In the meantime, all is not lost: you still have my blessing if you want to eat donuts every hour.