Skip to content

Instantly share code, notes, and snippets.

@kevinmutlow
Created October 22, 2017 03:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinmutlow/92d033938dcede62b49980fbb0c6676f to your computer and use it in GitHub Desktop.
Save kevinmutlow/92d033938dcede62b49980fbb0c6676f to your computer and use it in GitHub Desktop.
Custom/Cross-platform Tab-bar using Xamarin.Forms (for iOS and Android)
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App.Core.Controls.TabBar">
<Grid
x:Name="grdContainer"
RowSpacing="0"
ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="2" />
</Grid.RowDefinitions>
<!-- ColumnDefinitions defined in code as each child item is added -->
</Grid>
</ContentView>
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace App.Core.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TabBar : ContentView
{
private static BoxView underline;
public TabBar ()
{
InitializeComponent();
underline = new BoxView()
{
ClassId = "underlinebar",
BackgroundColor = Color.Red,
VerticalOptions = LayoutOptions.Fill,
HorizontalOptions = LayoutOptions.Fill
};
grdContainer.Children.Add(underline, SelectedIndex, 1);
grdContainer.ChildAdded += GrdContainer_ChildAdded;
}
private void GrdContainer_ChildAdded(object sender, ElementEventArgs e)
{
if (grdContainer.ColumnDefinitions.Count < grdContainer.Children.Count && e.Element.ClassId != "underlinebar")
{
grdContainer.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Star });
var columnIndex = grdContainer.ColumnDefinitions.Count - 1;
Grid.SetRow(e.Element, 0);
Grid.SetColumn(e.Element, columnIndex);
SelectedIndexChanged(this, 0, SelectedIndex);
}
}
public IList<View> Items { get { return grdContainer.Children; } }
#region SelectedIndex
public static BindableProperty SelectedIndexProperty =
BindableProperty.Create(
nameof(SelectedIndex),
typeof(int),
typeof(TabBar),
defaultValue: default(int),
defaultBindingMode: BindingMode.OneWay,
propertyChanged: SelectedIndexChanged
);
private static void SelectedIndexChanged(BindableObject bindable, object oldValue, object newValue)
{
int newColIndex = 0;
var ctrl = (bindable as TabBar);
var currentRow = Grid.GetRow(underline);
var currentCol = Grid.GetColumn(underline);
if (newValue is string)
int.TryParse(newValue as string, out newColIndex);
else if (newValue is int)
newColIndex = (int)newValue;
if (newColIndex < ctrl.grdContainer.ColumnDefinitions.Count)
{
Grid.SetRow(underline, 1);
Grid.SetColumn(underline, newColIndex);
var elementsInFirstRow = ctrl.grdContainer.Children.Where(w => Grid.GetRow(w) == 0);
foreach (var element in elementsInFirstRow)
{
if (Grid.GetColumn(element) == newColIndex)
element.ClassId = "selected";
else
element.ClassId = "";
}
}
}
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
#endregion
}
}
<?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:controls="clr-namespace:App.Core.Controls"
x:Class="App.Core.Pages.Other.TabBarPage">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="HeaderLabelStyle" TargetType="Label">
<Setter Property="FontSize" Value="Small" />
<Setter Property="HorizontalOptions" Value="Fill" />
<Setter Property="VerticalOptions" Value="Fill" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
<Setter Property="VerticalTextAlignment" Value="Center" />
<Setter Property="HeightRequest" Value="45" />
<Style.Triggers>
<Trigger TargetType="Label" Property="ClassId" Value="selected">
<Setter Property="FontAttributes" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<controls:TabBar SelectedIndex="{ Binding SelectedTab }">
<controls:TabBar.Items>
<Label Text="Header 1"
Style="{ StaticResource HeaderLabelStyle }">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{ Binding TabItemTappedCommand }" CommandParameter="0" />
</Label.GestureRecognizers>
</Label>
<Label Text="Header 2"
Style="{ StaticResource HeaderLabelStyle }">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{ Binding TabItemTappedCommand }" CommandParameter="1" />
</Label.GestureRecognizers>
</Label>
<Label Text="Header 3"
Style="{ StaticResource HeaderLabelStyle }">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{ Binding TabItemTappedCommand }" CommandParameter="2" />
</Label.GestureRecognizers>
</Label>
<Label Text="Header 4"
Style="{ StaticResource HeaderLabelStyle }">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{ Binding TabItemTappedCommand }" CommandParameter="3" />
</Label.GestureRecognizers>
</Label>
</controls:TabBar.Items>
</controls:TabBar>
<!-- PANEL 1 -->
<ContentView
Grid.Row="1"
IsVisible="False">
<StackLayout.Triggers>
<DataTrigger TargetType="ContentView" Binding="{Binding SelectedTab }" Value="0">
<Setter Property="IsVisible" Value="True" />
</DataTrigger>
</StackLayout.Triggers>
<Label Text="Panel 1"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentView>
<!-- PANEL 2 -->
<ContentView
Grid.Row="1"
IsVisible="False">
<StackLayout.Triggers>
<DataTrigger TargetType="ContentView" Binding="{Binding SelectedTab }" Value="1">
<Setter Property="IsVisible" Value="True" />
</DataTrigger>
</StackLayout.Triggers>
<Label Text="Panel 2"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentView>
<!-- PANEL 3 -->
<ContentView
Grid.Row="1"
IsVisible="False">
<StackLayout.Triggers>
<DataTrigger TargetType="ContentView" Binding="{Binding SelectedTab }" Value="2">
<Setter Property="IsVisible" Value="True" />
</DataTrigger>
</StackLayout.Triggers>
<Label Text="Panel 3"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentView>
<!-- PANEL 4 -->
<ContentView
Grid.Row="1"
IsVisible="False">
<StackLayout.Triggers>
<DataTrigger TargetType="ContentView" Binding="{Binding SelectedTab }" Value="3">
<Setter Property="IsVisible" Value="True" />
</DataTrigger>
</StackLayout.Triggers>
<Label Text="Panel 4"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentView>
</Grid>
</ContentPage>
using System.Windows.Input;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace App.Core.Pages.Other
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TabBarPage : ContentPage
{
public TabBarPage()
{
InitializeComponent();
BindingContext = this;
SelectedTab = 0;
}
private int _selectedTab;
public int SelectedTab
{
get { return _selectedTab; }
set
{
if (_selectedTab != value)
{
_selectedTab = value;
OnPropertyChanged();
}
}
}
public ICommand TabItemTappedCommand => new Command<string>(TabItemTappedAction);
void TabItemTappedAction(string tabIndex)
{
int newSelection = 0;
int.TryParse(tabIndex, out newSelection);
SelectedTab = newSelection;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment