Tab control de pestañas en Xamarin dentro de una página

Xamarin Forms cuenta con un control llamado TabbedPage que nos ayuda a optimizar los espacios en el diseño de aplicaciones móviles, este control consiste en utilizar pestañas que permiten al usuario pasar de un conjunto de controles a otro mientras se mantiene el contexto de la página actual.


Cuando deseas implementar múltiples pestañas en tu aplicación, el control  TabbedPage es el indicado a utilizar en tu aplicación. Es fácil de implementar una página independiente que proporciona navegación a otras subpáginas sin salir de la página principal. Como Xamarin lo indica en su documentación, el aspecto final del control es nativo de cada plataforma: para iOS, nos muestra iconos y texto en una barra en la parte inferior. Para Android, pestañas similares a las de una computadora de escritorio en la parte superior.

Sin embargo, los problemas pueden surgir a medida que intenta integrar TabbedPage en su proyecto y vas requiriendo más personalización. Si estás utilizando una página de navegación y estás abriendo páginas de contenido dentro de ella, hay problemas con los que tienes que lidiar para que una subpágina de navegación sea un contenedor para las subpáginas (Espero que se entienda esto porque ya parezco a Cantinflas), más adelante quedara más claro a lo que me refiero.

Una imagen dice más que mil palabras, si quieres tener un control como el que se muestra en la imagen, el código de este artículo te servirá. No olvides compartir el post.

Tab Control  Xamarin Forms      Xamarin Forms control Tab

¿Qué opciones tenemos?

Después de buscar y revisar los componentes disponibles en el sitio de Xamarin, no encontré ninguna buena opción para una solución de pestañas que funcione tanto en Android como en iOS. Entonces la opción es implementar el siguiente código, que lo que haces es crear un Tab y lo puedes colocar en la parte que desees de tu aplicación.

Beneficios

Este control lo llame CustomTabControls y sus principales beneficios son:

  • Es independiente, por lo que fácilmente puede ser referenciado y utilizado desde cualquier proyecto.
  • Se implementa como un conjunto de controles estándar en una página.
  • La interfaz de usuario para cada pestaña se obtiene en un solo lugar, por lo que no tiene que saltar entre diferentes archivos para ver cómo se verá la página.
  • Proporciona un conjunto de controles que hacen todo el trabajo por ti. Solo colócalos, defina su apariencia y rellene las pestañas.
  • Los controles heredan de los controles estándar y proporcionan todas las propiedades y métodos de su tipo subyacente. Esto brinda lo último en flexibilidad en diseño y estilo.
  • Los controles se pueden instanciar en código o usar a través de la sintaxis XAML estándar.
  • No hay renderizadores específicos de la plataforma.
  • Puedes utilizar MVVM

Bueno enseguida entramos en acción viendo los controles desarrollados. Debes crear una clase llamada CustomTabButtons y otra que se llame CustomTabButton

CustomTabButton: evento clic para cambiar entre pestañas.

El control CustomTabButtons, solo es un contenedor para los botones que llevaran a cada pestaña, y CustomTab es simplemente un contenedor para todos los elementos de diseño que el usuario quiere que se muestren en una pestaña determinada (mapas, listview, entry, etc).

El primer control que se le agrega funcionalidad es CustomTabButton. Su código se muestra enseguida.

using System;
using Xamarin.Forms;
namespace Controls

{

    public class CustomTabButtons: StackLayout { }

    public class CustomTabButton : Button

    {


        public CustomTabButton()

        {

            Clicked += ThisTabButtonClicked;

        }


        public void ThisTabButtonClicked(object s, EventArgs e)

        {

            CustomTabs prnt = validParentCustomTabs();

            if (prnt == null) return;


            prnt.SelectedTabButton = this;

        }


        private CustomTabs validParentCustomTabs()

        {           

            if (Parent != null && Parent.Parent != null &&

               Parent.Parent.GetType() == typeof(CustomTabs))

                return ((CustomTabs)Parent.Parent);
            else
            {
                throw new Exception(

                   "Error, el padre de un CustomTabButton " +

                   "deb ser un CustomTabs");
            }

        }

    }

}

 

Cuando el usuario hace clic en un botón de tabulación, el padre del elemento principal se marca; este debe ser un CustomTabs, de lo contrario se lanza una excepción. Si es correcto, la propiedad SelectedButton se establece en este botón. Esto tiene el efecto de cambiar el aspecto de los botones y configurar la pestaña asociada para que sea visible, como verás en CustomTabs.

 

CustomTabs: Donde se muestra todo el contenido

using System;

using System.Collections.Generic;

using System.Linq;

using Xamarin.Forms;


namespace Controls

{

    public class CustomTab : StackLayout { }


    public class CustomTabs : StackLayout

    {

        private Color _selectedColor = Color.White;


        public Color SelectedColor

        {

            get { return _selectedColor; }

            set { _selectedColor = value; }

        }


        private Color _unselectedColor = Color.WhiteSmoke;


        public Color UnselectedColor

        {

            get { return _unselectedColor; }

            set

            {

                _unselectedColor = value;

            }

        }

        internal List<CustomTabButton> TabButtons

        {

            get

            {

                CustomTabButtons tabButtons =

                   (CustomTabButtons)Children.

                   First(c => c.GetType() ==

                      typeof(CustomTabButtons));

                var buttonEnumerable =

                   tabButtons.Children.Select(c =>

                      (CustomTabButton)c);

                var buttonList =

                   buttonEnumerable.Where(c => c.GetType() ==

                      typeof(CustomTabButton)).ToList();

                return buttonList;

            }

        }


        internal List<CustomTab> Tabs

        {

            get

            {

                var childList =

                   Children.Where(c => c.GetType() ==

                      typeof(CustomTab));

                var tabList =

                   childList.Select(c => (CustomTab)c).ToList();

                return tabList;

            }

        }

        private int _selectedTabIndex;

        public int SelectedTabIndex

        {

            get { return _selectedTabIndex; }

            set

            {

                _selectedTabIndex = value;


                if (Tabs.Count > 0)

                    SelectionUIUpdate();

            }

        }


        public CustomTabButton SelectedTabButton

        {

            get { return TabButtons[_selectedTabIndex]; }

            set

            {

                var tabIndex = TabButtons.FindIndex(t =>

                   t == value);

                if (tabIndex == -1)

                    throw new Exception(

                       "SelectedTabButton asignó un botón que no se encuentra entre los elementos secundarios de CustomTabButtons.");


                if (tabIndex != _selectedTabIndex)

                    SelectedTabIndex = tabIndex;

            }

        }


        public CustomTab SelectedTab

        {

            get { return Tabs[_selectedTabIndex]; }

        }

            protected override void OnParentSet()

        {

            base.OnParentSet();


            if (TabButtons.Count != Tabs.Count)

            {

                throw new Exception(

                   "El número de botones y el número de pestañas no coinciden.");

            }


            SelectionUIUpdate();

        }

            private void SelectionUIUpdate()

        {

            foreach (var btn in TabButtons){

                btn.BackgroundColor = UnselectedColor;

                btn.BorderWidth = 0;

            }

               

            SelectedTabButton.BackgroundColor =

               SelectedColor;

            SelectedTabButton.BorderWidth = 1;

            SelectedTabButton.BorderColor = Color.WhiteSmoke;


            foreach (var tb in Tabs)

                tb.IsVisible = false;

            SelectedTab.IsVisible = true;

        }

    }

}

 

Con este código se implementa el contenido que va dentro de cada pestaña y se oculta o se muestra de acuerdo con lo que se selección en los botones CustomTabButton.

 

Implementar CustomTabControls

El primer paso es agregar una referencia en la aplicación al proyecto CustomTabControls o la puedes compilar y hacer referencia a la DLL, si lo prefieres.

En seguida debes agregar los controles a la página. En este ejemplo se muestra con XAML. Instanciar en código funciona de manera muy similar.

 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"

      xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

      xmlns:TabCtrls="clr-namespace:CustomTabControls;

          assembly=CustomTabControls"

      x:Class="App2.Page2"

      BackgroundColor="White"

      Title="  Time Sheet">

     <StackLayout Orientation="Vertical"

                 VerticalOptions="FillAndExpand"

                  HorizontalOptions="FillAndExpand">

       <TabCtrls: CustomTabs x:Name="ProjectsDaysTabs"

                 Orientation="Vertical"

              SelectedTabIndex="1"

             SelectedColor="Lime" UnselectedColor="Green"

              VerticalOptions="FillAndExpand"

              HorizontalOptions="FillAndExpand">  

           <TabCtrls: CustomTabButtons Orientation="Horizontal">

              <TabCtrls: CustomTabButton x:Name="ProjectsTabButton"

                 Text="Projects"

                 BorderRadius = "1" BorderWidth = "2" HeightRequest="35"

                 WidthRequest = "75" FontSize="13" TextColor="Black"/>

              <TabCtrls: CustomTabButton x:Name="WeeksTabButton" Text="Weeks"

                 BorderRadius = "1" BorderWidth = "2" HeightRequest="35"

                 WidthRequest = "75" FontSize="13" TextColor="Black"/>

              <TabCtrls: CustomTabButton x:Name="DaysTabButton" Text="Days"

                 BorderRadius = "1" BorderWidth = "2" HeightRequest="35"

                 WidthRequest = "75" FontSize="13" TextColor="Black"/>

           </TabCtrls: CustomTabButtons>  

           <TabCtrls: CustomTab x:Name="ProjectsTab" IsVisible="True"

                 VerticalOptions="FillAndExpand"

                 HorizontalOptions="FillAndExpand"

                 BackgroundColor="Pink">   

              <Label Text="Projects Tab" HorizontalOptions="Center"

                    TextColor="Black" />   

           </TabCtrls: CustomTab>  

           <TabCtrls: CustomTab x:Name="WeeksTab" IsVisible="False"

                 VerticalOptions="FillAndExpand"

                 HorizontalOptions="FillAndExpand"

                 BackgroundColor="Yellow">   

              <Label Text="Weeks Tab" HorizontalOptions="Center"

                    TextColor="Black"/>   

           </TabCtrls: CustomTab>  

           <TabCtrls: CustomTab x:Name="DaysTab" IsVisible="False"

                VerticalOptions="FillAndExpand"

                 HorizontalOptions="FillAndExpand"

                BackgroundColor="Aqua">  

             <Label Text="Days Tab" HorizontalOptions="Center"

                    TextColor="Black"/>   

          </TabCtrls:CustomTab>  

        </TabCtrls:CustomTabs>

     </StackLayout>

  </ContentPage>

 

¿Cómo se organizan los tabs?

 

<TabCtrls: CustomTabs>

     <TabCtrls: CustomTabButtons>

        <TabCtrls: CustomTabButton>

        <TabCtrls: CustomTabButton>

        <TabCtrls: CustomTabButton>

      </ TabCtrls: CustomTabButtons>

     <TabCtrls: CustomTab>

     <TabCtrls: CustomTab>

     <TabCtrls: CustomTab>

 

Todos los controles relacionados con pestañas están en el padre <TabCtrls: CustomTabs>. Dentro de eso hay una colección de botones llamados <TabCtrls: CustomTabButtons>. Los botones son del tipo <TabCtrls: CustomTabButton>. (¡La distinción singular / plural es importante!) Finalmente, los elementos <TabCtrls: CustomTab>, que son contenedores del contenido de cada pestaña, también están dentro del elemento principal (pero no agrupados en su propio elemento principal como con los botones).

Solo hay una cosa importante que debes saber: los elementos del botón y los elementos de la pestaña son cada uno una lista ordenada y el primer botón está asociado con la primera pestaña, el segundo con el segundo, y así sucesivamente. Obviamente, debe haber la misma cantidad de botones que pestañas.

Propiedades del control  Tab disponibles

Cada control tan simplemente se extiende de un control existente de Xamarin Forms, agregando sus propias propiedades y código. Estas son las clases base para cada una:

Control                              Control padre

CustomTabs                      StackLayout

CustomTabButtons            StackLayout

CustomTabButton              Botón

CustomTab                        StackLayout

Esto significa que cualquier propiedad que este en un StackLayout, como orientación, opciones horizontales/verticales, color de fondo, visibilidad, etc., está disponible en CustomTabs, CustomTabButtons y CustomTab . Del mismo modo, con CustomTabButton para las propiedades del botón.

Además de las propiedades estándar, se han añadido un par a CustomTabs para que pueda especificar información relacionada con las pestañas.

  1. SelectedTabIndex: un valor entero entre 0 y la pestaña numerada más alta (en este ejemplo, las pestañas están numeradas 0, 1 y 2). Esto determina qué pestaña se seleccionará cuando la página se muestre por primera vez.
  2. SelectedColor y UnselectedColor: el color aplicado al fondo del botón de tabulación seleccionado y los que no están seleccionados.

¡Esto es todo lo que necesitas saber! Puedes organizar tus controles como desees en cualquier lugar dentro de la página. Incluso puedes colocar los botones de tabulación debajo del contenido de la pestaña.

Conclusión

Espero que estos controles te sean útiles. Y que me ayudes compartiendo este artículo de Xamarin Forms.

Artículos relacionados
4 Comentarios
  • Sasse Comentar
    Wednesday, March 28, 2018

    ?f you ?ould l?ke to obtain much from th?s paragraph then y?u have to apply thes methods t? you? w?n webpage.

  • Woody Comentar
    Saturday, March 31, 2018

    I'm amazed, ? m?st say. Seldom ?o I c?me ?cross a blog that's equallly educatkve ?nd entertaining, ?nd let me tel? you, y?u have hit the nail ?n the head. The problem ?? someth?ng too f?w people are speaking intelligently ?bout. ?'m very haopy that I fo?nd th?s in my hunt foor something ?oncerning th?s.

  • Leonardo Comentar
    Wednesday, April 18, 2018

    Hola es posible que puedas compartir el código o prueba de concepto por favor

    • LuisComentar
      Wednesday, April 18, 2018

      Precisamente el post es para que aprendas a implementarlo...

  • Aslan Comentar
    Friday, November 2, 2018

    tengo este errorr: Incoherencia de accesibilidad el tipo de propiedad 'Custm tab' es menos accesible que la propiedad 'Customtabs.selectedTab'

Enviame un comentario