How-To: Navigate in XAML
This topic walks through controlling Navigation from XAML. This includes specifying data that should be attached to the navigation request.
Note
This guide uses predefined code created by the Uno Template using the Recommended
preset, however, it uses the MVVM
approach for the examples instead of MVUX
defined in the Recommended
preset.
Step-by-step
Important
This guide assumes you used the template wizard or dotnet new unoapp
to create your solution. If not, it is recommended that you follow the Creating an application with Uno.Extensions documentation to create an application from the template.
1. Navigation.Request
Navigation can be defined in XAML by placing the Navigation.Request
attached property on a specific XAML element. The string value specified in the Navigation.Request
is the route to be navigated to.
Depending on the type of the XAML element, the Navigation.Request
property will attach to an appropriate event in order to trigger navigation. For example, on a Button
, the Click
event will be used to trigger navigation, whereas the SelectionChanged
event on a ListView
is used. If you place a Navigation.Request
property on a static element, such as a Border
, Image
, or TextBlock
, the Tapped
event will be used to trigger navigation.
Add a new
Page
to navigate to,SamplePage.xaml
Add a new class,
SampleViewModel
, to the class library projectpublic class SampleViewModel { public SampleViewModel(INavigator navigator) { _navigator = navigator; } private readonly INavigator _navigator; }
In
MainPage.xaml
update theButton
to use theNavigation.Request
attached property instead of theClick
event handler.<Button Content="Go to SamplePage" uen:Navigation.Request="Sample" />
Tip
As Navigation.Request attached property exists in the
Uno.Extensions.Navigation.UI
namespace you will need to import this namespace on thePage
element with<Page x:Class="NavigateInXAML.Views.SamplePage" ... xmlns:uen="using:Uno.Extensions.Navigation.UI">
In
SamplePage.xaml
add aButton
, again with theNavigation.Request
attached property. The "-" navigation route is used to navigate back.<Button Content="Go Back" uen:Navigation.Request="-" />
Tip
While this works, it relies on reflection to convert the request path "Sample" to the corresponding view, i.e.
SamplePage
. It's better to defineViewMap
andRouteMap
Add a
ViewMap
and aRouteMap
for theSamplePage
into theRegisterRoutes
method in theApp.xaml.cs
fileprivate static void RegisterRoutes(IViewRegistry views, IRouteRegistry routes) { views.Register( new ViewMap(ViewModel: typeof(ShellViewModel)), new ViewMap<MainPage, MainViewModel>(), new DataViewMap<SecondPage, SecondViewModel, Entity>(), new ViewMap<SamplePage, SampleViewModel>() ); routes.Register( new RouteMap("", View: views.FindByViewModel<ShellViewModel>(), Nested: [ new ("Main", View: views.FindByViewModel<MainViewModel>()), new ("Second", View: views.FindByViewModel<SecondViewModel>()), new ("Sample", View: views.FindByViewModel<SampleViewModel>()), ] ) ); }
2. Navigation.Data
In addition to specifying the route to navigate to, the Navigation.Data attached property can be used to define the data to be attached to the navigation request. The data can be accessed by the view model associated with the route using constructor injection.
Note
It's currently not possible to send data with uen:Navigation.Data
alongside uen:Region.Name
when navigating through NavigationView or TabBar items. uen:Navigation.Data
only supports sending data with uen:Navigation.Request
. To send data when navigating with these items, you'll need to use code-behind or a view model. For more details, see this example here.
Define a record (or class),
Widget
, that is the type of data that will be attached to the navigation request.public record Widget(string Name, double Weight);
Add a property,
Widgets
, toMainViewModel
that returns an array of predefinedWidget
instances.public Widget[] Widgets { get; } = [ new Widget("NormalSpinner", 5.0), new Widget("HeavySpinner", 50.0) ];
Replace the
Button
with aListView
inMainPage.xaml
that has theItemsSource
property data bound to theWidgets
property.<StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"> <ListView ItemsSource="{Binding Widgets}" x:Name="WidgetsList" SelectionMode="Single"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Padding="10"> <TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Age}" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackPanel>
Add a
Button
to theStackPanel
onMainPage.xaml
that has bothNavigation.Request
, that specified the navigation route, and theNavigation.Data
properties. In this case theNavigation.Data
attached property is data bound to theSelectedItem
property on the named elementWidgetsList
(which matches thex:Name
set on the previously addedListView
)<Button Content="Go to Sample Page" uen:Navigation.Request="Sample" uen:Navigation.Data="{Binding SelectedItem, ElementName=WidgetsList}"/>
Update
SecondViewModel
to accept aWidget
as the second constructor parameterpublic class SampleViewModel { public string Title => "Sample Page"; public string Name { get; } public SampleViewModel(Widget widget) { Name = widget.Name; } }
Add a
TextBlock
toSecondPage.xaml
that shows the name of theWidget
supplied during navigation.<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"> <Run Text="Widget Name:" /> <Run Text="{Binding Name}" /> </TextBlock>
In order for the
Widget
to be injected into theSampleViewModel
during navigation, aDataMap
has to be added to theViewMap
. Therefore, we can change theViewMap
instantiation toDataViewMap
and provide theWidget
as a generic argument:new DataViewMap<SamplePage, SampleViewModel, Widget>()
3. Navigating To SelectedItem
Instead of having to select an item in the ListView
and then clicking on the Button
, Navigation can be triggered when the user selects an item in the ListView
.
Add the
Navigation.Request
property to theListView
. TheNavigation.Data
property is not required as the selected item will automatically be attached to the navigation request. Also remove theSelectionMode
property as it is no longer necessary for theListView
to track the selected item.<ListView ItemsSource="{Binding Widgets}" uen:Navigation.Request="Sample"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Padding="10"> <TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Age}" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>