Browse DevX
Sign up for e-mail newsletters from DevX


Learn Navigation in "Avalon"—the Windows Presentation Foundation

Windows Presentation Foundation, the nascent framework for building next-generation user interfaces in Windows Vista, is available now in beta, but you don't have to wait for Vista to get started. In this second article in the series, we show you how to use the new navigation model.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

n my last article, I talked about the different types of applications one can build using Microsoft's new Avalon UI framework as well as XAML, the language used to build Avalon applications. In this article, I will continue my foray into Avalon application development; this time I will focus on the "navigational" nature of Avalon applications. (Note: Since that first article, Microsoft bestowed a permanent name on Avalon: Windows Presentation Framework. Throughout the rest of this article, we will refer to Avalon as WPF.)

As discussed in the earlier article, WPF applications can be broadly classified into two types:

  • Standalone Windows applications
  • Applications containing a series of pages
The latter type, where a user navigates among a series of pages, is more common. The navigational element in applications of this type is provided by the Hyperlink control. However the Hyperlink control presents several shortcomings such as difficulty in passing values to the destination page, the ability to know when a destination page has returned to the calling page, as well as difficulty in passing a value back to the calling page.

And so, in this article I'll show you how to get around those problems; I'll create an WPF application that navigates from one page to another, passing values between pages.

What You Need
  • Microsoft Visual Studio 2005
  • Microsoft Pre-Release Software: Windows Presentation Framework ("Avalon") and Windows Communication Framework ("Indigo") Beta1 RC
  • The WinFX SDK

Basics of Navigation
Before I dive into the theory of page navigation in WPF, I'd like to start by writing a simple application, explaining key features as I go. First, using Visual Studio 2005, create a new WPF project (I'm using VB.NET) and name the application AvalonNavigation (see Figure 1).

Figure 1. Getting Started: Start by creating a new WPF Navigation application in Visual Studio 2005.
Figure 2. Content in Solution Explorer: Here's what you should see after creating your new project.

Your Solution Explorer should look like Figure 2.

First I'll examine the MyApp.xaml file in the project. An Avalon Navigation application starts with the <NavigationApplication> root element. The StartupUri attribute specifies the page to be loaded when the application is executed; in this case the startup page is Page1.xaml:

<NavigationApplication x:Class="MyApp" xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005" StartupUri="Page1.xaml" > <NavigationApplication.Resources> </NavigationApplication.Resources> </NavigationApplication>

The content of Page1.xaml is shown below:

<Page x:Class="Page1" xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005" > <Grid> </Grid> </Page>

Now I'll add a new Avalon PageFunction item to the project: right-click on the project name in Solution Explorer and then select Add New Item…. and select the Avalon PageFunction template. Change the name of the page to Page2.xaml and click Add (see Figure 3).

Figure 3. Pagination: Add a new Avalon PageFunction item to the project.
Figure 4. Point A to Point B: At this point you can navigate from Page1.xaml to Page2.xaml.

Once Page2.xaml is added to the project, double-click on it to examine its content:

<PageFunction x:Class="Page2" x:TypeArguments="String" xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005" > <Grid> </Grid> </PageFunction>

An AvalonPageFunction is basically a WPF page, except that it can return a result (hence the PageFunction name) back to the calling page. Figure 4 shows how you can make use of Page2.xaml from Page1.xaml and then get the result returned by Page2.xaml.

There are four types of PageFunction pages:

  • BooleanPageFunction—returns a Boolean value; represented by the <BooleanPageFunction> element.
  • Int32PageFunction—returns a Integer value; represented by the <Int32PageFunction> element.
  • ObjectPageFunction—returns an object value; represented by the <ObjectPageFunction> element.
  • StringPageFunction—returns a string; represented by the <StringPageFunction> element.
In Page2.xaml, the root element is <PageFunction>, and not any of the four PageFunction pages listed above. The <PageFunction> is the generic version of the PageFunction pages and you indicate its type by the x:TypeArguments attribute (set to "String" in this example). If you wish to convert the page to any of the four types described above, say, the StringPageFunction, you can simply change the root element as shown (note that the x:TypeArguments attribute is removed):

<StringPageFunction x:Class="Page2" xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005" > <Grid> </Grid> </StringPageFunction>

For now, though, I'll stick to the original <PageFunction> root element.

Populate Page1.xaml with the following:

<Page x:Class="Page1" xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005" > <StackPanel> <Button Click="ButtonClick" HorizontalAlignment="Left" Margin="10,5,10,5" Width="120">Go to Page 2 </Button> <TextBlock Margin="10,5,10,5"> <Inline Name="txtResult"></Inline> </TextBlock> </StackPanel> </Page>

Basically, this page contains a Button control to load Page2.xaml (see Figure 5) and also a TextBlock control to display the result returned by Page2.xaml (note that the first time it is loaded the TextBlock control is not visible since it has no value).

Figure 5. Page1.xaml: The button control loads the second page of the application.
Figure 6. Page2.xaml: The second page of the application will ask for a user input that will be passed back to Page1.xaml.

In the code-behind of Page1.xamlPage1.xaml.vb—code the following:

Partial Public Class Page1 Inherits Page Private WithEvents p2 As Page2 Private Sub ButtonClick( _ ByVal sender As Object, _ ByVal e As RoutedEventArgs) Dim myApp As NavigationApplication Dim navWindow As NavigationWindow myApp = CType( _ System.Windows.Application.Current, _ NavigationApplication) navWindow = CType(myApp.MainWindow, _ NavigationWindow) p2 = New Page2 p2.InitializeComponent() navWindow.Navigate(p2) End Sub Private Sub return_handler( _ ByVal sender As Object, _ ByVal e As ReturnEventArgs(Of String)) _ Handles p2.Return txtResult.Text = "Returned: " & e.Result.ToString End Sub End Class

In Page1.xaml.vb, I added two methods:
  • ButtonClick—to service the clicking of the Button control
  • return_handler—to retrieve the result returned by Page2.xaml.
To navigate to a PageFunction page, you need to create an instance of the destination page, call its InitializeComponent() method, and then finally use the Navigate() method to navigate to that page. When the destination page function returns a value, you capture the data by servicing the Return event of the page. The returned result is retrieved via the ReturnEventArgs argument.

Now I'll populate Page2.xaml:

<PageFunction x:Class="Page2" x:TypeArguments="String" xmlns="http://schemas.microsoft.com/winfx/avalon/2005" xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005" > <StackPanel HorizontalAlignment="Left"> <TextBlock Margin="10,5,10,5"> What is your name?</TextBlock> <TextBox Name="txtName" Margin="10,5,10,5" Width="200"></TextBox> <Button Click="ButtonClick" Margin="10,5,10,5" Width="60" HorizontalAlignment="Left">Done</Button> </StackPanel> </PageFunction>

Page2.xaml will display a string of text followed by a TextBox and a Button control (see Figure 6). The intention here is to prompt the user to enter a name and then pass the name back to Page1.xaml.

Code the code-behind Page2.xaml.vb as follows:

Partial Public Class Page2 Inherits PageFunction(Of String) Private Sub ButtonClick( _ ByVal sender As Object, _ ByVal e As RoutedEventArgs) OnFinish(New ReturnEventArgs(Of _ String)(txtName.Text)) End Sub End Class

Note that the Page2 class inherits from the PageFunction class (matching the corresponding <PageFunction> element in the XAML page); you also need to specify the generic type (String, in this case).

When the Done button is clicked, you return the name entered using the OnFinish() method. The OnFinish() method will return the specified string back to the calling page (see Figure 7).

Figure 7. Flow of the Application: From page 1 to page 2 and back, grabbing a name along the way.
Figure 8. Back and Forward: You can examine the history of pages navigated through the application.

The navigational button in the application work as they would in a browser, taking you forward and back through visited pages (see Figure 8). The "untitled" in Figure 8 refers to Page2.xaml.

All pages that you have loaded are stored automatically in a stack data structure known as the journal. In some cases you might not want to record certain pages in the journal (such as preventing users from inadvertently navigating to a page that is part of a transaction, thereby disrupting the flow of the application). To manually remove a page from the journal, set the RemoveFromJournal property of the current page to "true":

' Page2.xaml Private Sub ButtonClick( _ ByVal sender As Object, _ ByVal e As RoutedEventArgs) Me.RemoveFromJournal = True OnFinish(New ReturnEventArgs(Of _ String)(txtName.Text)) End Sub

With that, Page2.xaml will not be visible in the navigational buttons (see Figure 9).
Figure 9. Not Back There: You can remove a page from the journal, which records the page history.

Passing Data into a Page
You have seen how a PageFunction page can pass a value back to the calling page using the OnFinish() method. What about passing a value from a calling page to a PageFunction page? In this case, you can do so by adding a constructor to the PageFunction page. Assuming you need to pass in values to Page2.xaml, you would need to add the following constructors:

Partial Public Class Page2 Inherits PageFunction(Of String) Public Sub New() End Sub Public Sub New(ByVal data As String) ' process the incoming data End Sub Private Sub ButtonClick( _ ByVal sender As Object, _ ByVal e As RoutedEventArgs) Me.RemoveFromJournal = True OnFinish(New ReturnEventArgs(Of _ String)(txtName.Text)) End Sub End Class

Note that you need to add the empty constructor or else you will get an error during compilation time. Also, you can add as many constructors as you wish, as long as they have different signatures.

In the calling page, to pass data into the destination page, you simply need to supply the necessary parameter:

p2 = New Page2("the string to pass into") p2.InitializeComponent() navWindow.Navigate(p2)

Thanks for your registration, follow us on our social networks to keep up-to-date