XamarinForms Shell navigation with TabBar items of previous page - xamarin.forms

I need to create a "detail" page that when I clicked from my "master" page, on some button, it redirects me to that "detail" page with navigation bar which has back button.
Code for this is as follows:
protected async Task GoToPage(string route)
{
await Shell.Current.GoToAsync(route);
}
Question is: how to preserve downtab from my previous master page, to show as well as on my detail page?
design is look like this:
https://i.imgur.com/tBqHhH8.png
I only have navigation bar.
In my AppShell.xaml I defined TabBar section for my "master" page ("master" page is first in my tabbar menu).
Should I create separate TabBar for my "detail" page and somehow to insert navigation bar?

You can put your Page to the MenuItem tab.
<MenuItem Text="About"
IconImageSource="info.png"
Command="{Binding AboutPageCommand}" />
Background code.
public ICommand AboutPageCommand => new Command(async () => await NavigateToAboytPageAsync());
async Task NavigateToAboytPageAsync()
{
await Shell.Current.GoToAsync($"aboutPage");
Shell.Current.FlyoutIsPresented = false;
}
Register the page in the App.xaml.cs
Routing.RegisterRoute("aboutPage", typeof(AboutPage));

There is a property called IsTabStop you can set in the Tab tag which can preserve downtab from your previous master page:
IsTabStop, of type bool, indicates whether a Tab is included in tab
navigation. Its default value is true, and when its value is false the
Tab is ignored by the tab-navigation infrastructure, irrespective if a
TabIndex is set.
You can set it to false at specific tab to keep downtab at that tab.
Code example:
<!-- Your Pages -->
<TabBar>
<Tab Title="Master" Icon="tab_feed.png" IsTabStop="False">
<ShellContent ContentTemplate="{DataTemplate local:ItemsPage}" />
</Tab>
<Tab Title="About" Icon="tab_about.png" >
<ShellContent ContentTemplate="{DataTemplate local:AboutPage}" />
</Tab>
</TabBar>
Here is the document: shell/tabs

Related

Any ideas why "GoToAsync" doesn't work in AppShell.cs?

Register detail page routes
AppShell.cs
public partial class AppShell : Xamarin.Forms.Shell
{
public AppShell(string message)
{
InitializeComponent();
Routing.RegisterRoute(nameof(ItemDetailPage), typeof(ItemDetailPage));
Routing.RegisterRoute(nameof(NewItemPage), typeof(NewItemPage));
Routing.RegisterRoute("LoginPage", typeof(LoginPage));
if (message != "main") {
int itemid = Int32.Parse(message);
var item = App.Database.GetItemAsyncId(itemid).Result;
var itemValue = new Models.Item
{
Id = item.Id,
Description = item.Description,
Text = item.Text
};
OnItemSelected(itemValue);
}
}
public async void OnItemSelected(Item item)
{
await Shell.Current.GoToAsync("LoginPage");
//Shell.Current.GoToAsync($"{nameof(ItemDetailPage)}?{nameof(ItemDetailViewModel.ItemId)}={item.Id}");
}}
When I launch the application on the emulator, the application should launch the pages:
"AboutPage" - starts;
"LoginPage" - it does not start.
"LoginPage" should load using
Shell.Current.GoToAsync("LoginPage");
The editor does not show errors.
is navigationStack
<FlyoutItem x:Name="ItemsPage" Icon="icon_feed.png">
<ShellContent Route="ItemsPage" ContentTemplate="{DataTemplate local:ItemsPage}" />
</FlyoutItem>
<FlyoutItem x:Name="About" Icon="icon_about.png">
<ShellContent Route="AboutPage" ContentTemplate="{DataTemplate local:AboutPage}" />
</FlyoutItem>
<!-- When the Flyout is visible this will be a menu item you can tie a click behavior to -->
<MenuItem Text="Logout" StyleClass="MenuItemLayoutStyle" Clicked="OnMenuItemClicked">
</MenuItem>
<!--
TabBar lets you define content that won't show up in a flyout menu. When this content is active
the flyout menu won't be available. This is useful for creating areas of the application where
you don't want users to be able to navigate away from. If you would like to navigate to this
content you can do so by calling
await Shell.Current.GoToAsync("//LoginPage");
-->
<TabBar>
<ShellContent Route="LoginPage" ContentTemplate="{DataTemplate local:LoginPage}" />
</TabBar>
Does your method get called?
What is the value of the message variable? Check if the if (message != "main") is true sometimes
It will only happen on the constructor.
[Edit]
To navigate a page of a Shell inside the constructor, you will have to replace "Shell.Current.GoToAsync('LoginPage')" with "this.GoToAsync('LoginPage')"
At the moment of the instance, the created Shell is not the "Current" shell of the application

Xamarin Forms Shell GotoAsync

I am using the Shell template from VS2019 in a Xamarin Forms app.I have two pages listed in the shell flyout.
<FlyoutItem Title="Home" Icon="icon_about.png">
<ShellContent Route="HomePage" ContentTemplate="{DataTemplate local:HomePage}" />
</FlyoutItem>
<FlyoutItem Title="Logoff time" Icon="icon_about.png">
<ShellContent Route="SecondPage" ContentTemplate="{DataTemplate
local:SecondPage}" />
</FlyoutItem>
Using the hamburger menu I navigate away from the Homepage to another page SecondPage. I have a button on the SecondPage which perform an action and from that action I want to navigate back to the HomePage. When I use this code:
var route = $"{nameof(HomePage)}";
await Shell.Current.GoToAsync(route);
The HomePage shows but there is a back button instead of the hamburger menu. If I tap the hamburger icon the page navigates back to the SecondPage.
How do I navgate from SecondPage to HomePage and show the hamburger icon?
With so little information (not knowing the details of how your navigation is implemented) it's hard to help you. But the first things that come to mind are the following you could try.
Try one of the following for popping the SecondPage and navigating back to:
await Shell.Current.Navigation.PopAsync();
or
await Shell.Current.Navigation.PopModalAsync();
And if you have a navigation bar with a back button that you don't want then try this in the HomePage.xaml
NavigationPage.HasNvaigationBar="False"
or
Shell.NavBarIsVisible="False"
The two
//
before the route will accomplish my aims.This resets the root page apparently so HomePage once again become the root page.
var route = $"//{nameof(HomePage)}"
I have solved this issue in my own project; I wanted this page to show up at the start of the application, using the navigation class to push the screen and then pop it when the user clicks a button. I had the same issue of a back button showing up, instead of the hamburger icon, when moving the user to the HomePage. I hope this helps someone in the future.
Classes are as follows:
public partial class AppShell : Xamarin.Forms.Shell {
public AppShell() {
InitializeComponent();
Routing.RegisterRoute(nameof(ItemDetailPage), typeof(ItemDetailPage));
Routing.RegisterRoute(nameof(NewItemPage), typeof(NewItemPage));
Routing.RegisterRoute(nameof(HomePage), typeof(HomePage));
Routing.RegisterRoute(nameof(NewUserPage), typeof(NewUserPage));
this.Navigation.PushAsync(new NewUserPage());
}
}
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NewUserPage : ContentPage {
public NewUserPage() {
InitializeComponent();
this.BindingContext = new NewUserViewModel();
Shell.SetNavBarIsVisible(this, false);
}
}
class NewUserViewModel : BaseViewModel {
public NewUserViewModel() {
NewUserClickedCommand = new Command(OnNewUserClicked);
}
public Command NewUserClickedCommand { get; }
private async void OnNewUserClicked() {
await Shell.Current.Navigation.PopAsync();
}
}
And the XAML file for NewUserPage:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Project.Views.NewUserPage">
<ContentPage.Content>
<StackLayout>
<Label Text="This is a page for new users"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<Button Text="Click for New User" BackgroundColor="{StaticResource Primary}" Command="{Binding NewUserClickedCommand}"></Button>
</StackLayout>
</ContentPage.Content>
</ContentPage>

Master Detail breaks parameters?

I don't need a MasterDetail until I've navigated through two regular ContentPages first, where I collect information that the MasterDetail will need. The ContentPages make use of the INavigationParameters sent to OnNavigatedTo and OnNavigatedFrom for that collected information.
The the SecondPage viewmodel handles a button command wherein the handler calls the MasterDetail:
NavigationService.NavigateAsync("CompareSelectionsMasterDetail/CompareSelectionsNavigationPage/RangeCircle",
parameters);
Sure enough, the RangeCircleViewModel.OnNavigatedTo receives the parameters. However, when I go back (like with the Android back button), its OnNavigatedFrom is not called, thereby sending null parameters back leaving my ContentPages with no idea what their state was.
The viewmodel for CompareSelectionMasterDetail doesn't do anything and the viewmodel for CompareSelectionsNavigationPage just does this:
public class CompareSelectionsNavigationPage : NavigationPage, INavigationPageOptions
{
public bool ClearNavigationStackOnNavigation => false;
}
I'm not sure what it means to have a NavigationPage in the MasterDetailPage XAML and the separate CompareSelectionsNavigationPage that I call with Prism but if I remove the XAML one, the RangeCircle page doesn't render. If I only have the XAML one and change the Prism navigation to CompareSelectionsMasterDetail/NavigationPage/RangeCircle then I get the same behavior as using both NavigationPages.
My MasterDetail XAML is simple.
> <MasterDetailPage.Master>
> <NavigationPage Title="Required Foo" Icon="hamburger_icon.png">
> <x:Arguments>
> <ContentPage Title="Menu">
> <StackLayout Padding="40">
> <!-- TODO: // Update the Layout and add some real menu items -->
> <Label Text="Hello John"/>
> <Button Text="Range Circle" Command="{Binding NavigateToRangeCircleCommand}" CommandParameter="ViewA" />
> </StackLayout>
> </ContentPage>
> </x:Arguments>
> </NavigationPage> </MasterDetailPage.Master>
Every example I can find of Master Detail (especially with Prism) starts off the app with Master Detail page. I'm not sure if my usage is what breaks OnNavigatedFrom? (Basically ContentPage(Page1)->ContentPage(Page2)->MasterDetail->NavigationPage->NavigationPage->ContentPage(RangeCircle))
The individual ContentPages don't have a problem calling OnNavigatedFrom.

ReactiveTabbedPage set child view to navigate to

I am using ReactiveTabbedPage 'HomeTabbedPage' which has 4 view as shown
<rxui:ReactiveTabbedPage x:Class="HomeTabbedPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:views="clr-namespace:MeasuringPoint.Views;assembly=MeasuringPoint"
xmlns:controls="clr-namespace:MeasuringPoint.Controls;assembly=MeasuringPoint"
xmlns:resx="clr-namespace:MeasuringPoint.Resources;assembly=MeasuringPoint"
xmlns:page="clr-namespace:MeasuringPoint.Pages"
xmlns:vms="clr-namespace:MeasuringPoint.ViewModels"
x:TypeArguments="vms:HomeTabbedViewModel" BackgroundColor="White">
<views:CreateView x:Name="createView" Title="Create"/>
<views:HistoryView x:Name="historyView" Title="History"/>
<views:SyncView x:Name="syncView" Title="Sync"/>
<views:SettingsView x:Name="settingsView" Title="Settings" />
</rxui:ReactiveTabbedPage>
By default Createview is the one shown. I need to navigate to another 'Page' and come back to another child view 'SettingsView'
If I call
HostScreen
.Router
.Navigate
.Execute(new SettingsViewModel())
.Subscribe();
The SettingsView is displayed but the tabs disappear.
If I can navigate back to the HomeTabbedPage
HostScreen
.Router
.Navigate
.Execute(new HomeTabbedViewModel())
.Subscribe();
Then the CreateView is displayed.
Am I missing something here?

titanium alloy open view from button

After being away from Titanium for awhile, seems I forgot even the simplest tasks. Case in point: I have a stack of buttons on a main page. Each button should open its respective view when clicked. How do I open that associated view when the button is clicked?
View
<Alloy>
<Tab title="Home">
<Window class="container">
<Label>This is the Home View</Label>
<ImageView id="kplLogo"/>
<View id="homeNav">
<Button class="navButton" id="recognizeButton" title="Recognize" onClick="doClick" />
<Button class="navButton" id="reactButton" title="React"/>
<Button class="navButton" id="reportButton" title="Report"/>
</View>
</Window>
</Tab>
</Alloy>
When the user clicks a button, such as the recognizeButton for now, it should open the recognizeView. I know it's simple, but I'm having brainlock at the moment.
Thanks for the help. Ask if you need more detail.
First, add a property to each Button which will make possible to invoke the child View controllers, such as:
<Button id="recognizeButton" title="Recognize" child_controller="recognizeView" />
Also use an id on the <tab> element:
<Tab id="hometab">
Then, in the controller, add the event listener:
$.recognizeButton.addEventListener('click', function(e) {
if (e.source.child_controller) {
controller = Alloy.createController(e.source.child_controller);
$.hometab.open(controller.getView());
}
});
This will open a new window in the same tab, preserving history so that when you click reutrn, you'll be back in the main tab. If you need a broader example, check this: https://github.com/asiviero/drupanium_app/tree/master/app in which I use the home view in that manner, opening up views from controllers inside "includes" folder

Resources