A user on the Expression Blend/SketchFlow forums asked a question about how to navigate between pages in xaml.

There are a number of solutions to this problem, including using a hyperlink item inside an element such as a textblock.

<TextBlock>
    <Hyperlink NavigateUri="Page3.xaml">
        <Run Text="Page 3"/>
    </Hyperlink>
</TextBlock>

However, this will only work when contained within a Page element, and won’t work if you want to have navigation UI in the same page containing a frame. To enable both of these scenarios in an easy to use way, I wrote a simple NavigationAction behavior that will navigate to a Page from either inside a Page, or from an element that is inside the same screen as a Frame element.

The Action created below may be attached to any item inside a page, or any item on a screen that has a Frame object being used to navigate to pages. Set the PageProperty to the filename of the page to be shown.

  • To use this behavior in Blend, add a new Action item to your project, and replicate the code provided below in your action.
  • Build your project.
  • In the asset panel, find your new Action, and drag it onto an element on the artboard.
  • Select the behavior in the Objects and Timeline panel
  • Open the properties panel
  • Set the Page property to the page you want loaded
  • Adjust the trigger event to the event you would like, most likely MouseLeftButtonUp or Down

Source code and example usage are provided below.

The FindChild method is a modified version of code posted here: http://stackoverflow.com/questions/636383/wpf-ways-to-find-controls

	public class NavigateAction : TriggerAction<DependencyObject>
	{
		public static readonly DependencyProperty PageProperty = DependencyProperty.Register("Page", typeof(string), typeof(NavigateAction), new PropertyMetadata(null));

		public string Page
		{
			get { return GetValue(PageProperty) as string; }
			set { SetValue(PageProperty, value); }
		}

		private NavigationService navigationService;
		public NavigateAction()
		{
			NavigationWindow navigationWindow = Application.Current.MainWindow as NavigationWindow;
			if (navigationWindow != null)
			{
				this.navigationService = navigationWindow.NavigationService;
			}

		}

		protected override void Invoke(object o)
		{
			if (this.navigationService == null)
			{
				// Try to find a frame in the main window.
				Frame frame = FindChild<Frame>(Application.Current.MainWindow);
				if (frame != null)
				{
					this.navigationService = frame.NavigationService;
				}
			}

			if (this.navigationService != null)
			{
				this.navigationService.Navigate(new Uri(this.Page, UriKind.Relative));
			}
		}

		public static T FindChild<T>(DependencyObject parent) where T : DependencyObject
		{
			if (parent == null) return null;
			T foundChild = null;
			int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
			for (int i = 0; i < childrenCount; i++)
			{
				var child = VisualTreeHelper.GetChild(parent, i);
				T childType = child as T;
				if (childType == null)
				{
					foundChild = FindChild<T>(child);
					if (foundChild != null)
					{
						return foundChild;
					}
				}
				else
				{
					return (T)child;
				}
			}
			return foundChild;
		}
	}
}

Using the behavior on siblings of a Frame element:

<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:WpfApplication21"
	x:Class="WpfApplication21.MainWindow"
	x:Name="Window"
	Title="MainWindow"
	Width="640" Height="480">
	<Grid>
		<Grid.RowDefinitions>
			<RowDefinition Height="0.925*"/>
			<RowDefinition Height="0.075*"/>
		</Grid.RowDefinitions>
		<Frame x:Name="MainFrame" Content="" Source="Pages\Page1.xaml" Margin="0,0,0,4"/>
		<StackPanel Grid.Row="1" Orientation="Horizontal">

			<Ellipse Fill="Red" Stroke="Black" Width="100">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page1.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</Ellipse>
			<Ellipse Fill="#FF00FF1C" Stroke="Black" Width="100">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page2.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</Ellipse>
			<Ellipse Fill="#FF0BC6CD" Stroke="Black" Width="100">
			<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page3.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</Ellipse>
		</StackPanel>
	</Grid>
</Window>

Using the behavior on items within a page:

<Page
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:WpfApplication21"
	x:Class="WpfApplication21.Page1"
	x:Name="Page"
	WindowTitle="Page"
	FlowDirection="LeftToRight"
	Width="640" Height="480"
	WindowWidth="640" WindowHeight="480">

	<Grid x:Name="LayoutRoot">
		<StackPanel>
			<TextBlock Text="Page 1" TextWrapping="Wrap">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page1.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</TextBlock>
			<TextBlock Text="Page 2" TextWrapping="Wrap">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page2.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</TextBlock>
			<TextBlock HorizontalAlignment="Left" Text="Page 3" TextWrapping="Wrap">
				<i:Interaction.Triggers>
					<i:EventTrigger EventName="MouseLeftButtonUp">
						<local:NavigateAction Page="Pages/Page3.xaml"/>
					</i:EventTrigger>
				</i:Interaction.Triggers>
			</TextBlock>
		</StackPanel>
	</Grid>
</Page>