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…

Wireless in Berkeley

I’m flying down to Berkeley this afternoon to work Cal’s Internship & Summer Job Fair tomorrow. [Opportunistic Recruiting Plug: If you’re a Cal student with passion for software and you’re looking for an internship or full-time opportunity, you should come by and chat. Even though it’s primarily an internship career fair, we have plenty of full-time opportunities as well.]

Vidya is going to be there representing her employer, and Kenny is coming along because we’ve been looking for an excuse to go to Berkeley and have a yummy dinner. While Kenny is there, he is going to be working remotely. I remembered from the last time we were in Berkeley that it was a little bit difficult to find places with wireless internet (our hotel had wireless access, but only in the lobby). So I did a little web search.

The on-campus wireless network, AirBears, is only open to current faculty and students. No love for alumni. Which basically leaves local coffee shops. I found two websites with listings of cafes with wi-fi in Berkeley. The lists are short, possibly out of date, and include few places that are all that close to campus. And for some of the listings, it seems that the “free wi-fi” they refer to is just AirBears, which we can’t access.

I thought this was odd, as I can barely throw a stone in Seattle without hitting a cafe that has wi-fi. I think there are at least four on our little 15th Ave strip right behind our condo. Kenny was surprised too:

Kenny: Didn’t Berkeley, like, invent the Internet?
Lauren: No, silly, that was Al Gore.

Update 2/23/06, 10:04PM: It turns out that Yali’s is a good place for wireless.

Non-Consecutive Presidential Terms

The PI ran a story today about the difficulties of sharing a name with a president. My college roommate could tell you all about that one. She’s just hoping that Grover Cleveland, age 42, of Seattle, doesn’t come after her for stealing his email address.

You Can Sleep When You’re 30

A few months ago, Kenny and I were driving on Denny Way, and we saw a billboard for some energy drink that said, “You can sleep when you’re 30.” At that point, Kenny got very excited about his upcoming 30th birthday, thinking that he’d finally be able to get some well-deserved rest.

It’s been a little over a month since that fateful birthday, and I’m not quite sure that has happened (what with multiple all-nighters pulled in South America and Miami, and his trip to Atlanta a little over a week ago). But we had fun taking him out for a belated birthday bash at the Chapel this past weekend. All the photos are here, a few highlights below.