Skip to content

Commit

Permalink
Merge pull request #2573 from unoplatform/mergify/bp/release/stable/5…
Browse files Browse the repository at this point in the history
….0/pr-2320

docs: Update existing reference navigation docs (backport #2320)
  • Loading branch information
agneszitte authored Sep 26, 2024
2 parents 9d5fef6 + ac902aa commit 58a66c9
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 97 deletions.
35 changes: 7 additions & 28 deletions doc/Reference/Navigation/Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,22 @@ There are `INavigator` extension methods that accept a variety of parameters, de

## Navigation Controls

An application typically has one or more views responsible for controlling navigation. Eg a Frame that navigates between pages, or a TabBar that switches tabs
An application typically has one or more views responsible for controlling navigation. For example, a Frame that navigates between pages, or a TabBar that switches tabs

Navigation controls can be categorized in three distinct groups with different Navigation goals.
Navigation controls can be categorized into three distinct groups with different Navigation goals.

| Content-Based | Has a content area that's used to display the current view |
|----------------------|------------------------------------------------------------------------------------------------------------------------|
| ContentControl | Navigation creates an instance of a control and sets it as the Content |
| Panel (eg Grid) | Navigation sets a child element to Visible, hiding any previously visible child. Two scenarios:<br> - An existing child is found. The child is set to Visible<br> - No child is found. A new instance of a control is created and added to the Panel. The new instance is set to visible<br>Note that when items are hidden, they're no removed from the visual tree |
| Frame | Forward navigation adds a new page to the stack based <br>Backward navigation pops the current page off the stack<br>Combination eg forward navigation and clear back stack |
| Panel (eg Grid) | Navigation sets a child element to Visible, hiding any previously visible child. Two scenarios:<br> - An existing child is found. The child is set to Visible<br> - No child is found. A new instance of a control is created and added to the Panel. The new instance is set to visible<br>Note that when items are hidden, they're not removed from the visual tree |
| Frame | Forward navigation adds a new page to the stack-based <br>Backward navigation pops the current page off the stack<br>Combination, for example, forward navigation and clear back stack |
| | |
| **Selection-Based** | **Has selectable items** |
| NavigationView | Navigation selects the NavigationViewitem with the correct Region.Name set |
| **Selection-Based** | **Has selectable items** |
| NavigationView | Navigation selects the NavigationViewItem with the correct Region.Name set |
| TabBar | Navigation selects the TabBarItem with the correct Region.Name set |
| | |
| **Prompt-Based (Modal)** | **Modal style prompt, typically for capturing input from user** |
| **Prompt-Based (Modal)** | **Modal style prompt, typically for capturing input from user** |
| ContentDialog | Forward navigation opens a content dialog <br>Backward navigation closes the dialog |
| MessageDialog | Forward navigation opens a MessageDialog<br>Backward navigation closes the MessageDialog |
| Popup | Forward navigation opens the popup<br>Backward navigation closes the popup |
| Flyout | Forward navigation opens the flyout<br>Backward navigation closes the flyout |

## Regions

A region is the abstraction of the view responsible for handling navigation.

Regions are structured into a logical hierarchical representation that shadows the navigation-aware controls in the visual hierarchy. The hierarchy allows navigation requests to be propagated up to parent and down to child regions as required.

Regions are specified by setting Region.Attached="true" on a navigation control (eg Frame, ContentControl, Grid).

```csharp
<ContentControl uen:Region.Attached="true" />
```

Pushing a view to this region:
`navigator.NavigateRouteAsync(this,"ProductDetails");`
or
`navigator.NavigateViewAsync<ProductDetailsControl>(this);`
or
`navigator.NavigateViewModelAsync<ProductDetailsViewModel(this);`
or
`navigator.NavigateDataAsync(this, selectedProduct);`
15 changes: 11 additions & 4 deletions doc/Reference/Navigation/Navigation.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
---
uid: Reference.Navigation.Overview
---
# Navigation Reference

The pages in this section provide extended reference information for Navigation-related features.
# Navigation Reference Overview

## Have questions or feedback?
Welcome to the Navigation Reference documentation! Here, you will find detailed information about the specific APIs utilized across various aspects of navigation. These topics will guide you through the design principles, navigation regions, qualifiers, and essential interfaces like `INavigator`. You'll also learn how to implement custom request handlers effectively. Use the table below to explore each topic further.

* Help us shape the documentation for this topic by providing feedback on the Uno.Extensions [repo](https://github.com/unoplatform/uno.extensions/discussions/categories/general)
| **Topic** | **Description** |
|--------------------------|---------------------------------------------------------------------------------|
| [Design](xref:Reference.Navigation.Design) | Learn about the foundational design concepts behind the navigation system. |
| [Navigation Region](xref:Reference.Navigation.Regions) | Understand how navigation regions work and how to leverage them in your app. |
| [Qualifiers](xref:Reference.Navigation.Qualifiers) | Explore how qualifiers are used to influence navigation decisions. |
| [INavigator](xref:Reference.Navigation.Navigator) | Discover the `INavigator` interface and how it orchestrates navigation. |
| [Implement IRequestHandler](xref:Reference.Navigation.RequestHandler) | Learn how to implement custom request handlers for specific navigation scenarios. |

If you're looking for an introduction, How-Tos, or instructions on how to install navigation, please refer to the [Navigation Introduction documentation](xref:Uno.Extensions.Navigation.Overview).
42 changes: 37 additions & 5 deletions doc/Reference/Navigation/NavigationRegion.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
---
uid: Reference.Navigation.Regions
---

# What is a Navigation Region

## Regions

A region is a part of the user interface that manages navigation.

Regions are organized in a hierarchy that mirrors the structure of the navigation controls in the user interface. This hierarchy allows navigation commands to move up to parent regions or down to child regions as needed.

To specify a region, you set `Region.Attached="true"` on a navigation control (like Frame, ContentControl, or Grid).

```xml
<ContentControl uen:Region.Attached="true" />
```

## Region Name

Regions can be named by specifying the Region.Name="XXX" property.
You can name a region by setting the `Region.Name="RegionName"` property.

For selection-based regions, the selectable items (NavigationViewItem, TabBarItem, …) are identified using the Region.Name property
In selection-based regions, the selectable items (like `NavigationViewItem`, `TabBarItem`, etc.) are identified using the Region.Name property.

```csharp
```xml
<muxc:NavigationView uen:Region.Attached="true">
<muxc:NavigationView.MenuItems>
<muxc:NavigationViewItem Content="Products" uen:Region.Name="Products" />
Expand All @@ -20,6 +33,25 @@ For selection-based regions, the selectable items (NavigationViewItem, TabBarIte
```

Switching selected item:
`naviator.NavigateRouteAsync(this,"Deals");`

- Define what a navigation region is and how the hierarchy of regions is created with the Region.Attached property
```csharp
navigator.NavigateRouteAsync(this, "Deals");
```

or

```csharp
navigator.NavigateViewAsync<DealsControl>(this);
```

or

```csharp
navigator.NavigateViewModelAsync<DealsViewModel>(this);
```

or

```csharp
navigator.NavigateDataAsync(this, selectedDeal);
```
85 changes: 83 additions & 2 deletions doc/Reference/Navigation/NavigationRequestHandler.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,87 @@
---
uid: Reference.Navigation.RequestHandler
---
# Building a Custom Request Handler

- Show how to create a custom NavigationRequestHandler that will intercept event on a control in order to trigger navigation.
# Building a Custom Request Handler with `IRequestHandler`

`IRequestHandler` plays a vital role in linking UI controls to navigation requests. This interface allows you to define how a view should respond to a navigation action and helps in handling the routing or navigation logic within a control.

This guide will explain how to create a custom `IRequestHandler` for your own controls, providing examples and best practices to ensure correct implementation.

## What is `IRequestHandler`?

The `IRequestHandler` interface defines two essential methods:

- **`CanBind(FrameworkElement view)`**: This method determines whether the handler can be bound to the given view.
- **`Bind(FrameworkElement view)`**: This method binds the handler to the view, attaching any necessary logic or event listeners.

```csharp
public interface IRequestHandler
{
bool CanBind(FrameworkElement view);
IRequestBinding? Bind(FrameworkElement view);
}
```

These methods allow you to specify how the handler interacts with the control and how it should respond to navigation requests.

## Steps to Create a Custom `IRequestHandler`

### 1. Implement `IRequestHandler`

Start by creating a class that implements the `IRequestHandler` interface. The key part here is to define the specific logic for the control that you want to bind.

Alternatively you could extend one of the provided base classes, depending on your needs:

- `ControlRequestHandlerBase<TControl>`
This base class is useful when you are binding to a specific control type. It already implements `IRequestHandler` and simplifies the process by letting you focus on control-specific logic.

```csharp
public abstract record ControlRequestHandlerBase<TControl>(ILogger Logger) : IRequestHandler;
```

- `ActionRequestHandlerBase<TView>`
If your control needs to handle callback actions when subscribing and unsubscribing to events (such as click events), you should extend `ActionRequestHandlerBase`. This base class adds the necessary infrastructure for subscribing/unsubscribing from control-specific events.

```csharp
public abstract record ActionRequestHandlerBase<TView>(ILogger Logger, IRouteResolver Resolver) : ControlRequestHandlerBase<TView>(Logger)
where TView : FrameworkElement;
```

### 2. Check if the Control Can Be Bound (`CanBind`)

> [!NOTE]
> If you are extending `ControlRequestHandlerBase<TControl>` or `ActionRequestHandlerBase<TView>` the `CanBind` method is already implemented. You can check its implementation [here](https://github.com/unoplatform/uno.extensions/blob/d4fa6e44326bf140d08fb1eb205d4acba1ffe202/src/Uno.Extensions.Navigation.UI/Controls/ControlRequestHandlerBase.cs#L14-L37).
In the `CanBind` method, verify that the control you are working with is the correct type. For instance, if you are creating a handler for a custom control named `MyCustomControl`, the `CanBind` method should return `true` only if the `FrameworkElement` is of that type.

Example:

```csharp
public bool CanBind(FrameworkElement view)
{
return view is MyCustomControl;
}
```

This method ensures that your handler will only attempt to bind to appropriate controls.

### 3. Bind the Control to Navigation (`Bind`)

The `Bind` method is where the actual magic happens. Here, you attach event handlers or other logic to respond to the control's events, such as clicks, pointer interactions, or selection changes.

### 4. Manage Resource Cleanup and Unbinding

Ensure that the events are detached properly when the control is unloaded to avoid memory leaks. Your custom `IRequestBinding` should take care of unsubscribing from events when no longer needed.

In the `Bind` method, you should return a `RequestBinding` object that takes care of attaching and detaching event handlers as the control is loaded and unloaded.

### 5. Register Your Custom `IRequestHandler`

After implementing the `IRequestHandler`, you need to register it in your application. Typically, this is done in your service registration:

```csharp
services.AddSingleton<IRequestHandler, MyCustomControlRequestHandler>();
```

This allows your custom handler to be automatically picked up and used whenever a navigation request involves `MyCustomControl`.
119 changes: 116 additions & 3 deletions doc/Reference/Navigation/Navigator.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
uid: Reference.Navigation.Navigator
---
# Custom INavigator Implementation

## INavigator
# INavigator

The `INavigator` is an interface that handles navigating between different views or pages in an application. It manages how you move from one part of your app to another, often keeping track of routes and handling navigation-related tasks.

The `NavigateAsync` method on the `INavigator` interface accepts a NavigationRequest parameter and returns a Task that can be awaited in order to get a NavigationResponse.

Expand All @@ -14,6 +15,118 @@ public interface INavigator
}
```

The `CanNavigate` method checks if the app can navigate to a specific route. It takes a `Route` parameter and returns a `Task<bool>`, which tells you whether navigation is possible. Before navigating, you can use CanNavigate to see if it's allowed or makes sense.

```csharp
public interface INavigator
{
Task<bool> CanNavigate(Route route);
}
```

## `INavigator` Extension Methods

There are `INavigator` extension methods that accept a variety of parameters, depending on the intent, which are mapped to a corresponding combination of Route and Result values.

- Walk through a simple INavigator implementation
### Navigates to the specified route

Navigates to the view associated with the route as defined in the `RouteMap`.

```csharp
Task<NavigationResponse?> NavigateRouteAsync(this INavigator navigator, object sender, string route, string qualifier = Qualifiers.None, object? data = null, CancellationToken cancellation = default)
```

Usage:

```csharp
var navigationResponse = await _navigator.NavigateRouteAsync(this, route: "MyExample");
```

### Navigates to the specified view

Navigates to the specified view.

```csharp
Task<NavigationResponse?> NavigateViewAsync<TView>(this INavigator navigator, object sender, string qualifier = Qualifiers.None, object? data = null, CancellationToken cancellation = default)
```

Usage:

```csharp
var response = await _navigator.NavigateViewAsync<MyExamplePage>(this);
```

### Navigates to the view through the viewmodel

Navigates to the view associated with the viewmodel as defined in the `ViewMap`.

```csharp
Task<NavigationResponse?> NavigateViewModelAsync<TViewViewModel>(this INavigator navigator, object sender, string qualifier = Qualifiers.None, object? data = null, CancellationToken cancellation = default)
```

Usage:

```csharp
var response = await _navigator.NavigateViewModelAsync<MyExampleViewModel>(this);
```

### Navigates to the view associated with the data type

Navigates to the view associated with the data type as defined in the `DataViewMap`. The type of the data object will be used to resolve which route to navigate to.

```csharp
Task<NavigationResponse?> NavigateDataAsync<TData>(this INavigator navigator, object sender, TData data, string qualifier = Qualifiers.None, CancellationToken cancellation = default)
```

Usage:

```csharp
await _navigator.NavigateDataAsync<MyData>(this);
```

### Navigates to a Route and Retrieving Result Data

Navigates to a route and get the result of the specified data type, as defined in the `ResultDataViewMap`.

```csharp
Task<NavigationResultResponse<TResult>?> NavigateForResultAsync<TResult>(this INavigator navigator, object sender, string qualifier = Qualifiers.None, object? data = null, CancellationToken cancellation = default)
```

Usage:

```csharp
var returnObject = await _navigator.NavigateForResultAsync<MyObject>(this);
```

Alternatively the `GetDataAsync<TResult>()` could be used:

```csharp
var returnObject = await _navigator.GetDataAsync<MyObject>(this);
```

All methods mentioned also have `ForResultAsync` variations available. These variations can be used if you need to retrieve data while navigating. For example:

```csharp
NavigateRouteForResultAsync<TResult>()
NavigateViewForResultAsync<TView, TResult>()
NavigateViewModelForResultAsync<TViewViewModel, TResult>()
NavigateDataForResultAsync<TData, TResult>()
```

## NavigationResponse

The `NavigationResponse` object encapsulates the result of a navigation operation. It includes:

- **Route**: The route that was navigated to.
- **Success**: Indicates whether the navigation was successful.
- **Navigator**: The `INavigator` instance that processed the final segment of the route.

## NavigationRequest

The `NavigationRequest` object represents a request for navigation. It includes:

- **Sender**: The originator of the navigation request.
- **Route**: The route to navigate to.
- **Cancellation**: An optional `CancellationToken` to cancel the navigation.
- **Result**: An optional type for the result of the navigation.
- **Source**: An optional `INavigator` instance that initiated the navigation.
Loading

0 comments on commit 58a66c9

Please sign in to comment.