ReactiveTabbedPage set child view to navigate to - xamarin.forms

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?

Related

Xamarin.Forms: Display additional tab on specific Platform

Good morning
I have a TabbedPage in my application. Due to restictions I would like to display one more tab on Android than on iOS.
My current TabbedPage look like:
<TabbedPage>
...
<ContentPage x:Name="Page1"/>
<ContentPage x:Name="Page2"/>
<ContentPage x:Name="Page3"/>
<ContentPage x:Name="Page4"/>
</TabbedPage>
I have decided to render Page3 only for Android. I changed my code into:
<TabbedPage>
...
<ContentPage x:Name="Page1"/>
<ContentPage x:Name="Page2"/>
<OnPlatform x:TypeArguments="Page">
<On Platform="Android">
<On.Value>
<ContentPage x:Name="Page3"/>
</On.Value>
</On>
</OnPlatform>
<ContentPage x:Name="Page4"/>
</TabbedPage>
This leads to runtime exception when entering into this TabbedPage:
An error occurred: 'Value cannot be null. Parameter name: item'.
Callstack: ' at
Xamarin.Forms.ObservableWrapper`2[TTrack,TRestrict].Add (TRestrict
item) [0x00008] in D:\a\1\s\Xamarin.Forms.Core\ObservableWrapper.cs:27
I tried to remove x:Name but did not work. Any ideas?
PS. As a workaround I am always able to:
Constructor()
{
if(Runtime.IsIOS)
{
this.Children.Remove(this.Page3)
}
}
However would be better to not render it at all and have it at XAML level.
Unless someone comes up with a way to do this in XAML, this is the best you can do (building on Jason's comment):
xaml:
<ContentPage x:Name="Page1"/>
<ContentPage x:Name="Page2"/>
<!-- no Page3 in XAML -->
<ContentPage x:Name="Page4"/>
c#:
private ContentPage page3;
Constructor()
{
InitializeComponent();
if (Runtime.IsAndroid)
{
page3 = new ContentPage();
// After pages 1 and 2.
Children.Insert(2, page3);
}
}
// Elsewhere in code-behind.
if (page3 != null)
{
...refer to page3...
}
This has the advantage of not constructing the page at all on iOS.
It also makes it easy to test whether page3 is there (page3 != null).
In practice, the other pages will typically be their own classes. Given partial class Page3 : ContentPage elsewhere:
private Page3 page3;
...
page3 = new Page3();
According to this,xaml is an alternative to programming code for instantiating and initializing objects, and organizing those objects in parent-child hierarchies.If you add one additional tab on android it will cause a NullReferenceException on ios.So you may want to do it in codebehind with Device.RuntimePlatform.
Here is my test you can refer to:
switch (Device.RuntimePlatform)
{
case Device.Android:
Children.Add(page1);
Children.Add(page2);
break;
case Device.iOS:
Children.Add(page1);
break;
case Device.UWP:
Children.Add(page1);
break;
}

XamarinForms Shell navigation with TabBar items of previous page

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

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.

xamarin form : how to disable tab button

i have tabbed page in which i have 3 tabs i need to enabled=false 2nd and 3rd tabs. I need to work like form wizard when i finish 1st page then 2nd tab should be enabled=true, i have tried like this as my code below, but its not working
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="maintab"
x:Class="OnlineKIHStore.Views.CheckOuts"
BarBackgroundColor="#2F3C51"
Title="Checkout"
BarTextColor="White">
<ContentPage x:Name="first" Title="Login/Register" Icon="user" IsEnabled="True">
<StackLayout>
</ContentPage>
<ContentPage x:Name="second" Title="SHIPPING" Icon="shipping" IsEnabled="False">
</ContentPage>
<ContentPage x:Name="third" Title="Payment" Icon="payment" IsEnabled="False">
</ContentPage>
</TabbedPage>
If you mention "enabled=false" -You can click the tabbar. It will load the page, but will not allow to do any action in that particular page.
If you mention "IsVisible="False"" - Still you can click the tabbar. You can just see a empty screen.
One Method - At first don't load the tabbar you wanted. After finishing the page 1, inserted the tabbar you needed.
Here is my sample as per your code
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="maintab"
x:Class="OnlineKIHStore.Views.CheckOuts"
BarBackgroundColor="#2F3C51"
Title="Checkout"
BarTextColor="White">
<TabbedPage.Children>
<firstPage Title="Login/Register" Icon="user"/>
<thirdPage Title="Payment" Icon="payment"/>
</TabbedPage.Children>
In .CS file
public CheckOuts
{
InitializeComponent();
if(check_finished_pageone)
{
Children.Insert(2, new SecondPage { Title = "SHIPPING", Icon = "shipping" });
}}
Once you are done with the page1 reload the tabpage. So now your second page with new tab bar will be added.
One more method is once the page 2 is clicked you can navigate to page 1 prompting the user to finish the page 1
e<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="maintab"
x:Class="OnlineKIHStore.Views.CheckOuts"
BarBackgroundColor="#2F3C51"
Title="Checkout"
CurrentPageChanged="TabbedPage_CurrentPageChanged"
BarTextColor="White">
.CS file
private async void TabbedPage_CurrentPageChanged(object sender, EventArgs e){
var i = this.Children.IndexOf(this.CurrentPage);
if (i == 2)
{
await App.Current.MainPage.DisplayAlert("Error", "You have to finish the page one before navigating this page", "Ok"))
this.CurrentPage = this.Children[0];
}}
This method allows you click the 2nd tabbar, but it will re-direct to page 1 again.

Back button missing on NavigationPage

I'm using Prism.Forms 6.3 in a Xamarin.Forms 2.3.4.247 project. I'm having a hard time tracking why the back arrow button isn't visible when I navigate to a details page within a Master/Detail setup.
I can navigate to the Pages just fine, but the back-button never shows up. Instead, the hamburger menu icon is always visible. This is my "Master" page.
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FloatSink.Apps.Mobile.Views.ValueConverters"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="FloatSink.Apps.Mobile.Views.MainPage"
BackgroundColor="Blue">
<MasterDetailPage.Master>
<ContentPage Title="Menu">
<StackLayout Padding="40">
<Label Text="Hello" />
<Button Text="Feed" Command="{Binding NavigateCommand}" CommandParameter="NavigationPage/FeedPage" />
<Button Text="Options" Command="{Binding NavigateCommand}" CommandParameter="NavigationPage/OptionsPage" />
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</MasterDetailPage>
This is two of my Detail pages.
<?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="FloatSink.Apps.Mobile.Views.FeedPage">
<Label Text="Hello from Feed Page!" />
</ContentPage>
<?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="FloatSink.Apps.Mobile.Views.OptionsPage">
<Label Text="Hello from Options Page!" />
</ContentPage>
I navigate to it using the CommandParameter specified in my Master page, using the navigation service in the MainPageViewModel.
private void NavigateToPage(string uri)
{
this.navigationService.NavigateAsync(uri);
}
I've setup my NavigationPage like this during the startup of the app so I land on the FeedPage first, then I can navigate to the OptionsPage.
public class App : PrismApplication
{
public App(IPlatformInitializer dependencyRegister) : base(dependencyRegister) { }
protected override void OnInitialized()
{
base.NavigationService.NavigateAsync("MainPage/NavigationPage");
}
protected override void RegisterTypes()
{
var builder = new ContainerBuilder();
builder.RegisterModule<NavigationRegistration>();
builder.RegisterModule<ServicesRegistration>();
base.Container.RegisterTypeForNavigation<NavigationPage>();
// This is deprecated but we have to wait for Prism.Autofac to update itself
builder.Update(base.Container);
}
}
The DI registrations associated with navigation are done in this module:
internal class NavigationRegistration : Module
{
/// <summary>
/// Load the navigation related types into the given builder.
/// </summary>
/// <param name="builder">Container Builder that will be turned into the final IOC Container</param>
protected override void Load(ContainerBuilder builder)
{
// Register the NavigationPage in Xamarin with the Prism Navigation system.
//builder.RegisterType<NavigationPage>().AsSelf();
//PageNavigationRegistry.Register(nameof(NavigationPage), typeof(NavigationPage));
// Get all of the Types that represent a View in our assembly for DI and navigation registration
// If start-up time ever becomes an issue, we can replace this assembly scan with explicit registrations.
Type[] viewTypes = base.ThisAssembly.GetTypes().Where(type => type.IsAssignableTo<Page>()).ToArray();
// Iterate over each discovered View Type and register it with the navigation system and DI container.
for(int index = 0; index < viewTypes.Length; index++)
{
Type viewType = viewTypes[index];
builder.RegisterType(viewType).Named<Page>(viewType.Name);
// If we ever need to disconnect a view name from its Type name, we can do so here.
// We would build a custom attribute to decorate the view with, pull the attribute from the Type
// and register the Type with the attribute value.
PageNavigationRegistry.Register(viewType.Name, viewType);
}
}
}
Again I can each one of my detail pages without problem, the hamburger menu exists and I can open the Master page to view my buttons used for navigating. I just can't navigate backwards once I'm there. Is that something I'm supposed to wire up myself?
I'm not sure I'm reading your question right but it sounds like this is normal behavior. In my (short) experience with XF/Prism, every navigation from the master is a beginning of the stack. If you were to add another page, e.g. from Master->PageA->PageB, I would expect Page A to have the hamburger menu but going to PageB would give you the back arrow.
For using NavigationPage inside uri you should register it for navigation in the App.xaml.cs:
protected override void RegisterTypes()
{
// your registrations
Container.RegisterTypeForNavigation<NavigationPage>();
}
I most cases it is the reason.
To Navigate To Master Page
"/MasterPage/NavigationPage/ViewA"
To Navigate out of Master Page from ViewA and with back button
"ViewB"
You need to start your app with MainPage = new NavigationPage(new StartPage()); That is how it is solved in normal situation. So maybe try to register your MainPage wrapped in a NavigationPage.

Resources