How to disable Tab on TabbedPage xamarin forms? - xamarin.forms

Im looking for a way to disable a tab within a TabbedPage,
here my example of two tabs
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns=".."
xmlns:x=".."
x:Class="App.Views.Pages_Tabbed"
x:Name="myTabs"
xmlns:page="clr-App.Views">
<TabbedPage.Children>
<page:MemberShip x:Name="firstPage" ></page:MemberShip>
<page:Consent x:Name="secondPage"></page:Consent>
<page:Question x:Name="thirdPage"> </page:Question >
</TabbedPage.Children>
</TabbedPage> ```
I tried to set the Page to IsEnabled = false or IsVisible= false but not working .. it hide only content.
Also i followed https://stackoverflow.com/questions/54657758/how-to-set-the-currentpage-property-of-a-tabbedpage-in-the-oncurrentpagechanged but it crash the app cuz of the infinite loop.

The official does not seem to provide a way to directly disable the tab.
You can also hide the tab temporarily when you don't want the user to click on it. Let the user load it when he can click.
Here is the background code:
public partial class TabbedPage1 : TabbedPage
{
public static TabbedPage theTabbedPage {get; set;}
public TabbedPage1()
{
InitializeComponent();
theTabbedPage = this;
theTabbedPage.Children.RemoveAt(1);//Hide the second tab
}
}
When the user can click, use abbedPage1.theTabbedPage.Children.Insert(1,new Page2() {Title="page2"}); to add the tab.
Hope this method can help you too.

Related

Xamarin.Forms Open ContentView with parameters in xaml

In my app there is a certain listview that I use over and over in my app, only with different elements in it. Therefore, I put everything inside a contentview and inflated it in my xaml like so:
<ContentPage Title="Newbies" BackgroundColor="#fafafa">
<views:CV_AllAdsRes />
</ContentPage>
The class looks like this:
public partial class CV_AllAdsRes : ContentView
{
public CV_AllAdsRes(int id)
{
InitializeComponent();
SetAds();
}
}
Now, this doenst work, because I am not using a "default constructor". If I remove the "int id" from the constructor, it works no problem. But I need to be able to inflate this content view with different parameters inside the xaml.
Am I understanding this concept wrong?
How can I inflate my content view and give it parameters via xaml?
Thank you
I solved it by using a second constructor next to the default one and giving it arguments from xaml like so:
<views:CV_AllAdsRes >
<x:Arguments >
<x:Int32>5</x:Int32>
</x:Arguments>
</views:CV_AllAdsRes>
this will give ID=5.

Xamarin Forms / ReactiveUI - Using ReactiveUI for masterdetail page shows that viewmodel is null

I have a xamarin forms application that is based on ReactiveUI. The viewmodels inherit from ReactiveObject and the codebehind the xaml of the pages , they inherit/are based on from ReactiveContentPage, in case of the masterdetailpage it inherits from the ReactiveMasterDetailPage. The contentpages/masterdetailpage it self are based on ReactiveContent.
What I want to achieve is the following: Get views/xamlpages by giving a viewmodel type. I have the following code for that. But it gives a null at _viewLocator.Resolve...
public async Task<TViewModel> PushViewModelAsync<TViewModel>(bool animated) where TViewModel : class
{
var viewModel = DependencyInjectionService.Get<TViewModel>();
var view = _viewLocator.ResolveView(viewModel);
if (view is Page page)
{
view.ViewModel = viewModel;
await Application.Current.MainPage.Navigation.PushAsync(page, animated);
return viewModel;
}
else
{
throw new ArgumentException($"resolved view for {typeof(TViewModel)} is not a page.");
}
}
The problem is that this works for normal contentpages but it doesnt work for my MasterDetailPage, how is that possible?
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveMasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:vm="clr-namespace:DriverApp.ViewModels"
x:TypeArguments="vm:MainViewModel"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:local="clr-namespace:DriverApp.Views; assembly=MasterDetailPageNavigation"
x:Class="DriverApp.Views.MainPage"
Title="Personal Organiser">
<MasterDetailPage.Master>
<local:MasterPage x:Name="masterPage" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<local:PlanningPage/>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
The viewmodel:
public class MainViewModel : ReactiveObject, IActivatableViewModel
{
}
I inject it like this:
services.AddTransient<IViewFor<MainViewModel>, MainPage>();
var viewModel = DependencyInjectionService.Get<TViewModel>();
var view = _viewLocator.ResolveView(viewModel);
Based on the code provided I would expect the value you are passing into the _viewLocator.ResolveView to be null. You showed the code where you are registering the IViewFor but I don't see any code registering the ViewModel itself.
Registering IViewFor<Foo>, Foo doesn't register the view model. It tells the type system that a given ViewModel will resolve a specific page.
Also, it seems like you are using a different container than the one provided by ReactiveUI. Which is okay, but you have to make sure all your dependencies are registered correctly in the container you plan to resolve dependencies from.
Lastly. You say it gives a null, but you don't say if the ViewModel you are passing is null or the object you are using to resolve is null.
Either way, I think this is an issue of having the dependencies registered in the correct place, based on the information provided.

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.

ReactiveTabbedPage Data Binding

I have been using ReactiveUI for a while with Xamarin Forms, but I've hit a brick wall when trying to use a ReactiveTabbedPage. I can't figure out how the ViewModel will get bound to the ReactiveContentPage's that are the children of the ReactiveTabbedPage.
So, as an example, I might have the following XAML:
<ReactiveTabbedPage x:Name="TabbedPage">
<local:Page1View x:Name="Page1" />
<local:Page2View x:Name="Page2" />
</ReactiveTabbedPage>
Where Page1View and Page2View are both of type ReactiveContentPage and T is the associated ViewModel.
What I expected to happen was that when the ReactiveTabbedPage was navigated to, Page1View would be displayed, and the ViewModel would be loaded (in the same way it would if I navigated to the Page1View directly). However, the ViewModel never gets called (the constructor is never fired and no data binding occurs).
However, both Page1View and Page2View do render and I can see the initial data that is created in those views (e.g. default text for labels etc.).
I know that the ViewModel stuff is working correctly, because if I navigate to Page1View directly (e.g. not in the ReactiveTabbedPage) everything displays as I expect.
Have I missed something, or am I going about this the wrong way? Or is this just not supported in the current version of RxUI?
Any advice is greatly appreciated!
The responsibility for tying the VM to the child pages lies with the host page (i.e. the ReactiveTabbedPage). It alone knows which VM corresponds to which view.
Let's take this one step at a time. First of all, the MainViewModel:
public class MainViewModel : ReactiveObject
{
public ChildViewModel1 Child1 => new ChildViewModel1();
public ChildViewModel2 Child2 => new ChildViewModel2();
}
This code obviously isn't realistic because you wouldn't want to recreate the child VMs upon every property access. It's more the API that's pertinent here.
ChildViewModel1 looks like this:
public class ChildViewModel1 : ReactiveObject
{
public string Test => "Hello";
}
And ChildViewModel2 looks much the same.
Now we can go about setting the views up. Our MainView.xaml looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveTabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:TypeArguments="vms:MainViewModel"
xmlns:local="clr-namespace:ReactiveTabbedPageTest"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vms="clr-namespace:ReactiveTabbedPageTest.VMs"
x:Class="ReactiveTabbedPageTest.MainView">
<local:Child1View x:Name="child1View" Title="Child 1"/>
<local:Child2View x:Name="child2View" Title="Child 2"/>
</rxui:ReactiveTabbedPage>
Notice it declares each of the child views. We need to hook up the VMs to those views, which we do in the code-behind for MainView:
public partial class MainView : ReactiveTabbedPage<VMs.MainViewModel>
{
public MainView()
{
InitializeComponent();
this.ViewModel = new VMs.MainViewModel();
this.WhenActivated(
disposables =>
{
this
.OneWayBind(this.ViewModel, x => x.Child1, x => x.child1View.ViewModel)
.DisposeWith(disposables);
this
.OneWayBind(this.ViewModel, x => x.Child2, x => x.child2View.ViewModel)
.DisposeWith(disposables);
});
}
}
I've done this the safest way by using WhenActivated and OneWayBind calls. In reality, it's unlikely your child VMs will change, so directly assigning them rather than binding is totally fine.
Now our child views can be thrown together. Here's ChildView1.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ReactiveTabbedPageTest.Child1View"
x:TypeArguments="vms:ChildViewModel1"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vms="clr-namespace:ReactiveTabbedPageTest.VMs">
<Label x:Name="label" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
</rxui:ReactiveContentPage>
And the code-behind:
public partial class Child1View : ReactiveContentPage<ChildViewModel1>
{
public Child1View()
{
InitializeComponent();
this.WhenActivated(
disposables =>
{
this
.OneWayBind(this.ViewModel, x => x.Test, x => x.label.Text)
.DisposeWith(disposables);
});
}
}
Once again we're doing the usual RxUI binding goodness to associate properties in the VM with controls in the UI. And once again you could optimize this for properties that don't mutate.
For the purposes of this example, ChildView2 is much the same as ChildView1, but obviously it could be totally different.
The end result is as you'd expect:
What's not evident from the screenshot but is very important is that each tab is deactivating when you switch away from it (as would its associated view model if it implemented ISupportsActivation). This means you can clean up any bindings and subscriptions for that tab when it's not in use, reducing memory pressure and improving performance.

Resources