Skip to content

Commit

Permalink
Merge pull request #2695 from unoplatform/dev/erli/2449-pass-data-tab…
Browse files Browse the repository at this point in the history
…bar-navview
  • Loading branch information
kazo0 authored Feb 27, 2025
2 parents 59007cd + 0b71009 commit 7e88b5c
Show file tree
Hide file tree
Showing 25 changed files with 396 additions and 43 deletions.
19 changes: 18 additions & 1 deletion doc/Learn/Navigation/Advanced/HowTo-UseNavigationView.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ Choosing the right control for your navigation needs is important, and one commo
}
```

* Register `ViewMap` and `RouteMap` instances inside the `RegisterRoutes` method in `App.cs`. This associates the `ProductsPage` described above with `ProductsViewModel`, as well as avoiding the use of reflection for route discovery.
* Register `ViewMap` and `RouteMap` instances inside the `RegisterRoutes` method in `App.xaml.cs`. This associates the `ProductsPage` described above with `ProductsViewModel`, as well as avoiding the use of reflection for route discovery.

```csharp
private static void RegisterRoutes(IViewRegistry views, IRouteRegistry routes)
Expand Down Expand Up @@ -252,6 +252,22 @@ Choosing the right control for your navigation needs is important, and one commo
</NavigationView.MenuItems>
```

#### Using the `Navigation.Data` attached property

Sometimes, it is necessary to send data to your ViewModel from the previous page. This can be done using the `Navigation.Data` attached property. For example, if you want to send an `Entity` object from the `MainViewModel` to the `ProductsViewModel`:

```diff
<!-- Adds a products item -->
<NavigationViewItem Content="Products"
+ uen:Navigation.Data="{Binding Entity}"
uen:Region.Name="Products" />
```

For the full setup and more information on using the `Navigation.Data` attached property, refer to the documentation in the [How-To: Navigate in XAML](xref:Uno.Extensions.Navigation.HowToNavigateInXAML#2-navigationdata) guide.

> [!NOTE]
> You also need to set up a `DataViewMap`. For more information on `ViewMap` and `DataViewMap`, refer to the **ViewMap** documentation in the [How-To: Define Routes](xref:Uno.Extensions.Navigation.HowToDefineRoutes#viewmap) guide.
### 6. Putting it all together

* Observe how the `NavigationView` and the content area are now connected. When you select a `NavigationViewItem`, the corresponding `Grid` or `Page` will be shown.
Expand Down Expand Up @@ -285,6 +301,7 @@ Choosing the right control for your navigation needs is important, and one commo
<NavigationViewItem Content="Three"
uen:Region.Name="Three" />
<NavigationViewItem Content="Products"
uen:Navigation.Data="{Binding Entity}"
uen:Region.Name="Products" />
</NavigationView.MenuItems>

Expand Down
25 changes: 21 additions & 4 deletions doc/Learn/Navigation/Advanced/HowTo-UseTabBar.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,21 +272,21 @@ var builder = this.CreateBuilder(args)
public class SignUpViewModel
{
public SignUpViewMode()
public SignUpViewModel()
{
}
}
```
* Register `ViewMap` and `RouteMap` instances inside the `RegisterRoutes` method in `App.cs`. This associates the `SignUpPage` described above with `SignUpViewModel`, as well as avoiding the use of reflection for route discovery.
* Register `ViewMap` and `RouteMap` instances inside the `RegisterRoutes` method in `App.xaml.cs`. This associates the `SignUpPage` described above with `SignUpViewModel`, as well as avoiding the use of reflection for route discovery.
```csharp
private static void RegisterRoutes(IViewRegistry views, IRouteRegistry routes)
{
views.Register(
new ViewMap(ViewModel: typeof(ShellViewModel)),
new ViewMap<LoginPage, LoginViewModel>(),
new ViewMap<SignUpPage, SignUpViewModel>(),
new ViewMap<MainPage, MainViewModel>()
);
Expand All @@ -297,7 +297,7 @@ var builder = this.CreateBuilder(args)
new RouteMap("Main", View: views.FindByViewModel<MainViewModel>(),
Nested:
[
new RouteMap("Login", View: views.FindByViewModel<LoginViewModel>())
new RouteMap("SignUp", View: views.FindByViewModel<SignUpViewModel>())
]
)
]
Expand All @@ -324,6 +324,22 @@ var builder = this.CreateBuilder(args)
</utu:TabBar.Items>
```
#### Using the `Navigation.Data` attached property
Sometimes, it is necessary to send data to your ViewModel from the previous page. This can be done using the `Navigation.Data` attached property. For example, if you want to send an `Entity` object from the `MainViewModel` to the `SignUpViewModel`:
```diff
<!-- Sign up item -->
<utu:TabBarItem uen:Region.Name="SignUp"
+ uen:Navigation.Data="{Binding Entity}"
Style="{StaticResource MaterialBottomTabBarItemStyle}" />
```
For the full setup and more information on using the `Navigation.Data` attached property, refer to the documentation in the [How-To: Navigate in XAML](xref:Uno.Extensions.Navigation.HowToNavigateInXAML#2-navigationdata) guide.
> [!NOTE]
> You also need to set up a `DataViewMap`. For more information on `ViewMap` and `DataViewMap`, refer to the **ViewMap** documentation in the [How-To: Define Routes](xref:Uno.Extensions.Navigation.HowToDefineRoutes#viewmap) guide.
### 6. Putting it all together
* When a `TabBarItem` is selected, the content which corresponds to the route name of the item will be displayed, with the `Visibility` property changed if needed.
Expand Down Expand Up @@ -391,6 +407,7 @@ var builder = this.CreateBuilder(args)
<utu:TabBarItem uen:Region.Name="Three"
Style="{StaticResource MaterialBottomTabBarItemStyle}" />
<utu:TabBarItem uen:Region.Name="SignUp"
uen:Navigation.Data="{Binding Entity}"
Style="{StaticResource MaterialBottomTabBarItemStyle}" />
</utu:TabBar.Items>
</utu:TabBar>
Expand Down
3 changes: 0 additions & 3 deletions doc/Learn/Navigation/HowTo-NavigateInXAML.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,6 @@ Depending on the type of the XAML element, the `Navigation.Request` property wil

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](https://github.com/unoplatform/Uno.Samples/blob/20a110a75de43c8ddb04f149ae96fe2b74c93af1/UI/Navigation/src/Navigation/Presentation/TabBarWithDataNavigation/TabBarWithDataViewModel.cs#L23-L29).
- Define a record (or class), `Widget`, that is the type of data that will be attached to the navigation request.

```csharp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ protected async Task SelectionChanged(FrameworkElement sender, FrameworkElement?
return;
}

await nav.NavigateRouteAsync(sender, path);
var data = selectedItem.GetData();

await nav.NavigateRouteAsync(sender, path, data: data);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
namespace TestHarness.UITest;

public class Given_Apps_Regions : NavigationTestBase
{
[Test]
public async Task When_Regions_Send_Data_NavView()
{
InitTestSection(TestSections.Apps_Regions);

App.WaitThenTap("ShowAppButton");

App.WaitElement("RegionsHomePageTextBox");

var textToSet = "Hello, World!";

App.SetText("RegionsHomePageTextBox", textToSet);

App.WaitThenTap("RegionsHomePageThirdPage");

App.WaitElement("RegionsThirdPageTextBock");

var textFromTb = App.GetText("RegionsThirdPageTextBock");

Assert.AreEqual(textToSet, textFromTb);
}

[Test]
public async Task When_Regions_Send_Data_TabBar()
{
InitTestSection(TestSections.Apps_Regions);

App.WaitThenTap("ShowAppButton");

App.WaitThenTap("RegionsHomePageRegionsTbData");

App.WaitThenTap("RegionsTbDataPageTabOne");

var textToSet = "Hello, World!";

App.SetText("RegionsFirstTbiDataPageTextBox", textToSet);

App.WaitThenTap("RegionsTbDataPageTabTwo");

var textFromTb = App.GetText("RegionsSecondTbiDataPageTextBox");

Assert.AreEqual(textToSet, textFromTb);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@

namespace TestHarness.UITest;
namespace TestHarness.UITest;

public static class QueryExtensions
{
Expand Down Expand Up @@ -73,6 +72,17 @@ public static string GetText(this IApp app, string elementName)
return element.GetText();
}

/// <summary>
/// Set the value of the Text property for an element, once it's available.
/// </summary>
public static void SetText(this IApp app, string elementName, string text)
{
var element = app.Marked(elementName);
app.WaitForElement(element);

element.SetDependencyPropertyValue("Text", text);
}

/// <summary>
/// Get bounds rect for an element.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
namespace TestHarness.Ext.Navigation.Apps.Regions;

public class FourthData
public class RegionEntityData
{
public string Name { get; set; }

public RegionEntityData(string name)
{
Name = name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Page x:Class="TestHarness.Ext.Navigation.Apps.Regions.RegionsFirstTbiDataPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TestHarness.Ext.Navigation.Apps.Regions"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uen="using:Uno.Extensions.Navigation.UI"
xmlns:utu="using:Uno.Toolkit.UI"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Center"
Style="{StaticResource TitleLarge}"
Text="First Tab" />
<TextBlock Margin="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="Data persists between both the tabs." />
<StackPanel Grid.Row="1"
HorizontalAlignment="Center"
Orientation="Horizontal"
Spacing="4">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"
Text="Entity name:" />
<TextBox Width="400"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"
Header="First Tab"
AutomationProperties.AutomationId="RegionsFirstTbiDataPageTextBox"
Style="{StaticResource FilledTextBoxStyle}"
Text="{Binding Entity.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace TestHarness.Ext.Navigation.Apps.Regions;

public sealed partial class RegionsFirstTbiDataPage : Page
{
public RegionsFirstTbiDataPage()
{
this.InitializeComponent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace TestHarness.Ext.Navigation.Apps.Regions;

public record RegionsFirstTbiDataViewModel
{
public RegionEntityData? Entity { get; set; }

public RegionsFirstTbiDataViewModel(RegionEntityData? entity)
{
Entity = entity;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,7 @@
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<utu:NavigationBar Content="Fourth Page">
<utu:NavigationBar.MainCommand>
<AppBarButton>
<AppBarButton.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/Images/back.png" />
</AppBarButton.Icon>
</AppBarButton>
</utu:NavigationBar.MainCommand>
</utu:NavigationBar>
<utu:NavigationBar Content="Fourth Page" />
<StackPanel Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace TestHarness.Ext.Navigation.Apps.Regions;

public class RegionsFourthViewModel (FourthData fourthData)
public class RegionsFourthViewModel (RegionEntityData fourthData)
{
public FourthData FourthData { get; set; } = fourthData;
public RegionEntityData FourthData { get; set; } = fourthData;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
mc:Ignorable="d">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<TextBox Grid.Row="0"
AutomationProperties.AutomationId="RegionsHomePageTextBox"
Text="{Binding MyText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

<muxc:NavigationView Grid.Row="1"
uen:Region.Attached="True"
IsBackButtonVisible="Collapsed"
Expand All @@ -20,24 +29,37 @@
PaneDisplayMode="Auto">
<muxc:NavigationView.MenuItems>

<muxc:NavigationViewItem uen:Region.Name="RegionsFirst" Content="First Page">
<muxc:NavigationViewItem uen:Region.Name="RegionsFirst"
Content="First Page">
<muxc:NavigationViewItem.Icon>
<SymbolIcon Symbol="Accept" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>

<muxc:NavigationViewItem uen:Region.Name="RegionsSecond" Content="Second Page">
<muxc:NavigationViewItem uen:Region.Name="RegionsSecond"
Content="Second Page">
<muxc:NavigationViewItem.Icon>
<SymbolIcon Symbol="Share" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>

<muxc:NavigationViewItem uen:Region.Name="RegionsThird" Content="Third Page">
<muxc:NavigationViewItem uen:Region.Name="RegionsThird"
uen:Navigation.Data="{Binding MyText}"
AutomationProperties.AutomationId="RegionsHomePageThirdPage"
Content="Third Page">
<muxc:NavigationViewItem.Icon>
<SymbolIcon Symbol="OutlineStar" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>

<muxc:NavigationViewItem uen:Region.Name="RegionsTbData"
AutomationProperties.AutomationId="RegionsHomePageRegionsTbData"
Content="RegionsTbData">
<muxc:NavigationViewItem.Icon>
<SymbolIcon Symbol="DockBottom" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>

</muxc:NavigationView.MenuItems>

<Grid x:Name="NavigationGrid"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.ComponentModel;

namespace TestHarness.Ext.Navigation.Apps.Regions;

public class RegionsHomeViewModel : INotifyPropertyChanged
{
private string _myText = "Type Something and go to Third Page";

public string MyText
{
get => _myText;
set
{
if (_myText != value)
{
_myText = value;
OnPropertyChanged(nameof(MyText));
}
}
}

public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Loading

0 comments on commit 7e88b5c

Please sign in to comment.