Xamarin forms: How to make tabs on bottom of the page? - xamarin.forms

I am looking for tabs which will be on the bottom part of the page. I tried the xamarin forms TabbedPage, but for ios only the tabs are coming bottom and for android and windows the tabs are showing on the top. Is there any way to achieve this feature?

Edit: for new Xamarin.Forms projects you should use Xamarin.Forms Shell, it provides easy tabs and other great features.
To accomplish that you have 3 options:
1) To use new bottom tabbar native control you must have Xamarin Forms version 3.1 or above.
On your tabbed page you need to add the android specification for bottom placemente:
XAML
<?xml version="1.0" encoding="utf-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.ToolbarPlacement="Bottom"
x:Class="YouProjectNameSpace.MyTabbedPage">
</TabbedPage>
Or c# code behind
using System;
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
using Xamarin.Forms;
namespace YouProjectNameSpace
{
public partial class MyTabbedPage : TabbedPage
{
public MyTabbedPage()
{
InitializeComponent();
On<Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);
}
}
}
If you want more customization you can add:
<?xml version="1.0" encoding="utf-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.ToolbarPlacement="Bottom"
BarBackgroundColor="#2196F3"
BarTextColor="White"
android:TabbedPage.BarItemColor="#66FFFFFF"
android:TabbedPage.BarSelectedItemColor="White"
x:Class="YouProjectNameSpace.MyTabbedPage">
</TabbedPage>
2) Create a custom renderer for Android of the tabbed page and move it to the bottom
using System;
using Xamarin.Forms;
namespace YouProjectNameSpace
{
public class CustomTabbedPage : TabbedPage
{
}
}
And the renderer:
using System;
using Android.Content;
using Android.Support.Design.Widget;
using Android.Support.V4.View;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android.AppCompat;
[assembly: ExportRenderer(typeof(CustomTabbedPage), typeof(CustomTabbedPageRenderer))]
namespace YouProjectNameSpace.Android
{
public class CustomTabbedPageRenderer : TabbedPageRenderer
{
public CustomTabbedPageRenderer(Context context): base(context)
{
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
this.InvertLayoutThroughScale();
base.OnLayout(changed, l, t, r, b);
}
private void InvertLayoutThroughScale()
{
this.ViewGroup.ScaleY = -1;
TabLayout tabLayout = null;
ViewPager viewPager = null;
for (int i = 0; i < ChildCount; ++i)
{
Android.Views.View view = GetChildAt(i);
if (view is TabLayout tabs)
tabLayout = tabs;
else if (view is ViewPager pager)
viewPager = pager;
}
tabLayout.ViewTreeObserver.AddOnGlobalLayoutListener(new GlobalLayoutListener(viewPager, tabLayout));
tabLayout.ScaleY = viewPager.ScaleY = -1;
}
private class GlobalLayoutListener : Java.Lang.Object, Android.Views.ViewTreeObserver.IOnGlobalLayoutListener
{
private readonly ViewPager viewPager;
private readonly TabLayout tabLayout;
public GlobalLayoutListener(ViewPager viewPager, TabLayout tabLayout)
{
this.viewPager = viewPager;
this.tabLayout = tabLayout;
}
public void OnGlobalLayout()
{
this.viewPager.SetPadding(0, -this.tabLayout.MeasuredHeight, 0, 0);
this.tabLayout.ViewTreeObserver.RemoveOnGlobalLayoutListener(this);
}
}
}
}
3) Use specific library like PXTabs, this one creates a full Xamarin Forms bottom tabs without any native code.
If you want to read more about bottom tabs and renderers:
Setting TabbedPage Toolbar Placement and Color
Xamarin.Forms: Official Bottom Navigation/Bottom Tabs on Android
BottomNavigationBarXF

c# code worked it.
using System;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
using Xamarin.Forms;
namespace YouProjectNameSpace
{
public partial class MyTabbedPage : TabbedPage
{
public MyTabbedPage()
{
InitializeComponent();
On<Xamarin.Forms.PlatformConfiguration.Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);
}
}
}

Related

Display the background image as tiles at Xamarin.Forms

I am using Xamarin.Forms to develop an application for Android.
I am using the Xamarin.
I want to display the background as tiles.
I have prepared an image of 100x100 pixels.
\Android\MyApp\MyApp.Android\Resources\drawable\background.jpg
<?xml version="1.0" encoding="utf-8" ? >
<ContentPage 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"
xmlns:viewmodels="clr-namespace:MyApp.ViewModels"
x:DataType="viewmodels:LoginViewModel"
mc:Ignorable="d"
x:Class="MyApp.Views.LoginPage"
Shell.NavBarIsVisible="False"
BackgroundImage="background.jpg"
>
This way, the background.jpg will be enlarged and displayed.
I want to display it repeatedly in tiles.
You can display it repeatedly in tiles through below steps.
1.Create a drawable xml file on your Android project:
<?xml version="1.0" encoding="utf-8" ?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:tileMode="repeat"
android:src="#drawable/clock" >
</bitmap>
2.After that create below two effect classes on your shared project:
//base class
namespace AppTiles
{
public class BaseEffect : RoutingEffect
{
public const string EffectNamespace = "AppTiles";
public BaseEffect(String effectId) : base($"{EffectNamespace}.
{effectId}")
{
}
}
}
//effect class
namespace AppTiles
{
public class CoverEffect : BaseEffect
{
public CoverEffect() : base(nameof(CoverEffect))
{
}
}
}
3.Create a effect class in your Android project
[assembly: ResolutionGroupName("AppTiles") ]
[assembly: ExportEffect(typeof(AppTiles.Droid.CoverEffect),
nameof(CoverEffect))]
namespace AppTiles.Droid
{
public class CoverEffect : PlatformEffect
{
protected override void OnAttached()
{
UpdateBackground();
}
protected override void OnDetached()
{
}
private void UpdateBackground()
{
Android.Views.View mView = Container as Android.Views.View;
if (mView != null)
{
mView.Background = ContextCompat.GetDrawable(mView.Context, Resource.Drawable.XMLFile1);
}
}
}
}
4.Add below xmal in shared main page code behind:
<StackLayout Orientation="Vertical" Spacing="0">
<StackLayout.Effects>
<effects:CoverEffect/>
</StackLayout.Effects>
</StackLayout>
Below is the screenshot:

My scroll view does not responding i can not scroll down

In my XAML file, I used scroll view for scroll down but it does not work I can not find any issue where I did a couple of extra things here one is I add pull to refresh nuget and use it and I create a theme content class and I inherited it with default content page. another command in content page is working but scroll view does not support
Here my Xaml file
<views:BaseContentPage.Content>
<ScrollView BackgroundColor="White">
<controls:PullToRefreshLayout x:Name="PullToRefreshLayout" IsPullToRefreshEnabled="True" RefreshCommand="RefreshPatientDetailsPage">
<StackLayout>
// some code
</StackLayout>
</controls:PullToRefreshLayout>
</ScrollView>
</views:BaseContentPage.Content>
this is the Base content page I create
public abstract class BaseContentPage : ContentPage
{
public readonly BaseViewModel BaseViewModel;
protected bool IsNavigated;
public BaseContentPage(BaseViewModel baseViewModel)
{
BaseViewModel = baseViewModel;
}
protected abstract override void OnAppearing();
protected override void OnDisappearing()
{
IsNavigated = true;
}
}
I think this scroll view does not work because of the NuGet I put or it's the base content page
for android, I used this customer render
public class SortPaneListViewRendererAndroid : ListViewRenderer
{
public SortPaneListViewRendererAndroid(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.VerticalScrollBarEnabled = false;
var listView = Control as Android.Widget.ListView;
listView.DividerHeight = 1;
}
}
}
The reason this was happening is because you are nesting scrollable controls, Removing one of them will solve the issue

Problem seen creating View-ViewModel lookup table - you have more than one View registered for the ViewModels

I started an Xamarin.Froms project with MvvmCross. I followed the documentation on the offical MvvmCross website to start the Android project with Xamarin.Forms. Here is my code in my Core project:
public class App : MvxApplication
{
public App()
{
}
public override void Initialize()
{
base.Initialize();
Mvx.IoCProvider.RegisterSingleton(new NavigationStack());
Mvx.IoCProvider.RegisterSingleton<IMvxAppStart>(new MvxAppStart<MainViewModel>(this, Mvx.IoCProvider.Resolve<IMvxNavigationService>()));
}
}
public class MainViewModel : BaseViewModel
{
public MainViewModel(NavigationStack navigationStack) : base(navigationStack)
{
}
}
Code that's in my Forms project:
MainView.xaml:
<views:MvxContentPage x:TypeArguments="viewModels:MainViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
xmlns:mvx="clr-namespace:MvvmCross.Forms.Bindings;assembly=MvvmCross.Forms"
xmlns:viewModels="clr-namespace:MyApp.Core.ViewModels;assembly=MyApp.Core"
x:Class="MyApp.Forms.Views.MainView">
<ContentPage.Content>
<StackLayout Margin="10">
<Label Text="Subtotal" />
</StackLayout>
</ContentPage.Content>
</views:MvxContentPage>
MainView.xaml.cs:
public partial class MainView : MvxContentPage<MainViewModel>
{
public MainView()
{
InitializeComponent();
}
}
In my Android project:
[Activity(
Label = "MyApp.Droid",
Theme = "#style/MyTheme",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
LaunchMode = LaunchMode.SingleTask)]
public class MainActivity : MvxFormsAppCompatActivity<MvxFormsAndroidSetup<Core.App, Forms.App>, Core.App, Forms.App>
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
}
}
It compiles, but when I launch the app I get the exception:
MvvmCross.Exceptions.MvxException: Problem seen creating
View-ViewModel lookup table - you have more than one View registered
for the ViewModels: 2*MainViewModel (MainActivity,MainView)
If your ViewModel is called MainViewModel and your Forms page is too, you might get a name conflict because MvvmCross will have 2 view to viewmodel lookups. You can prevent this by naming your Activity differently like "FormsActivity.cs".
You could also rename your MainViewModel to MvxMainViewModel(whatever you like), then this exception will disappear.

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation?

I getting this erro when I run my application after adding the Xamarin.Forms.Maps NuGet package. I am trying to display a Map in my MapPage. I have hadded name space definitions to my XAML file.
I have and I have added Xamarin.FormsMaps.Init(this, bundle) to the Android project in the MainActivity and to the iOS project using Xamarin.FormsMaps.Init(); in the AppDelegate file.
I searched google and stackoverflow and couldn't find any useful document.
here is the MainPage.xaml code
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TravelRecord"
x:Class="TravelRecord.MainPage">
<StackLayout VerticalOptions="Center" Margin="20,0">
<Entry Placeholder="Enter Your Email... " Keyboard="Email" x:Name="emailEntry"
TextColor="{StaticResource blueColor}" Text="Test"/>
<Entry Placeholder="Enter Your Password... " TextColor="{StaticResource blueColor}"
IsPassword="True" x:Name="passwordEntry" Text="Test"/>
<Button Text="Log in" x:Name="LoginButton" Clicked="LoginButton_Clicked" Margin="0,50,0,0"
BackgroundColor="{StaticResource blueColor}" TextColor="White"/>
</StackLayout>
</ContentPage>
and the MainPage.cs file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TravelRecord
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void LoginButton_Clicked(object sender, EventArgs e)
{
bool emailIsNullOrEmpty = string.IsNullOrEmpty(emailEntry.Text);
bool passwordIsNullOrEmpty = string.IsNullOrEmpty(passwordEntry.Text);
if (emailIsNullOrEmpty || passwordIsNullOrEmpty)
{
}
else
{
Navigation.PushAsync(new HomPage());
}
}
}
}
from which I navigate to the MapsPage using the Login button.
here is the MapsPage.xaml
<?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="TravelRecord.MapPage"
xmlns:maps="clr-namespace:Xamarin.Forms.GoogleMaps;assembly=Xamarin.Forms.GoogleMaps">
<ContentPage.Content>
<maps:Map />
</ContentPage.Content>
</ContentPage>
and the MapsPage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TravelRecord
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void LoginButton_Clicked(object sender, EventArgs e)
{
bool emailIsNullOrEmpty = string.IsNullOrEmpty(emailEntry.Text);
bool passwordIsNullOrEmpty = string.IsNullOrEmpty(passwordEntry.Text);
if (emailIsNullOrEmpty || passwordIsNullOrEmpty)
{
}
else
{
Navigation.PushAsync(new HomPage());
}
}
}
}
here MainActivity.cs of the android project:
using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.IO;
namespace TravelRecord.Droid
{
[Activity(Label = "TravelRecord", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
Xamarin.FormsMaps.Init(this, bundle);
string dataBaseName = "travelRecord.db";
var dataBaseBath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string fullPath = Path.Combine(dataBaseBath, dataBaseName);
LoadApplication(new App(fullPath));
}
}
}
and the AppDelegate of the iOS project
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Foundation;
using UIKit;
namespace TravelRecord.iOS
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Xamarin.FormsMaps.Init();
string dataBaseName = "travelRecord.db";
var dataBaseBath =Path.Combine( System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal),"..","Library");
string fullPath = Path.Combine(dataBaseBath, dataBaseName);
LoadApplication(new App(fullPath));
return base.FinishedLaunching(app, options);
}
}
}
I have also added this line to the AndroidManifest.xml file
<meta-data android:name="com.google.android.geo.API_KEY" android:value ="AIzaSyBXuv3VQ4-5pxLcbje-397wvmb3y6RoxsE"></meta-data>
Does anyone know how to solve this ? thanks

How to Decrease Height of Navigation bar in xamarin forms

I have used MasterDetail page as main page of my app. Here TabbedPage is my detail page which contain various ContentPage as children of TabbedPage. I have set Details as Detail = NavigationPage(tabbedpage); it work great for IOS but in android it occupie more space in NavigationBar title and Tabbed Name, I cannot omit both, my question is how can i reduce the height of title bar of navigation page.
Please check attached image for reference.
You need to set padding platform specific for example.
<OnPlatform x:TypeArguments="FontAttributes" x:Key="fontAttributes">
<OnPlatform.iOS>Bold</OnPlatform.iOS>
<OnPlatform.Android>Italic</OnPlatform.Android>
</OnPlatform>
You need to implement a CustomNavigationPage with a Custom renderer. Refer this - https://xamgirl.com/transparent-navigation-bar-in-xamarin-forms/
<?xml version="1.0" encoding="utf-8" ?>
<NavigationPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TransparentNavBarXForms.CustomNavigationPage"
BarTextColor="White">
<NavigationPage.BarBackgroundColor>
<OnPlatform x:TypeArguments="Color">
<On Platform="Android, iOS" Value="Transparent" />
</OnPlatform>
</NavigationPage.BarBackgroundColor>
</NavigationPage>
public partial class CustomNavigationPage : NavigationPage
{
public CustomNavigationPage() : base()
{
InitializeComponent();
}
public CustomNavigationPage(Page root) : base(root)
{
InitializeComponent();
}
public bool IgnoreLayoutChange { get; set; } = false;
protected override void OnSizeAllocated(double width, double height)
{
if (!IgnoreLayoutChange)
base.OnSizeAllocated(width, height);
}
}
public class CustomNavigationPageRenderer : NavigationPageRenderer
{
public CustomNavigationPageRenderer(Context context) : base(context)
{
}
IPageController PageController => Element as IPageController;
CustomNavigationPage CustomNavigationPage => Element as CustomNavigationPage;
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
CustomNavigationPage.IgnoreLayoutChange = true;
base.OnLayout(changed, l, t, r, b);
CustomNavigationPage.IgnoreLayoutChange = false;
int containerHeight = b - t;
PageController.ContainerArea = new Rectangle(0, 0, Context.FromPixels(r - l), Context.FromPixels(containerHeight));
for (var i = 0; i < ChildCount; i++)
{
AView child = GetChildAt(i);
if (child is Android.Support.V7.Widget.Toolbar)
{
continue;
}
child.Layout(0, 0, r, b);
}
}
}
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new CustomNavigationPage(new MainPage());
}
}

Resources