Avalon + Indigo = Magic

At PDC 2005, Chris Sells and Doug Purdy gave a talk entitled “Avalon + Indigo = Magic”, in which they built a smart client app using Avalon and Indigo and showed off some of the fun integration points between the two. They also had a bit of a comedy act going on, as was only fitting for the last day of a long and meaty conference.

They had this great idea that they would show a human example of how the combination of Avalon and Indigo is better than the sum of its parts, so they somehow convinced me and Kenny to get up on stage with them and discuss our recent engagement. Chris immortalized this moment on his blog:

I’ve got a developer from Indigo and a PM from Avalon in the front row that we bring up on stage because they’ve just gotten engaged and we made them kiss to show off the power of Avalon and Indigo integration, I announce that I got ordained on two separate internet churches the night before in case they wanted me to marry them on the spot. They politely decline, but the audience eats it up.

Well, I’m sure Chris will be very proud, because Kenny and I are giving a little Avalon + Indigo talk of our own next week. We’ll be at Florida Institute of Technology, and we’re going to show a group of CS students how to write a multiplayer networked game using Avalon and Indigo. In 45 minutes. So, uh, if you happen to be an FIT student and a reader of my blog (are there any of you out there?), come on by. :)

Implementing Tabbed Browsing Using Island Frame

In my last post, I introduced a WPF feature that we affectionately call “Island Frame” (an Island Frame is actually a Frame which has Frame.JournalOwnership = JournalOwnership.OwnsJournal). One of my favorite applications of this feature is that it is now very easy to build UI that uses a tabbed navigation model.

Tabbed browsing using 3 Island Frames

The basic XAML for this is as follows:

<Window x:Class="BeautifulIsland.Window1"
    xmlns=
    "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Tabbed Browsing Sample"
    Height="400"
    Width="500"
    >
  <TabControl>
    <TabItem>
      <TabItem.Header>
      <Binding ElementName="TabFrame1" Path="Content.Title" />
      </TabItem.Header>
      <Frame Name="TabFrame1" JournalOwnership="OwnsJournal" 
            Source="page1.xaml" />
    </TabItem>
    <TabItem>
      <TabItem.Header>
        <Binding ElementName="TabFrame2" Path="Content.Title" />
      </TabItem.Header>
      <Frame Name="TabFrame2" JournalOwnership="OwnsJournal" 
           Source="page2.xaml" />
    </TabItem>
    <TabItem>
      <TabItem.Header>
        <Binding ElementName="TabFrame3" Path="Content.Title" />
      </TabItem.Header>
      <Frame Name="TabFrame3" JournalOwnership="OwnsJournal" 
            Source="page3.xaml" />
    </TabItem>
  </TabControl>
</Window>

The notable parts of this XAML are as follows:

  • I have a TabControl with three TabItems, each of whose only child is a Frame.
  • For each Frame, I have set JournalOwnership = JournalOwnership.OwnsJournal.
  • I did not need to set NavigationUIVisibility because the default value is “Automatic”, which means that navigation UI will be shown if the Frame has its own Journal, and will not be shown if it doesn’t.
  • I used databinding to bind the TabItem.Header property to the title of the Page hosted in the Frame.

Obviously this sample is simplified. In a real tabbed browsing application, you would want to provide a mechanism for creating new tabs and opening content in those tabs. But that’s the easy part. :)

You can download the source for this sample here. It works on the February CTP.

“Island Frame”

You may have noticed in the past that the WPF Frame element didn’t work very well when hosted in a Window (it worked just fine in a NavigationWindow or in a Page hosted in the browser). The Frame could navigate to content, but there was no way to navigate forward or back, as you could do with a Frame hosted in a NavigationWindow. This wasn’t just because there was no navigation UI associated with the Frame, but because the Frame had no Journal of its own; that is, there was no bookkeeping mechanism to record the navigations.

The reason that Frame didn’t have a Journal is somewhat historical — WPF Frames don’t have their own Journals because HTML frames don’t have their own travel logs. In HTML, a frame’s navigation history is recorded by the travel log associated with the top-level page. That is why clicking on the back and forward buttons on the browser may cause either the top-level browser window or a frame to navigate — it depends on what is at the top of the travel log’s “back stack”. WPF Frames work the same way for consistency, and in most cases, this is the behavior people want.

But there were a few holes in this story. In particular:

  • What if you wanted to host a Frame inside a (non-navigation) Window, to provide a region of navigable content on a page?
  • What if you wanted to have Frames whose navigation was independent of the top-level navigation container?

The answer to both of these questions was the same: roll your own. And rolling your own wasn’t easy.

To address this, we added a new property to Frame that enables Frame to have its own Journal. The property is called JournalOwnership, and there is a JournalOwnership enum with three values: Automatic, OwnsJournal, and UsesParentJournal. These properties behave as follows:

  • Automatic – Whether or not this Frame has its own Journal depends on its parent. If the Frame is hosted by a Frame or NavigationWindow, it behaves as though UsesParentJournal was set. If it is not hosted by a Frame or NavigationWindow, it behaves as though OwnsJournal was set.
  • OwnsJournal – The Frame has its own Journal which operates independent of the hosting navigator’s Journal (if it has one).
  • UsesParentJournal – The Frame’s JournalEntries are merged into the hosting navigator’s Journal. If the hosting navigator does not have a Journal, then no journaling occurs for this Frame.

Around here, we started affectionately calling a Frame that has its own Journal an “Island Frame”. The name stuck, and now everyone on my team uses it (and it’s much easier than saying “a Frame that has Frame.JournalOwnership=JournalOwnership.OwnsJournal”).

The other detail that falls out of providing this property is that you may want to provide some navigation UI on an independent Frame, to control the navigations that occur inside that Frame. For this, we added a NavigationUIVisibility property on Frame, whose default value will show navigation UI if the Frame has its own Journal, and will not show navigation UI if the Frame shares a Journal with a parent navigator.

If this all sounds somewhat obscure and theoretical, hopefully my next post (a cool application of Island Frames) will help illuminate the possibilities a little better…