devxlogo

Learn Navigation in “Avalon”—the Windows Presentation Foundation

Learn Navigation in “Avalon”—the Windows Presentation Foundation

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 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:

                

The content of Page1.xaml is shown below:

                

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:

                

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 element.
  • Int32PageFunction?returns a Integer value; represented by the element.
  • ObjectPageFunction?returns an object value; represented by the element.
  • StringPageFunction?returns a string; represented by the element.

In Page2.xaml, the root element is , and not any of the four PageFunction pages listed above. The 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):

                

For now, though, I’ll stick to the original root element.

Populate Page1.xaml with the following:

                                             

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.xaml?Page1.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 SubEnd 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:

                  What is your name?               

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 SubEnd Class

Note that the Page2 class inherits from the PageFunction class (matching the corresponding 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.xamlPrivate Sub ButtonClick( _   ByVal sender As Object, _   ByVal e As RoutedEventArgs)    Me.RemoveFromJournal = TrueOnFinish(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 SubEnd 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)

Linear Navigation
Now that you've had an introduction to page navigation in WPF applications, it's time for some vital specifics. There are basically two main types of navigations in WPF:

  • Linear
  • Hierarchical
In a linear navigation, all pages are strung together and one loads after another. Figure 10 shows three pages all linked up in a linear fashion. For hierarchical navigation, the pages are linked in a tree-like structure. I will discuss hierarchical navigation in the next section.


Figure 10. Linear Navigation: The three pages of the application have a linear relationship.
?
Figure 11. Page1.xaml: Here's the first page of the new application, which will use linear navigation.

I'll now create a simple application to show how linear navigation works. Using Visual Studio 2005, create a new WPF (Avalon) Navigation application. With the default Page1.xaml page, add two more PageFunction pages and name them Page2.xaml and Page3.xaml. I will also take this chance to illustrate the different types of page function pages.

Populate Page1.xaml as follows:

                              Page 1                                 Enter your name                                                                  

Figure 11 shows how Page1.xaml will look like when loaded.

In the code-behind, Page1.xaml.vb, code the following:

Partial Public Class Page1    Inherits Page    Public WithEvents nextPage As Page2    '---used for preserving the name entered in this page    Private Shared _Name As String    Private Sub OnLoaded( _       ByVal sender As Object, _       ByVal e As RoutedEventArgs) Handles Me.Loaded        txtName.Text = _Name    End Sub    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)        '---navigate to next page---        If sender.Equals(btnNextPage) Then            '---save the name entered            _Name = txtName.Text            nextPage = New Page2            nextPage.InitializeComponent()            navWindow.Navigate(nextPage)        End If        '---exit the application---        If sender.Equals(btnExit) Then            System.Windows.Application.Current.Shutdown()        End If    End Sub    Private Sub nextPage_Return( _       ByVal sender As Object, _       ByVal args As StringReturnEventArgs) Handles _       nextPage.Return        '---display the string entered in the next window        returnString.Text = "From Page 2: " & _           args.Result.ToString    End SubEnd Class

Here are the important points to note for this page:

  • The ButtonClick() method is fired when either the Next Page button or the Exit button is clicked.
  • If the Next Page button is clicked, the application navigates to Page2.xaml. If the Exit button is clicked, the application quits.
  • Before the application navigates to the Page2.xaml, the name entered by the user must be preserved using a shared variable. This is because when you navigate between pages (either programmatically or using the navigational buttons) the state of the page is not preserved. Therefore you need to maintain your own state.
  • The OnLoaded() method is fired whenever the page is loaded, so this is the place to set the value of controls to their original values/state.
  • The nextPage_Return() method is fired when Page2.xaml returns a value to it.

Populate Page2.xaml as follows:

                                 Page 2                                  Enter your company name                                                                                                            

Note that Page2.xaml is a StringPageFunction, which means it will return a string value back to the calling page. Figure 12 shows how Page2.xaml will look like when loaded.

Figure 12. Page2.xaml: Here's the second page of the application, which returns a string to the first page.

In the code-behind, Page2.xaml.vb, code the following:

Partial Public Class Page2    Inherits StringPageFunction    Public WithEvents nextPage As Page3'---used for preserving the company name entered ' in this page    Private Shared _CompanyName As String    Private Sub OnLoaded(ByVal sender As Object, _       ByVal e As RoutedEventArgs) Handles Me.Loaded        txtCompanyName.Text = _CompanyName    End Sub    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)        '---navigate to next page---        If sender.Equals(btnNextPage) Then            _CompanyName = txtCompanyName.Text            nextPage = New Page3            nextPage.InitializeComponent()            navWindow.Navigate(nextPage)        End If        If sender.Equals(btnPreviousPage) Then            OnFinish(txtCompanyName.Text)        End If        '---exit the application---        If sender.Equals(btnExit) Then            System.Windows.Application.Current.Shutdown()        End If    End Sub    Private Sub nextPage_Return(ByVal sender As Object, _       ByVal args As BooleanReturnEventArgs) Handles _       nextPage.Return        '---display the string entered in the next window        returnString.Text = "From Page 3: " & _           args.Result.ToString    End SubEnd Class

Basically, Page2.xaml.vb is very similar to Page1.xaml.vb, except that when the Previous Page button is clicked, it returns the company name entered back to the calling page using the OnFinish() method. The Next Page button will cause the application to navigate to Page3.xaml.

Finally, populate Page3.xaml as follows:

                              Page 3                                 Single?                     True            False                                                                     

Note that Page3.xaml is a BooleanPageFunction, which returns a Boolean result back to the calling page. Figure 13 shows how Page3.xaml will look like when loaded.


Figure 13. Page3.xaml: The final page in this three-page application asks for a simple Boolean, which is returned to page 2.
?
Figure 14. Flow of the Application: The three pages of this linear application go forward through pages 1, 2, and 3, sequentially, and then back in reverse order.

In the code-behind, Page3.xaml.vb, code the following:

Partial Public Class Page3    Inherits BooleanPageFunction'---used for preserving the maritial status ' selected in this page    Private Shared _Status As Boolean    ' Sample event handlers:    Private Sub OnLoaded(ByVal sender As Object, _       ByVal e As RoutedEventArgs) Handles Me.Loaded        If _Status = True Then            rbTrue.IsChecked = True        Else            rbFalse.IsChecked = True        End If    End Sub    Private Sub ButtonClick(ByVal sender As Object, _       ByVal e As RoutedEventArgs)        If sender.Equals(btnPreviousPage) Then            If rbTrue.IsChecked = True Then                _Status = True            Else                _Status = False            End If            OnFinish(_Status)        End If        '---exit the application---        If sender.Equals(btnExit) Then            System.Windows.Application.Current.Shutdown()        End If    End SubEnd Class

Now you can run the application and trace the flow (see Figure 14).

Hierarchical Navigation

Figure 15. Hierarchy of Three: This hierarchical navigation application has pages 2 and 3 returning always to page 1.

In a WPF app with hierarchical navigation, users can branch out to other pages based on their choice. Figure 15 shows a typical hierarchical navigation application. Note that each page can branch out to as many pages as required, but as each page exits it will return to its calling page.

Figure 16 shows an example implementation of the hierarchical navigation application shown in Figure 15. In the first page, the user can enter his name and to select his country, he can click on the Select Country button to load the second page. To select his industry, he can click on the Select Industry button to load the third page. The pages for selecting country and industry both return to Page 1.

Once the selections are made, the results are shown on the main page, which serves as the master page for the entire application (see Figure 17).

Listing 1 is the code?broken into six sections?of the code for the three pages. Please be careful to heed the breaks in this code. Subheads are given.


Figure 16. Two Child Pages: An implementation of a hierarchical navigation application is shown.
?
Figure 17. Back to the Beginning: Data entered from the other pages are shown in Page1.xaml.

In this article, you have seen an overview, and two simple drill-downs, on how to paginate applications in the Windows Presentation Framework (Avalon). You have seen the five types of PageFunction pages available. In the next article, I will show you how data-binding is done in Avalon.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist