question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Page-based navigation system

See original GitHub issue

Since we are in need of a mobile-first navigation system and currently lack resources and imagination to design our own one, the proposal is to use Page-based navigation like Xamarin.Forms which is already familiar to people doing mobile apps.

So this spec aims to provide a familiar API while adding some MVVM-friendliness that Xamarin.Forms lacks.

Xamarin.Forms Shell’s counterpart is out of the scope of this spec.

Overview

The basic element of the navigation system is a Page. There can be only one “active” page, which is the inner-most page of the navigation system:

image

Each navigation page that has children only has at most one active child towards which it forwards any platform information and asks it for the desired titlebar color or window state.

Any platform events like system Back button being pressed are sent to the innermost active page. Since the active page has nothing to do with the input focus, the page host is required to traverse the tree to find the element to use as the RoutedEvent’s source.

View location

To make the system more MVVM-friendly, we are using object instead of Page. To convert an object to a page, we should do the following:

  1. if object is not a Control, we are using our standard IDataTemplate lookup mechanism and convert it into a Control
  2. if original object or IDataTemplate-produced control is not a Page, wrap it into a ContentPage

PageNavigationHost

class PageNavigationHost : Control
{
    public object Page { get; set; }
}

This control acts as the page system host and is supposed to be used as ISingleViewApplicationLifetime.MainView or as Window.Content

This is the only control in this spec that would be communicating with TopLevel’s IInsetsManager directly.

The only property is Page which is the root page of the app’s navigation system.

Page

The base class for more specialized pages. It’s not supposed to be used directly by the user code.

public class Page : TemplatedControl
{
    // The page's title, can be used by the parent page or by the page host
    public virtual string? Title { get; set; }
    
    // The page's icon, can be used by the parent page or by the page host
    public virtual IImage Icon { get; set; }
    
    // The safe area which identifies portions of the page area that are not obscured by any platform-drawn elements.
    // This property is set by the parent page or by the page host. 
    // The parent page is responsible for adjusting this value to account for padding
    //   that's already added by the parent page's content
    public virtual Thickness SafeAreaPadding { get; set; }
    
    // The system bar theme that is desired by the current page. 
    // The parent Page can ignore this property if it displays its own content adjacent to the System Bar  
    public virtual SystemBarTheme? SystemBarTheme { get; set; }
    
    // The child page that should be used as the source for navigation-related RoutedEvent
    public virtual Page? ActiveChildPage { get; }
}

ContentPage

This class is supposed to serve for used-defined content mostly acts in the same way as a UserControl, but can manage SafeAreaPadding automatically

public class ContentPage : Page
{
    // Page's content, usually defined in MyPage.axaml
    public object Content { get; set; }
    
    // If this property is true, SafeAreaPadding is added to Padding property
    public bool AutomaticallyApplySafeAreaPadding {get; set;} = true;
}
<ControlTheme x:Key="SimpleContentPage" TargetType="ContentPage">
      <Setter Property="Template">
        <ControlTemplate>
          <ContentPresenter Name="PART_ContentPresenter"
                            HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                            VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Content="{TemplateBinding Content}"
                            ContentTemplate="{TemplateBinding ContentTemplate}"
                            CornerRadius="{TemplateBinding CornerRadius}" />
        </ControlTemplate>
      </Setter>
    </ControlTheme>

ContentPage uses AutomaticallyApplySafeAreaPadding, Padding and SafeAreaPadding properties to determine the Padding of PART_ContentPresenter.

MultiPage

This is a base class for pages that can have multiple child pages

public class MultiPage : Page
{
    // The list of pages. The last one is considered to be the active one.
    public IEnumerabe? Pages { get; set; }
    
    // A template to use for pages, overrides standard IDataTemplate lookup just like with ItemsControl
    public IDataTemplate? PageTemplate {get; set;}
}

NavigationPage

This Page maintains a navigation stack.

public class NavigationPage : MultiPage
{
    // Navigation bar background
    public IBrush BarBackground { get; set; }
    // Navigation bar text color
    public IBrush BarTextColor { get; set; }
    
    // Attached properties
    
    // Sets the text of the Back button when this page is active
    public static void SetBackButtonTitle(Page page, string title);
    
    // Hides or shows the Back button and controls OS back button behavior when this page is active
    // True by default
    public static void SetHasBackButton(Page page, bool hasButton);
    
    // Sets a custom control to be used as the title when this page is active, when it's null, Page.Title property is used
    public static void SetTitleView(Page page, object control);
    
    // Sets an icon to be drawn at the left of the title when this page is active
    public static void SetTitleIcon(Page page, IImage image);
}

SelectingMultiPage

This is a base class for MultiPages that can have user-initiated selection

public class SelectingMultiPage : MultiPage
{
    // The currently selected page
    public object SelectedPage {get; set;}
}

CarouselPage

A page that users can swipe from side to side to display pages of content, like a gallery.

public class CarouselPage : SelectingMultiPage
{
}

TabbedPage

A page that displays an array of tabs across the top of the screen, each of which loads content onto the screen.

This page should be using child Page’s Title and Icon properties for the tab items

public class TabbedPage : SelectingMultiPage
{
    // Tab bar background
    public IBrush BarBackground { get; set; }
    // Tab bar text color
    public IBrush BarTextColor { get; set; }
}

MasterDetailPage

A page that manages two panes of information: A master page that presents data at a high level, and a detail page that displays low-level details about information in the master. This should be implemented using our SplitView.

public class MasterDetailPage : Page
{
    public object Master {get; set;}
    public object Detail {get; set;}
    public bool IsPresented {get;set;}
}

System Back button handling

When Android’s back button is pressed, it should be intercepted by PageNavigationHost and raised as PageNavigationSystemBackButtonPressed routed event on the inner-most active Page. NavigationPage should mark it as Handled if HasBackButton == true and it has successfully popped the active page from the navigation stack

Issue Analytics

  • State:open
  • Created 7 months ago
  • Reactions:4
  • Comments:11 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
maxkatz6commented, Mar 9, 2023

Note, it could be possible in the other way around. I.e. make ReactiveUI suppose new avalonia types. Keeping implementations separated in Avalonia.ReactiveUI. As a reference https://github.com/reactiveui/ReactiveUI/blob/de86956daac9efcb20fabc7cc4ef3a438d0450d2/src/ReactiveUI.Maui/ReactiveMasterDetailPage.cs#L16 https://github.com/reactiveui/ReactiveUI.Samples/blob/main/Xamarin/MasterDetail/MasterDetail/MainPage.xaml It’s out of scope of initial prototype though.

1reaction
kekekekscommented, Mar 9, 2023

We have a policy to not depend on 3rd party MVVM frameworks. Avalonia should be 100% usable without an MVVM framework. So any existing RxUI features are orthogonal to the page-based navigation system.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to develop page based navigation UI on Apple Watch
The project uses SwiftUI for the content of the screens, but top-level navigation is handled by building it in the Storyboard and connecting...
Read more >
A WatchKit Page-based Navigation Tutorial
This chapter will work through the creation of an example that makes use of both page-based navigation and modal interface controller presentation within...
Read more >
Implementing Page Based Navigation for SwiftUI Apple ...
Here is a guide how to implement page-based navigation for watchOS ... First, create a new SwiftUI view, for example called CounterView.
Read more >
Page Based Navigation WPF For Beginners
In WPF application, you can navigate from one page to another page; such a navigation system is known as Page Based Navigation.
Read more >
WatchKit: A Coordinator pattern for Page-based Navigation
The thing about this is there's no real controller for these pages, and not every use case will make Storyboard-based segue navigation possible....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found