Información general sobre navegación

Windows Presentation Foundation (WPF) admite la navegación de tipo explorador que se puede usar en dos tipos de aplicaciones: aplicaciones independientes y aplicaciones de explorador XAML (XBAP). Para empaquetar el contenido para la navegación, WPF proporciona la clase Page. Puede navegar desde una clase Page a otra mediante declaración con el uso de Hyperlink, o mediante programación, con el uso de NavigationService. WPF usa el diario para recordar las páginas desde las que se navegó o para volver a ellas.

Page, Hyperlink, NavigationService y el diario constituyen la base de la compatibilidad de la navegación que ofrece WPF. Esta introducción describe estas características detalladamente antes de abordar la compatibilidad con la navegación avanzada que incluye navegación a objetos y a archivos Extensible Application Markup Language (XAML) y HTML.

Nota

En este tema, el término "explorador" hace referencia solo a los exploradores que pueden hospedar aplicaciones WPF, lo que actualmente incluye Microsoft Internet Explorer y Firefox. En los casos en que las características específicas de WPF solo sean compatibles con un explorador concreto, se hace referencia a la versión del explorador.

En este tema se proporciona información general sobre las funcionalidades de navegación clave de WPF. Estas funcionalidades están disponibles tanto para aplicaciones independientes como XBAP, aunque este tema las presenta dentro del contexto de XBAP.

Nota

En este tema no se explica cómo crear e implementar XBAP. Para obtener más información sobre XBAP, consulte Información general sobre las aplicaciones de explorador XAML de WPF.

En esta sección se describen y se muestran los siguientes aspectos de navegación:

Implementación de una página

En WPF, puede navegar a varios tipos de contenido, entre los que se incluyen objetos .NET Framework, objetos personalizados, valores de enumeración, controles de usuario y archivos XAML y HTML. Sin embargo, verá que la manera más cómoda y común de empaquetar contenido es mediante Page. Además, Page implementa características específicas de la navegación para mejorar su apariencia y simplificar el desarrollo.

Con Page, puede implementar mediante declaración una página navegable de contenido XAML con el uso de marcación como se muestra a continuación.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />

La clase Page que se implementa en la marcación XAML tiene Page como elemento raíz y requiere la declaración de espacio de nombres WPF XML. El elemento Page incluye el contenido al que quiere navegar y que quiere mostrar. Para agregar contenido, establezca el elemento de la propiedad Page.Content, tal como se muestra en la marcación siguiente.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Page.Content>
    <!-- Page Content -->
    Hello, Page!
  </Page.Content>
</Page>

Page.Content solo puede contener un elemento secundario; en el ejemplo anterior, el contenido es una sola cadena, "Hello, Page!" En la práctica, normalmente usará un control de diseño como elemento secundario (vea Diseño) para incluir y redactar el contenido.

Los elementos secundarios de un elemento Page se consideran como el contenido de Page y, por lo tanto, no es necesario utilizar la declaración explícita Page.Content. La marcación siguiente es el equivalente declarativo del ejemplo anterior.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <!-- Page Content -->
  Hello, Page!
</Page>

En este caso, Page.Contentse establece automáticamente con los elementos secundarios del elemento Page. Para obtener más información, consulte Modelo de contenido de WPF.

La clase Page solo de marcación es útil para mostrar contenido. Sin embargo, Page también puede mostrar los controles que permiten a los usuarios interactuar con la página, y puede responder a la interacción del usuario al controlar eventos y llamar a la lógica de la aplicación. La clase Page interactiva se implementa mediante una combinación de marcación y código subyacente, como se muestra en el ejemplo siguiente.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.HomePage">
  Hello, from the XBAP HomePage!
</Page>
using System.Windows.Controls;

namespace SDKSample
{
    public partial class HomePage : Page
    {
        public HomePage()
        {
            InitializeComponent();
        }
    }
}

Imports System.Windows.Controls

Namespace SDKSample
    Partial Public Class HomePage
        Inherits Page
        Public Sub New()
            InitializeComponent()
        End Sub
    End Class
End Namespace

Para permitir que un archivo de marcación y un archivo de código subyacente funcionen juntos, se necesita la configuración siguiente:

  • En el marcado, el elemento Page debe incluir el atributo x:Class. Cuando se compila la aplicación, la existencia de x:Class en el archivo de marcación provoca que Microsoft Build Engine (MSBuild) cree una clase partial que deriva de Page y tiene el nombre especificado por el atributo x:Class. Para esto es necesario agregar una declaración de espacio de nombres XML para el esquema XAML (xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"). La clase generada partial implementa InitializeComponent, que se llama para registrar los eventos y establecer las propiedades que se implementan en la marcación.

  • En el código subyacente, la clase debe ser una instancia de partial con el mismo nombre especificado mediante el atributo x:Class en el marcado, y debe derivarse de Page. Esto permite que el archivo de código subyacente se asocie a la clase partial que se genera para el archivo de marcación cuando se compila la aplicación (consulte Compilar una aplicación de WPF).

  • En el código subyacente, la clase Page debe implementar un constructor que llame al método InitializeComponent. InitializeComponent se implementa mediante la clase partial generada por el archivo de marcado para registrar eventos y establecer las propiedades que se definen en el marcado.

Nota

Cuando se agrega una nueva instancia de Page al proyecto con Visual Studio, se implementa Page mediante el marcado y el código subyacente, e incluye la configuración necesaria para crear la asociación entre los archivos de marcado y código subyacente, como se describe aquí.

Una vez tenga una clase Page, podrá navegar a ella. Para especificar la primera clase Page a la que navega una aplicación, debe configurar Page de inicio.

Configuración de una página de inicio

Las aplicaciones XBAP requieren que se hospede una determinada cantidad de infraestructuras de aplicaciones en un explorador. En WPF, la clase Application forma parte de una definición de aplicación que establece la infraestructura de aplicación necesaria (consulte Información general sobre la administración de aplicaciones).

Una definición de aplicación se suele implementar mediante marcación y código subyacente, con el archivo de marcación configurado como un elemento MSBuild ApplicationDefinition. A continuación, puede ver una definición de aplicación para una aplicación XBAP.

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.App" />
using System.Windows;

namespace SDKSample
{
    public partial class App : Application { }
}

Imports System.Windows

Namespace SDKSample
    Partial Public Class App
        Inherits Application
    End Class
End Namespace

La aplicación XBAP puede usar su definición de aplicación para especificar una clase Page de inicio, que es la clase Page que se carga automáticamente cuando se inicia la aplicación XBAP. Para ello, establezca la propiedad StartupUri con el identificador uniforme de recursos (URI) para la clase Page que quiera.

Nota

En la mayoría de los casos, Page se compila en una aplicación o se implementa con una aplicación. En estos casos, el URI que identifica una clase Page es un URI de paquete, que es un URI que se ajusta al esquema pack. Los URI de paquete se analizan con más detalle en Empaquetar URI en WPF. También puede navegar al contenido con el esquema HTTP, que se describe a continuación.

Puede establecer StartupUri mediante declaración en la marcación, como se muestra en el ejemplo siguiente.

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.App"
    StartupUri="PageWithHyperlink.xaml" />

En este ejemplo, el atributo StartupUri está establecido con un URI de paquete relativo que identifica HomePage.xaml. Cuando se inicia la aplicación XBAP, se navega a HomePage.xaml y se muestra automáticamente. Esto se presenta en la ilustración siguiente, que muestra la aplicación XBAP que se inició desde un servidor web.

Página XBAP

Nota

Para obtener más información sobre el desarrollo e implementación de aplicaciones XBAP, consulte Información general sobre las aplicaciones de explorador XAML de WPF e Implementar una aplicación de WPF.

Configuración del título, el ancho y el alto de la ventana host

Algo que quizás haya observado en la ilustración anterior es que el título del explorador y del panel de pestañas es el URI de la aplicación XBAP. Además de largo, el título no es atractivo ni informativo. Por este motivo, Page ofrece una forma de cambiar el título estableciendo la propiedad WindowTitle. Además, puede configurar el ancho y el alto de la ventana del explorador estableciendo WindowWidth y WindowHeight, respectivamente.

WindowTitle, WindowWidth y WindowHeight puede establecerse mediante declaración en la marcación, tal como se muestra en el ejemplo siguiente.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.HomePage"
    WindowTitle="Page Title"
    WindowWidth="500"
    WindowHeight="200">
  Hello, from the XBAP HomePage!
</Page>

El resultado se muestra en la ilustración siguiente.

Título, alto y ancho de la ventana

Una aplicación XBAP típica consta de varias páginas. La manera más sencilla de navegar de una página a otra es usar Hyperlink. Puede agregar mediante declaración una clase Hyperlink a Page mediante el elemento Hyperlink, que se muestra en la marcación siguiente.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  WindowTitle="Page With Hyperlink"
  WindowWidth="250"
  WindowHeight="250">
<Hyperlink NavigateUri="UriOfPageToNavigateTo.xaml">
  Navigate to Another Page
</Hyperlink>
</Page>

Un elemento Hyperlink requiere lo siguiente:

  • URI de paquete de Page al que se va a navegar, según lo especifica el atributo NavigateUri.

  • Contenido al que un usuario puede hacer clic para iniciar la navegación, como texto e imágenes (para averiguar el contenido que el elemento Hyperlink puede contener, consulte Hyperlink).

En la siguiente ilustración se muestra una aplicación XBAP con Page que tiene Hyperlink.

Página con hipervínculo

Como cabría esperar, si hace clic en Hyperlink, la aplicación XBAP navega a Page que identifica el atributo NavigateUri. Además, la aplicación XBAP agrega una entrada para la clase anterior Page en la lista Páginas recientes de Internet Explorer. Esto se muestra en la siguiente ilustración.

Botones Atrás y Adelante

Además de permitir la navegación de una clase Page a otra, Hyperlink también admite la navegación por fragmentos.

Navegación por fragmentos

La navegación por fragmentos es la navegación a un fragmento de contenido de la clase actual Page u otra clase Page. En WPF, un fragmento de contenido es el contenido que incluye un elemento con nombre. Un elemento con nombre es un elemento que tiene el conjunto de atributos Name. En la siguiente marcación, se muestra un elemento TextBlock con nombre que contiene un fragmento de contenido.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    WindowTitle="Page With Fragments" >
<!-- Content Fragment called "Fragment1" -->
<TextBlock Name="Fragment1">
  Ea vel dignissim te aliquam facilisis ...
</TextBlock>
</Page>

Para que una clase Hyperlink navegue a otro fragmento de contenido, el atributo NavigateUri debe incluir lo siguiente:

  • El URI de la clase Page con el fragmento de contenido al que debe navegar.

  • Un carácter "#".

  • Nombre del elemento de la clase Page que contiene el fragmento de contenido.

Un URI de fragmento tiene el formato siguiente.

PageURI#ElementName

A continuación, se muestra un ejemplo de Hyperlink que está configurado para navegar a un fragmento de contenido.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  WindowTitle="Page That Navigates To Fragment" >
<Hyperlink NavigateUri="PageWithFragments.xaml#Fragment1">
  Navigate To pack Fragment
</Hyperlink>
</Page>

Nota

En esta sección se describe la implementación de la navegación por fragmentos predeterminada en WPF. WPF también le permite implementar su propio esquema de navegación por fragmentos, que, en parte, requiere la administración del evento NavigationService.FragmentNavigation.

Importante

Puede navegar a fragmentos de páginas dinámicas XAML (archivos XAML de solo marcación con Page como elemento raíz) solo si se puede navegar por las páginas mediante HTTP.

Sin embargo, una página XAML dinámica puede navegar a sus propios fragmentos.

Mientras que Hyperlink permite que un usuario inicie la navegación a una clase Page concreta, el trabajo de buscar y descargar la página lo realiza la clase NavigationService. Básicamente, NavigationService proporciona la capacidad para procesar una solicitud de navegación en nombre del código de cliente, como Hyperlink. Además, NavigationService implementa compatibilidad de nivel superior para realizar un seguimiento de una solicitud de navegación e influir en ella.

Cuando se hace clic en Hyperlink, WPF llama a NavigationService.Navigate para buscar y descargar Page en el URI de paquete especificado. La clase Page descargada se convierte en un árbol de objetos cuyo objeto raíz es una instancia de la clase Page descargada. Una referencia al objeto raíz Pagese almacena en la propiedad NavigationService.Content. El URI de paquete del contenido al que se navegó se almacena en la propiedad NavigationService.Source, mientras que NavigationService.CurrentSource almacena el URI de paquete de la última página a la que se navegó.

Nota

Una aplicación WPF puede tener más de un elemento NavigationService activo actualmente. Para obtener más información, consulte la sección Hosts de navegación que se encuentra más adelante en este tema.

Navegación programática con el servicio de navegación

No es necesario que obtenga información sobre NavigationService si la navegación se implementa mediante declaración en la marcación con Hyperlink, porque Hyperlink usa NavigationService en su nombre. Esto significa que, siempre que el elemento primario directo o indirecto de Hyperlink sea un host de navegación (consulte Hosts de navegación), Hyperlink será capaz de encontrar y usar el servicio de navegación del host de navegación para procesar una solicitud de navegación.

Sin embargo, existen situaciones en las que es necesario usar NavigationService directamente, así como lo siguiente:

  • Cuando tenga que crear una instancia de Page con un constructor que no es sin parámetros.

  • Cuando tenga que establecer propiedades en Page antes de navegar a este.

  • Cuando la clase Page a la que se deba navegar solo se pueda determinar en tiempo de ejecución.

En estas situaciones, debe escribir código para iniciar la navegación mediante programación llamando al método Navigate del objeto NavigationService. Esto requiere la obtención de NavigationService.

Obtención de una referencia a NavigationService

Por motivos que se tratan en la sección Hosts de navegación, una aplicación WPF puede tener más de un NavigationService. Esto significa que su código debe encontrar una manera de buscar NavigationService, que normalmente es NavigationService que dirigía a la clase Page actual. Puede obtener una referencia a NavigationService llamando al método staticNavigationService.GetNavigationService. Para obtener el elemento NavigationService que navegó a una clase Page concreta, debe pasar una referencia a la clase Page como argumento del método GetNavigationService. El código siguiente muestra cómo obtener el elemento NavigationService para la clase Page actual.

using System.Windows.Navigation;
// Get a reference to the NavigationService that navigated to this Page
NavigationService ns = NavigationService.GetNavigationService(this);
' Get a reference to the NavigationService that navigated to this Page
Dim ns As NavigationService = NavigationService.GetNavigationService(Me)

Como acceso directo para buscar el elemento NavigationService de Page, Page implementa la propiedad NavigationService. Esto se muestra en el ejemplo siguiente.

using System.Windows.Navigation;
// Get a reference to the NavigationService that navigated to this Page
NavigationService ns = this.NavigationService;
' Get a reference to the NavigationService that navigated to this Page
Dim ns As NavigationService = Me.NavigationService

Nota

Page solo puede obtener una referencia a NavigationService cuando Page genera el evento Loaded.

Navegación programática a un objeto de página

En el ejemplo siguiente se muestra cómo se utiliza NavigationService para navegar mediante programación a Page. La navegación programática es necesaria porque solo se pueden crear instantáneas de la clase Page a la que se está navegando mediante un constructor único que no es sin parámetros. Page con el constructor que no es sin parámetros se muestra en la marcación y el código siguientes.

<Page
    x:Class="SDKSample.PageWithNonDefaultConstructor"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="PageWithNonDefaultConstructor">
  
  <!-- Content goes here -->
  
</Page>
using System.Windows.Controls;

namespace SDKSample
{
    public partial class PageWithNonDefaultConstructor : Page
    {
        public PageWithNonDefaultConstructor(string message)
        {
            InitializeComponent();

            this.Content = message;
        }
    }
}

Namespace SDKSample
    Partial Public Class PageWithNonDefaultConstructor
        Inherits Page
        Public Sub New(ByVal message As String)
            InitializeComponent()

            Me.Content = message
        End Sub
    End Class
End Namespace

Page que dirige a Page con el constructor que no es sin parámetros se muestra en la marcación y el código siguientes.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.NSNavigationPage">

  <Hyperlink Click="hyperlink_Click">
    Navigate to Page with Non-Default Constructor
  </Hyperlink>

</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace SDKSample
{
    public partial class NSNavigationPage : Page
    {
        public NSNavigationPage()
        {
            InitializeComponent();
        }

        void hyperlink_Click(object sender, RoutedEventArgs e)
        {
            // Instantiate the page to navigate to
            PageWithNonDefaultConstructor page = new PageWithNonDefaultConstructor("Hello!");

            // Navigate to the page, using the NavigationService
            this.NavigationService.Navigate(page);
        }
    }
}

Namespace SDKSample
    Partial Public Class NSNavigationPage
        Inherits Page
        Public Sub New()
            InitializeComponent()
        End Sub

        Private Sub hyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Instantiate the page to navigate to
            Dim page As New PageWithNonDefaultConstructor("Hello!")

            ' Navigate to the page, using the NavigationService
            Me.NavigationService.Navigate(page)
        End Sub
    End Class
End Namespace

Cuando se hace clic en Hyperlink de esta clase Page, la navegación se inicia mediante la creación de instantáneas de la clase Page a la que se debe navegar mediante el constructor que no es sin parámetros y una llamada al método NavigationService.Navigate. Navigate acepta una referencia al objeto al que dirigirá NavigationService, en lugar de un URI de paquete.

Navegación programática con Pack URI

Si necesita crear un URI de paquete mediante programación (por ejemplo, solo cuando puede determinar el URI de paquete en tiempo de ejecución), puede usar el método NavigationService.Navigate. Esto se muestra en el ejemplo siguiente.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.NSUriNavigationPage">
  <Hyperlink Click="hyperlink_Click">Navigate to Page by Pack URI</Hyperlink>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace SDKSample
{
    public partial class NSUriNavigationPage : Page
    {
        public NSUriNavigationPage()
        {
            InitializeComponent();
        }

        void hyperlink_Click(object sender, RoutedEventArgs e)
        {
            // Create a pack URI
            Uri uri = new Uri("AnotherPage.xaml", UriKind.Relative);

            // Get the navigation service that was used to
            // navigate to this page, and navigate to
            // AnotherPage.xaml
            this.NavigationService.Navigate(uri);
        }
    }
}

Namespace SDKSample
    Partial Public Class NSUriNavigationPage
        Inherits Page
        Public Sub New()
            InitializeComponent()
        End Sub

        Private Sub hyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Create a pack URI
            Dim uri As New Uri("AnotherPage.xaml", UriKind.Relative)

            ' Get the navigation service that was used to 
            ' navigate to this page, and navigate to 
            ' AnotherPage.xaml
            Me.NavigationService.Navigate(uri)
        End Sub
    End Class
End Namespace

Actualización de la página actual

Una clase Page no se descarga si tiene el mismo URI de paquete que el URI de paquete que se almacena en la propiedad NavigationService.Source. Para forzar a que WPF descargue la página actual de nuevo, puede llamar al método NavigationService.Refresh, tal como se muestra en el ejemplo siguiente.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.NSRefreshNavigationPage">
 <Hyperlink Click="hyperlink_Click">Refresh this page</Hyperlink>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace SDKSample
{
    public partial class NSRefreshNavigationPage : Page
    {

Namespace SDKSample
    Partial Public Class NSRefreshNavigationPage
        Inherits Page
        void hyperlink_Click(object sender, RoutedEventArgs e)
        {
            // Force WPF to download this page again
            this.NavigationService.Refresh();
        }
    }
}
        Private Sub hyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Force WPF to download this page again
            Me.NavigationService.Refresh()
        End Sub
    End Class
End Namespace

Hay muchas maneras de iniciar la navegación, tal como ha podido ver. Cuando se inicia la navegación, y mientras esta está en curso, puede realizar un seguimiento y dirigirla con los siguientes eventos que se implementan mediante NavigationService:

  • Navigating. Se produce cuando se solicita una nueva navegación. Puede utilizarse para cancelar la navegación.

  • NavigationProgress. Se produce periódicamente durante una descarga y ofrece información sobre el progreso de la exploración.

  • Navigated. Se produce cuando se ha encontrado la página y se ha descargado.

  • NavigationStopped. Se produce cuando se detiene la navegación (al llamar a StopLoading), o cuando se solicita una nueva navegación mientras hay una navegación en curso.

  • NavigationFailed. Se produce cuando se genera un error mientras se navega por el contenido solicitado.

  • LoadCompleted. Se produce cuando el contenido al que se navegó se carga y se analiza, y empieza a representarse.

  • FragmentNavigation. Se produce cuando se inicia la navegación a un fragmento de contenido, cosa que tiene lugar:

    • Inmediatamente, si el fragmento deseado está en el contenido actual.

    • Una vez se ha cargado el contenido de origen, si el fragmento deseado está en otro contenido.

Los eventos de navegación se generan en el orden que se muestra en la ilustración siguiente.

Diagrama de flujo de navegación de página

En general, estos eventos no afectan a Page. Es más probable que afecten a una aplicación y, por este motivo, estos eventos también los genera la clase Application:

Cada vez que NavigationService genera un evento, la clase Application genera el evento correspondiente. Frame y NavigationWindow ofrecen los mismos eventos para detectar la navegación en sus respectivos ámbitos.

En algunos casos,una clase Page podría estar interesada en estos eventos. Por ejemplo, es posible que Page administre el evento NavigationService.Navigating para determinar si se debe cancelar la navegación fuera de este o no. Esto se muestra en el ejemplo siguiente.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.CancelNavigationPage">
  <Button Click="button_Click">Navigate to Another Page</Button>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace SDKSample
{
    public partial class CancelNavigationPage : Page
    {
        public CancelNavigationPage()
        {
            InitializeComponent();

            // Can only access the NavigationService when the page has been loaded
            this.Loaded += new RoutedEventHandler(CancelNavigationPage_Loaded);
            this.Unloaded += new RoutedEventHandler(CancelNavigationPage_Unloaded);
        }

        void button_Click(object sender, RoutedEventArgs e)
        {
            // Force WPF to download this page again
            this.NavigationService.Navigate(new Uri("AnotherPage.xaml", UriKind.Relative));
        }

        void CancelNavigationPage_Loaded(object sender, RoutedEventArgs e)
        {
            this.NavigationService.Navigating += new NavigatingCancelEventHandler(NavigationService_Navigating);
        }

        void CancelNavigationPage_Unloaded(object sender, RoutedEventArgs e)
        {
            this.NavigationService.Navigating -= new NavigatingCancelEventHandler(NavigationService_Navigating);
        }

        void NavigationService_Navigating(object sender, NavigatingCancelEventArgs e)
        {
            // Does the user really want to navigate to another page?
            MessageBoxResult result;
            result = MessageBox.Show("Do you want to leave this page?", "Navigation Request", MessageBoxButton.YesNo);

            // If the user doesn't want to navigate away, cancel the navigation
            if (result == MessageBoxResult.No) e.Cancel = true;
        }
    }
}

Namespace SDKSample
    Partial Public Class CancelNavigationPage
        Inherits Page
        Public Sub New()
            InitializeComponent()

            ' Can only access the NavigationService when the page has been loaded
            AddHandler Loaded, AddressOf CancelNavigationPage_Loaded
            AddHandler Unloaded, AddressOf CancelNavigationPage_Unloaded
        End Sub

        Private Sub button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Force WPF to download this page again
            Me.NavigationService.Navigate(New Uri("AnotherPage.xaml", UriKind.Relative))
        End Sub

        Private Sub CancelNavigationPage_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            AddHandler NavigationService.Navigating, AddressOf NavigationService_Navigating
        End Sub

        Private Sub CancelNavigationPage_Unloaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            RemoveHandler NavigationService.Navigating, AddressOf NavigationService_Navigating
        End Sub

        Private Sub NavigationService_Navigating(ByVal sender As Object, ByVal e As NavigatingCancelEventArgs)
            ' Does the user really want to navigate to another page?
            Dim result As MessageBoxResult
            result = MessageBox.Show("Do you want to leave this page?", "Navigation Request", MessageBoxButton.YesNo)

            ' If the user doesn't want to navigate away, cancel the navigation
            If result = MessageBoxResult.No Then
                e.Cancel = True
            End If
        End Sub
    End Class
End Namespace

Si registra un controlador con un evento de navegación desde Page, al igual que en el ejemplo anterior, también debe anular el registro del controlador de eventos. Si no lo hace, pueden producirse efectos secundarios con relación a cómo la navegación WPF recuerda la navegación de Page con el diario.

Registro de la navegación con el diario

WPF usa dos pilas para recordar las páginas desde las que ha navegado: una pila de retroceso y una pila de avance. Cuando navega desde la clase Page actual a una nueva clase Page o avanza hacia una clase Page existente, se agrega la clase Page actual a la pila de retroceso. Cuando retrocede desde la clase Page a la clase Page anterior, se agrega la clase actual Page a la pila de avance. La pila de retroceso, la pila de avance y la funcionalidad para administrarlas se conocen en conjunto como el diario. Cada elemento de la pila de retroceso y la pila de avance es una instancia de la clase JournalEntry y se conoce como una entrada de diario.

Conceptualmente, el diario funciona de la misma forma que los botones Atrás y Adelante en Internet Explorer. Esto se muestra en la siguiente ilustración.

Botones Atrás y Adelante

Para las aplicaciones XBAP que hospeda Internet Explorer, WPF integra el diario en la interfaz de usuario de navegación de Internet Explorer. Esto permite a los usuarios navegar por las páginas en una aplicación XBAP mediante el uso de los botones Atrás, Adelante y Páginas recientes de Internet Explorer.

Importante

En Internet Explorer, cuando un usuario sale o vuelve a una aplicación XBAP, solo se retienen en el diario las entradas de las páginas que no se mantuvieron activas. Para obtener información sobre cómo mantener las páginas activas, consulte la sección Duración de la página y el diario más adelante en este tema.

De forma predeterminada, el texto de cada clase Page que aparece en la lista Páginas recientes de Internet Explorer es el URI de la clase Page. En muchos casos, esto no es especialmente significativo para el usuario. Afortunadamente, puede cambiar el texto mediante una de las siguientes opciones:

  1. Valor del atributo JournalEntry.Name adjunto.

  2. Valor del atributo Page.Title.

  3. Valor del atributo Page.WindowTitle y URI del elemento Page actual.

  4. URI del elemento Page actual. (Es el valor predeterminado).

El orden en que se enumeran las opciones coincide con el orden de prioridad para buscar el texto. Por ejemplo, si JournalEntry.Name está establecido, se omiten los demás valores.

En el ejemplo siguiente se usa el atributo Page.Title para cambiar el texto que aparece para una entrada del diario.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.PageWithTitle"
    Title="This is the title of the journal entry for this page.">
</Page>
using System.Windows.Controls;

namespace SDKSample
{
    public partial class PageWithTitle : Page
    {

Namespace SDKSample
    Partial Public Class PageWithTitle
        Inherits Page
    }
}
    End Class
End Namespace

Aunque un usuario puede navegar por el diario mediante los botones Atrás, Adelante y Páginas recientes en Internet Explorer, también puede explorar el diario con los mecanismos declarativos y programáticos que proporciona WPF. Una razón para ello es proporcionar interfaces de usuario de navegación personalizada en las páginas.

Puede agregar compatibilidad de la navegación del diario mediante declaración a través de los comandos de navegación que expone NavigationCommands. En el siguiente ejemplo se muestra cómo se utiliza el comando de navegación BrowseBack.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.NavigationCommandsPage">
<Hyperlink Command="NavigationCommands.BrowseBack">Back</Hyperlink>
<Hyperlink Command="NavigationCommands.BrowseForward">Forward</Hyperlink>
</Page>

Puede navegar mediante programación por el diario a través de uno de los siguientes miembros de la clase NavigationService:

El diario también se puede manipular mediante programación, como se describe en la sección Conservación del estado del contenido con el historial de navegación más adelante en este tema.

Duración de la página y el diario

Considere una aplicación XBAP con varias páginas que incluyen contenido avanzado, como gráficos, animaciones y elementos multimedia. La superficie de memoria de este tipo de páginas podría ser bastante grande, especialmente si se usan elementos multimedia de audio y vídeo. Dado que el diario "recuerda" las páginas a las que se ha navegado, una aplicación XBAP de este tipo podría consumir rápidamente una cantidad de memoria considerable.

Por este motivo, el comportamiento predeterminado del diario es almacenar los metadatos de Page en cada entrada del diario en lugar de una referencia a un objeto Page. Cuando se navega a una entrada de diario, sus metadatos Page se utilizan para crear una nueva instancia de la clase Page especificada. Como consecuencia, cada clase Page a la que se navega tiene la duración que se muestra en la ilustración siguiente.

Duración de la página

Aunque el uso del comportamiento predeterminado de registro en el diario puede ahorrar consumo de memoria, es posible que se reduzca el rendimiento de representación por página; volver a crear una instancia de Page puede llevar mucho tiempo, especialmente si incluye mucho contenido. Si necesita conservar una instancia Page en el diario, puede usar dos técnicas. En primer lugar, puede navegar mediante programación a un objeto Page llamando al método NavigationService.Navigate.

En segundo lugar, puede especificar que WPF conserve una instancia de Page en el diario estableciendo la propiedad KeepAlive en true (el valor predeterminado es false). Como se muestra en el ejemplo siguiente, puede establecer KeepAlive mediante declaración en la marcación.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.KeepAlivePage"
    KeepAlive="True">
  
  An instance of this page is stored in the journal.
  
</Page>

La duración de una clase Page que se mantiene activa es ligeramente diferente de una que no se mantiene activa. La primera vez que se navega a una clase Page que se mantiene activa, se crea una instancia de ella como una clase Page que no se mantiene activa. Sin embargo, dado que se conserva una instancia de Page en el diario, nunca se crean instancias de nuevo mientras permanece en el diario. Por consiguiente, si una clase Page tiene una lógica de inicialización que debe llamarse cada vez que se navega a Page, debería moverla del constructor a un controlador del evento Loaded. Como se muestra en la ilustración siguiente, los eventos Loaded y Unloaded se generan cada vez que se navega desde o a Page, respectivamente.

Cuando se provocan los eventos Loaded y Unloaded

Cuando una clase Page no se mantiene activa, no debería realizar ninguna de las acciones siguientes:

  • Almacenar una referencia a la base de datos ni ninguna parte de esta.

  • Registrar controladores de eventos con eventos que no haya implementado.

Estas acciones crearán referencias que harán que la clase Page se conserve en la memoria, incluso después de haberla quitado del diario.

En general, es preferible el comportamiento predeterminado de Page que no mantiene ninguna clase Page activa. Sin embargo, esto conlleva implicaciones de estado que se describen en la sección siguiente.

Conservación del estado del contenido con el historial de navegación

Si una clase Page no se mantiene activa, y tiene controles que recopilan datos del usuario, ¿qué les pasa a los datos si un usuario sale de la clase Page y vuelve a ella? Desde la perspectiva de una experiencia de usuario, el usuario debería ver los datos que especificó anteriormente. Desafortunadamente, dado que se crea una nueva instancia de Page con cada navegación, se vuelve a crear una instancia de los controles que recopilaron los datos y se pierden los datos.

Afortunadamente, el diario proporciona compatibilidad para recordar los datos a través de las navegaciones de Page, incluidos los datos de control. En concreto, la entrada de diario de cada Page actúa como un contenedor temporal para el estado de Page asociado. En los siguientes pasos se describe cómo se usa esta compatibilidad cuando se navega desde Page:

  1. Se agrega al diario una entrada de la clase Page actual.

  2. El estado de la clase Page se almacena con la entrada de diario de esa página, que se agrega a la pila de retroceso.

  3. Se navega a la nueva clase Page.

Cuando se vuelve a navegar a la página Page con el diario, se realizan los pasos siguientes:

  1. Se crea una instancia de Page (la entrada superior del diario en la pila de retroceso).

  2. Se actualiza Page con el estado que se almacenó con la entrada del diario para Page.

  3. Se vuelve a navegar a Page.

WPF usa automáticamente esta compatibilidad cuando se usan los siguientes controles en una clase Page:

Si una clase Page usa estos controles, los datos que se introducen en estos se recuerdan en las navegaciones de Page, como se muestra en la clase ListBoxFavorite Color de la ilustración siguiente.

Página con controles que recuerdan el estado

Cuando Page tiene controles distintos de los de la lista anterior, o cuando el estado se almacena en objetos personalizados, debe escribir código que permita al diario recordar el estado entre navegaciones Page.

Si necesita recordar pequeños fragmentos de estado entre navegaciones Page, puede usar las propiedades de dependencia (consulte DependencyProperty) que se configuran con la marca de metadatos FrameworkPropertyMetadata.Journal.

Si el estado que debe recordar Page entre navegaciones consta de varios fragmentos de datos, puede que le parezca que no se necesita tanto código si se encapsula el estado en una sola clase y se implementa la interfaz IProvideCustomContentState.

Si necesita navegar por varios estados de una sola clase Page, sin navegar desde la clase Page, puede usar IProvideCustomContentState y NavigationService.AddBackEntry.

Cookies

Otra forma en que las aplicaciones WPF pueden almacenar datos es con las cookies, que se crean, actualizan y eliminan mediante el uso de los métodos SetCookie y GetCookie. Las cookies que se pueden crear en WPF son las mismas que usan otros tipos de aplicaciones web; las cookies son fragmentos de datos arbitrarios que se almacenan en una aplicación de un equipo cliente durante las sesiones de la aplicación o en estas. Los datos de las cookies normalmente adoptan la forma de un par de nombre-valor en el formato siguiente.

Name=Value

Cuando los datos se pasan a SetCookie, junto con el Uri de la ubicación para la que se debe establecer la cookie, se crea una cookie en memoria y solo está disponible para la duración de la sesión actual de la aplicación. Este tipo de cookie se conoce como cookie de sesión.

Para almacenar una cookie mediante sesiones de la aplicación, debe agregarse una fecha de expiración para la cookie, con el formato siguiente.

NAME=VALUE; expires=DAY, DD-MMM-YYYY HH:MM:SS GMT

Se almacena una cookie con una fecha de expiración actual en la carpeta de archivos temporales de Internet de la instalación de Windows hasta que expire. Este tipo de cookie se conoce como cookie persistente porque se conserva entre sesiones de aplicación.

Para recuperar las cookies persistentes y de sesión, llame al método GetCookie y pase el Uri de la ubicación donde se estableció la cookie con el método SetCookie.

A continuación, se muestran algunas de las formas en que las cookies se admiten en WPF:

  • Las aplicaciones independientes WPF y XBAP pueden crear y administrar cookies.

  • Las cookies que crea una aplicación XBAP son accesibles desde el explorador.

  • Las aplicaciones XBAP del mismo dominio pueden crear y compartir cookies.

  • Las aplicaciones XBAP y las páginas HTML del mismo dominio pueden crear y compartir cookies.

  • Las cookies se envían cuando las aplicaciones XBAP y las páginas XAML dinámicas realizan solicitudes web.

  • Tanto las aplicaciones XBAP como las aplicaciones XBAP de nivel superior hospedadas en IFRAMES pueden acceder a las cookies.

  • La compatibilidad de las cookies en WPF es igual para todos los exploradores compatibles.

  • En Internet Explorer, WPF respeta la directiva de P3P que pertenece a las cookies, especialmente con respecto a las aplicaciones XBAP propias y de terceros.

Navegación estructurada

Si necesita pasar datos de una clase Page a otra, puede pasarlos como argumentos a un constructor que no es sin parámetros de la clase Page. Tenga en cuenta que, si usa esta técnica, debe mantener la clase Page activa; de lo contrario, la próxima vez que navegue a Page, WPF volverá a crear una instancia de Page mediante el constructor sin parámetros.

Como alternativa, su clase Page puede implementar propiedades que se establecen con los datos que deben pasarse. Sin embargo, todo se complica cuando una clase Page debe volver a pasar datos a la clase Page que navegó a esta. El problema es que la navegación no admite de forma nativa mecanismos para garantizar que una clase Page se devuelva una vez se navegue desde esta. Esencialmente, la navegación no admite la semántica de llamada/devolución. Para resolver este problema, WPF proporciona la clase PageFunction<T> que puede usar para asegurarse de que se devuelve una clase Page de una manera predecible y estructurada. Para obtener más información, consulte Información general sobre la navegación estructurada.

Clase NavigationWindow

En este punto, ha visto la gama de servicios de navegación que probablemente usará para compilar aplicaciones con contenido navegable. Estos servicios se trataron en el contexto de aplicaciones XBAP, aunque no están limitados a aplicaciones XBAP. Los sistemas operativos modernos y las aplicaciones de Windows aprovechan la experiencia de exploración de los usuarios modernos para incorporar la navegación de tipo explorador en aplicaciones independientes. Algunos ejemplos frecuentes son:

  • Diccionario de sinónimos de Word: navegación por las opciones de Word.

  • Explorador de archivos: navegación por archivos y carpetas.

  • Asistentes: desglose de una tarea compleja en varias páginas entre las que se puede navegar. Un ejemplo es el Asistente para componentes de Windows que controla la adición y eliminación de características de Windows.

Para incorporar la navegación de tipo explorador en sus aplicaciones independientes, puede usar la clase NavigationWindow. El elemento NavigationWindow deriva de Window y lo extiende con la misma compatibilidad para la navegación que proporcionan las aplicaciones XBAP. Puede usar NavigationWindow como ventana principal de la aplicación independiente o como una ventana secundaria, por ejemplo, un cuadro de diálogo.

Para implementar un elemento NavigationWindow, al igual que con la mayoría de clases de nivel superior en WPF (Window, Page, etc.), use una combinación de marcación y código subyacente. Esto se muestra en el ejemplo siguiente.

<NavigationWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.MainWindow" 
    Source="HomePage.xaml"/>
using System.Windows.Navigation;

namespace SDKSample
{
    public partial class MainWindow : NavigationWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

Namespace SDKSample
    Partial Public Class MainWindow
        Inherits NavigationWindow
        Public Sub New()
            InitializeComponent()
        End Sub
    End Class
End Namespace

Este código crea un elemento NavigationWindow que navega automáticamente a una clase Page (HomePage.xaml) cuando se abre el elemento NavigationWindow. Si el elemento NavigationWindow es la ventana principal de la aplicación, puede usar el atributo StartupUri para iniciarlo. Esto se muestra en la marcación siguiente.

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="MainWindow.xaml" />

En la siguiente ilustración se muestra el elemento NavigationWindow como ventana principal de una aplicación independiente.

Una ventana principal

En la ilustración, puede ver que el elemento NavigationWindow tiene un título, aunque no se estableció en el código de implementación del elemento NavigationWindow del ejemplo anterior. En su lugar, el título se establece mediante la propiedad WindowTitle, que se muestra en el siguiente código.

<Page 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Title="Home Page"
    WindowTitle="NavigationWindow">
</Page>

La configuración de las propiedades WindowWidth y WindowHeight también afecta al elemento NavigationWindow.

Normalmente, deberá implementar su propio elemento NavigationWindow cuando necesite personalizar su comportamiento o su apariencia. Si no realiza nada, puede usar un acceso directo. Si especifica el URI de paquete de una clase Page como StartupUri en una aplicación independiente, el elemento Application crea automáticamente un elemento NavigationWindow para hospedar la clase Page. En la marcación siguiente se muestra cómo habilitar esta opción.

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="HomePage.xaml" />

Si quiere que una ventana de la aplicación secundaria, como un cuadro de diálogo, sea un elemento NavigationWindow, puede utilizar el código del ejemplo siguiente para abrirla.

// Open a navigation window as a dialog box
NavigationWindowDialogBox dlg = new NavigationWindowDialogBox();
dlg.Source = new Uri("HomePage.xaml", UriKind.Relative);
dlg.Owner = this;
dlg.ShowDialog();
' Open a navigation window as a dialog box
Dim dlg As New NavigationWindowDialogBox()
dlg.Source = New Uri("HomePage.xaml", UriKind.Relative)
dlg.Owner = Me
dlg.ShowDialog()

En la figura siguiente se muestra el resultado.

Un cuadro de diálogo

Como puede observar, NavigationWindow muestra los botones Atrás y Adelante de tipo Internet Explorer que permiten a los usuarios navegar por el diario. Estos botones proporcionan la misma experiencia de usuario, como se muestra en la ilustración siguiente.

Botones Atrás y Adelante en un control NavigationWindow

Si sus páginas proporcionan su propia compatibilidad de navegación por el diario e interfaz de usuario, puede ocultar los botones Atrás y Adelante que muestra NavigationWindow estableciendo el valor de la propiedad ShowsNavigationUI en false.

Como alternativa, puede usar la compatibilidad con la personalización en WPF para reemplazar la interfaz de usuario de NavigationWindow.

Clase Frame

Tanto el explorador como NavigationWindow son ventanas que hospedan contenido navegable. En algunos casos, las aplicaciones tienen contenido que no hace falta que los hospede una ventana completa. En su lugar, dicho contenido se hospeda dentro de otro contenido. Puede insertar contenido navegable en otro contenido utilizando la clase Frame. Frame proporciona la misma compatibilidad que NavigationWindow y aplicaciones XBAP.

En el ejemplo siguiente se muestra cómo agregar Frame a Page mediante declaración con el uso del elemento Frame.

<Page 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  WindowTitle="Page that Hosts a Frame"
  WindowWidth="250"
  WindowHeight="250">
<Frame Source="FramePage1.xaml" />
</Page>

Esta marcación establece el atributo Source del elemento Frame con un URI de paquete para Pageal que Frame debe navegar inicialmente. En la siguiente ilustración se muestra una aplicación XBAP con Page que tiene Frame que ha navegado entre varias páginas.

Marco que ha navegado entre varias páginas

No solo debe usar Frame dentro del contenido de Page. También es común hospedar Frame dentro del contenido de Window.

De forma predeterminada, Frame solo usa su propio diario en ausencia de otro. Si Frame forma parte de contenido que se hospeda dentro de NavigationWindow o una aplicación XBAP, Frame usa el diario que pertenece a NavigationWindow o a la aplicación XBAP. Sin embargo, a veces, es posible que Frame deba ser responsable de su propio diario. Una razón para ello es permitir la navegación del diario dentro de las páginas que se hospedan en Frame. Esto se muestra en la ilustración siguiente.

Diagrama de marco y página

En este caso, puede configurar Frame para utilizar su propio diario estableciendo la propiedad JournalOwnership de Frame en OwnsJournal. Esto se muestra en la marcación siguiente.

<Page 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  WindowTitle="Page that Hosts a Frame"
  WindowWidth="250"
  WindowHeight="250">
<Frame Source="FramePage1.xaml" JournalOwnership="OwnsJournal" />
</Page>

En la ilustración siguiente se muestra el efecto de navegar dentro de Frame que usa su propio diario.

Un marco que usa su propio diario

Observe que las entradas del diario se muestran mediante la interfaz de usuario de navegación en Frame, en lugar de Internet Explorer.

Nota

Si Frame forma parte del contenido que se hospeda en Window, Frame usa su propio diario y, por lo tanto, muestra su propia interfaz de usuario de navegación.

Si la experiencia de usuario requiere Frame para proporcionar su propio diario sin mostrar la interfaz de usuario de navegación, puede ocultar la interfaz de usuario de navegación estableciendo NavigationUIVisibility en Hidden. Esto se muestra en la marcación siguiente.

<Page 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  WindowTitle="Page that Hosts a Frame"
  WindowWidth="250"
  WindowHeight="250">
<Frame 
  Source="FramePage1.xaml" 
  JournalOwnership="OwnsJournal" 
  NavigationUIVisibility="Hidden" />
</Page>

Frame y NavigationWindow son clases que se conocen como hosts de navegación. Un host de navegación es una clase a la que se puede navegar y que puede mostrar el contenido. Para lograr esto, cada host de navegación usa su propio NavigationService y diario. La construcción de un host de navegación básica se muestra en la ilustración siguiente.

Diagramas de navegador

En esencia, esto permite a NavigationWindow y Frame proporcionar la misma compatibilidad de navegación que proporciona una aplicación XBAP cuando se hospeda en el explorador.

Además de usar NavigationService y un diario, los hosts de navegación implementan los mismos miembros que implementa NavigationService. Esto se muestra en la ilustración siguiente.

Un diario en un marco y en un control NavigationWindow

Esto le permite programar la compatibilidad con la navegación directamente con ellos. Puede considerar esta opción si necesita proporcionar una interfaz de usuario de navegación personalizada para Frame que se hospeda en Window. Además, ambos tipos implementan miembros adicionales relacionados con la navegación, incluidos BackStack (NavigationWindow.BackStack, Frame.BackStack) y ForwardStack (NavigationWindow.ForwardStack, Frame.ForwardStack), que le permiten enumerar las entradas del diario en la pila de retroceso y avance, respectivamente.

Como se mencionó anteriormente, pueden haber varios diarios dentro de una aplicación. En la siguiente ilustración se muestra un ejemplo de cuándo ocurre esto.

Varios diarios dentro de una aplicación

En este tema, se han usado Page y aplicaciones XBAP de paquete para demostrar las distintas funcionalidades de navegación de WPF. Sin embargo, Page que se compila en una aplicación no es el único tipo de contenido al que se puede navegar, y las aplicaciones XBAP de paquete no son la única manera de identificar contenido.

Como se muestra en esta sección, también puede desplazarse a archivos XAML y HTML y objetos.

Un archivo XAML dinámico es un archivo con las siguientes características:

  • Solo contiene XAML (es decir, nada de código).

  • Tiene una declaración de espacio de nombres adecuada.

  • Tiene la extensión de nombre de archivo .xaml.

Por ejemplo, considere el siguiente contenido que se almacena como un archivo XAML dinámico, Person.xaml.

<!-- Person.xaml -->
<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <TextBlock FontWeight="Bold">Name:</TextBlock>
  <TextBlock>Nancy Davolio</TextBlock>
  <LineBreak />
  <TextBlock FontWeight="Bold">Favorite Color:</TextBlock>
  <TextBlock>Yellow</TextBlock>
</TextBlock>

Al hacer doble clic en el archivo, el explorador se abre y se desplaza al contenido y lo muestra. Esto se muestra en la siguiente ilustración.

Visualización del contenido del archivo Person.XAML

Se puede mostrar un archivo XAML dinámico de lo siguiente:

  • Un sitio web en la máquina local, la intranet o Internet.

  • Un recurso compartido de archivos de convención de nomenclatura universal (UNC).

  • El disco local.

Un archivo XAML se puede agregar a los favoritos del explorador, o ser la página principal del explorador.

Nota

Para obtener más información acerca de cómo publicar e iniciar páginas XAML dinámicas, consulte Implementar una aplicación de WPF.

Una limitación con respecto a un archivo XAML dinámico es que solo puede hospedar contenido que sea seguro de ejecutar en confianza parcial. Por ejemplo, Window no puede ser el elemento raíz de un archivo XAML dinámico. Para obtener más información, vea Seguridad de confianza parcial de WPF.

Como cabría esperar, también puede navegar a HTML. Simplemente, debe proporcionar un URI que usa el esquema HTTP. Por ejemplo, el siguiente XAML muestra Frame que navega a una página HTML.

<Frame Source="http://www.microsoft.com/default.aspx" />

Navegar a HTML requiere permisos especiales. Por ejemplo, no puede navegar desde una aplicación XBAP que se ejecuta en el espacio aislado de seguridad de confianza parcial de la zona de Internet. Para obtener más información, vea Seguridad de confianza parcial de WPF.

El control WebBrowser admite la interoperabilidad de código administrado o script, la navegación y el hospedaje de documentos HTML. Para obtener información detallada sobre el control WebBrowser, consulte WebBrowser.

Al igual que Frame, navegar a HTML mediante WebBrowser requiere permisos especiales. Por ejemplo, desde una aplicación de confianza parcial, puede navegar solo a HTML ubicado en el sitio de origen. Para obtener más información, vea Seguridad de confianza parcial de WPF.

Si tiene datos que se almacenan como objetos personalizados, una manera de mostrar los datos consiste en crear Page con el contenido que está enlazado a esos objetos (vea Información general sobre el enlace de datos). Si no necesita la sobrecarga de la creación de una página completa para mostrar los objetos, puede navegar directamente a ellos en su lugar.

Tenga en cuenta la clase Person que se implementa en el código siguiente.

using System.Windows.Media;

namespace SDKSample
{
    public class Person
    {
        string name;
        Color favoriteColor;

        public Person() { }
        public Person(string name, Color favoriteColor)
        {
            this.name = name;
            this.favoriteColor = favoriteColor;
        }

        public string Name
        {
            get { return this.name; }
            set { this.name = value; }
        }

        public Color FavoriteColor
        {
            get { return this.favoriteColor; }
            set { this.favoriteColor = value; }
        }
    }
}

Namespace SDKSample
    Public Class Person
        Private _name As String
        Private _favoriteColor As Color

        Public Sub New()
        End Sub
        Public Sub New(ByVal name As String, ByVal favoriteColor As Color)
            Me._name = name
            Me._favoriteColor = favoriteColor
        End Sub

        Public Property Name() As String
            Get
                Return Me._name
            End Get
            Set(ByVal value As String)
                Me._name = value
            End Set
        End Property

        Public Property FavoriteColor() As Color
            Get
                Return Me._favoriteColor
            End Get
            Set(ByVal value As Color)
                Me._favoriteColor = value
            End Set
        End Property
    End Class
End Namespace

Para navegar a ella, llame al método NavigationWindow.Navigate, como se muestra en el siguiente código.

<Page 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.HomePage"
  WindowTitle="Page that Navigates to an Object">
<Hyperlink Name="hyperlink" Click="hyperlink_Click">
  Navigate to Nancy Davolio
</Hyperlink>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace SDKSample
{
    public partial class HomePage : Page
    {
        public HomePage()
        {
            InitializeComponent();
        }

        void hyperlink_Click(object sender, RoutedEventArgs e)
        {
            Person person = new Person("Nancy Davolio", Colors.Yellow);
            this.NavigationService.Navigate(person);
        }
    }
}

Namespace SDKSample
    Partial Public Class HomePage
        Inherits Page
        Public Sub New()
            InitializeComponent()
        End Sub

        Private Sub hyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Dim person As New Person("Nancy Davolio", Colors.Yellow)
            Me.NavigationService.Navigate(person)
        End Sub
    End Class
End Namespace

En la figura siguiente se muestra el resultado.

Una página que navega a una clase

En esta ilustración, puede ver que no se muestra nada útil. De hecho, el valor que se muestra es el valor devuelto del método ToString para el objeto Person; de forma predeterminada, este es el único valor que WPF puede usar para representar el objeto. Podría invalidar el método ToString para devolver información más significativa, aunque seguirá siendo solo un valor de cadena. Una técnica que aprovecha las funcionalidades de presentación de WPF es el uso de una plantilla de datos. Puede implementar una plantilla de datos que WPF puede asociar a un objeto de un tipo determinado. En el código siguiente se muestra una plantilla de datos para el objeto Person.

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SDKSample" 
    x:Class="SDKSample.App"
    StartupUri="HomePage.xaml">

  <Application.Resources>

    <!-- Data Template for the Person Class -->
    <DataTemplate DataType="{x:Type local:Person}">
      <TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        <TextBlock FontWeight="Bold">Name:</TextBlock>
        <TextBlock Text="{Binding Path=Name}" />
        <LineBreak />
        <TextBlock FontWeight="Bold">Favorite Color:</TextBlock>
        <TextBlock Text="{Binding Path=FavoriteColor}" />
      </TextBlock>
    </DataTemplate>
    
  </Application.Resources>

</Application>

En este caso, la plantilla de datos está asociada al tipo Person mediante la extensión de marcación x:Type del atributo DataType. A continuación, la plantilla de datos enlaza los elementos TextBlock (consulte TextBlock) a las propiedades de la clase Person. En la siguiente ilustración se muestra el aspecto actualizado del objeto Person.

Navegación a una clase que tiene una plantilla de datos

Una ventaja de esta técnica es la coherencia que obtiene al poder volver a usar la plantilla de datos para mostrar los objetos de forma coherente en cualquier parte de la aplicación.

Para obtener más información, consulte Información general sobre plantillas de datos.

Seguridad

La compatibilidad de navegación de WPF permite navegar a las aplicaciones XBAP a través de Internet, y que las aplicaciones hospeden contenido de terceros. Para proteger las aplicaciones y los usuarios de un comportamiento malintencionado, WPF proporciona una variedad de características de seguridad que se describen en Seguridad y Seguridad de confianza parcial de WPF.

Vea también