Simple Accordion User Control In Xamarin Forms

Many users of JQuery UI (both Mobile as well as web) are quite fond of accordion user control and ask for the same or a similar kind of control in Xamarin/ Xamarin Forms. As we all know that Xamarin forms controls are an abstraction to the native controls available in respective native platforms and since there is no accordion control available in any of the native mobile frameworks it’s not available as an out-of-the-box control in Xamarin Forms. So in this article we will be creating a simple accordion user-control using simple Xamarin Forms controls like Button and ContentView.

Firstly, we need to understand the functionality of an accordion, as per Wikipedia the developer's definition of an accordion is:

    Several buttons or labels are stacked upon one another. At most one of them can be “active”. When a button is active the space below the button is used to display a paned window. The pane is usually constrained by the width of labels. When opened it shifts labels under the clicked label down according to the height of that window. Only one button or pane combination can be active at any one time; when a button is selected any other active panes cease to be active and are hidden. The active pane may have scrollbars.

And that’s exactly what I have done.

The ‘Accordion’ class is extended from ContentView class of Xamarin; forms in which I am creating an accordion from list ‘AccordianSource’ objects on DataBind Method of the Class. The code of ‘Accordion’ user control is as follows:
  1. public class Accordion: ContentView  
  2. {  
  3.     #region Private Variables  
  4.   
  5.     List < AccordionSource > mDataSource;  
  6.     bool mFirstExpaned = false;  
  7.     StackLayout mMainLayout;  
  8.      
  9.     #endregion  
  10.   
  11.     public Accordion()  
  12.     {  
  13.         var mMainLayout = new StackLayout();  
  14.         Content = mMainLayout;  
  15.     }  
  16.     public Accordion(List < AccordionSource > aSource)  
  17.     {  
  18.         mDataSource = aSource;  
  19.         DataBind();  
  20.     }  
  21.  
  22.     #region Properties  
  23.   
  24.     public List < AccordionSource > DataSource  
  25.     {  
  26.         get  
  27.         {  
  28.             return mDataSource;  
  29.         }  
  30.         set  
  31.         {  
  32.             mDataSource = value;  
  33.         }  
  34.     }  
  35.     public bool FirstExpaned  
  36.     {  
  37.         get  
  38.         {  
  39.             return mFirstExpaned;  
  40.         }  
  41.         set  
  42.         {  
  43.             mFirstExpaned = value;  
  44.         }  
  45.     }  
  46.  
  47.     #endregion  
  48.   
  49.     public void DataBind()  
  50.     {  
  51.         var vMainLayout = new StackLayout();  
  52.         var vFirst = true;  
  53.         if (mDataSource != null)  
  54.         {  
  55.             foreach(var vSingleItem in mDataSource)  
  56.             {  
  57.                 var vHeaderButton = new AccordionButton()  
  58.                 {  
  59.                     Text = vSingleItem.HeaderText,  
  60.                         TextColor = vSingleItem.HeaderTextColor,  
  61.                         BackgroundColor = vSingleItem.HeaderBackGroundColor  
  62.                 };  
  63.                 var vAccordionContent = new ContentView()  
  64.                 {  
  65.                     Content = vSingleItem.ContentItems,  
  66.                         IsVisible = false  
  67.                 };  
  68.                 if (vFirst)  
  69.                 {  
  70.                     vHeaderButton.Expand = mFirstExpaned;  
  71.                     vAccordionContent.IsVisible = mFirstExpaned;  
  72.                     vFirst = false;  
  73.                 }  
  74.                 vHeaderButton.AssosiatedContent = vAccordionContent;  
  75.                 vHeaderButton.Clicked += OnAccordionButtonClicked;  
  76.                 vMainLayout.Children.Add(vHeaderButton);  
  77.                 vMainLayout.Children.Add(vAccordionContent);  
  78.             }  
  79.         }  
  80.         mMainLayout = vMainLayout;  
  81.         Content = mMainLayout;  
  82.     }  
  83.     void OnAccordionButtonClicked(object sender, EventArgs args)  
  84.     {  
  85.         foreach(var vChildItem in mMainLayout.Children)  
  86.         {  
  87.             if (vChildItem.GetType() == typeof(ContentView)) vChildItem.IsVisible = false;  
  88.             if (vChildItem.GetType() == typeof(AccordionButton))  
  89.             {  
  90.                 var vButton = (AccordionButton) vChildItem;  
  91.                 vButton.Expand = false;  
  92.             }  
  93.         }  
  94.         var vSenderButton = (AccordionButton) sender;  
  95.         if (vSenderButton.Expand)  
  96.         {  
  97.             vSenderButton.Expand = false;  
  98.         }  
  99.         else vSenderButton.Expand = true;  
  100.         vSenderButton.AssosiatedContent.IsVisible = vSenderButton.Expand;  
  101.     }  
  102. }  
This control has one public method ‘DataBind’ and two public properties ‘FirstExpaned’ and ‘DataSource’. ’DataBind’ Method will be used when we use the control with XAML page as the control will not be initialized with data source passed. ‘FirstExpaned’ is a boolean to decide whether the control should appear with all items collapsed or first one expanded and ‘DataSource’ Property of the control will require the list of ‘AccordionSource’ class defined as below:
  1. using System;  
  2. public class AccordionSource  
  3. {  
  4.     public string HeaderText  
  5.     {  
  6.         get;  
  7.         set;  
  8.     }  
  9.     public Color HeaderTextColor  
  10.     {  
  11.         get;  
  12.         set;  
  13.     }  
  14.     public Color HeaderBackGroundColor  
  15.     {  
  16.         get;  
  17.         set;  
  18.     }  
  19.     public View ContentItems  
  20.     {  
  21.         get;  
  22.         set;  
  23.     }  
  24. }  
The property names of the class are pretty self explanatory. The three properties containing ‘Header’ are for the Header button created for each accordion item and the ContentItems is of View class so that we can put any container object inside it like ListView, StackView etc. In order to check whether the accordion header is expanded or not and to identify the Content associated with the button in order to show/hide, we require a button with some extra properties and we will get those by following ‘AccordionButton’ Class:
  1. public class AccordionButton: Button {  
  2.  #region Private Variables  
  3.  bool mExpand = false;  
  4.  #endregion  
  5.  public AccordionButton() {  
  6.   HorizontalOptions = LayoutOptions.FillAndExpand;  
  7.   BorderColor = Color.Black;  
  8.   BorderRadius = 5;  
  9.   BorderWidth = 0;  
  10.  }#region Properties  
  11.  public bool Expand {  
  12.   get {  
  13.    return mExpand;  
  14.   }  
  15.   set {  
  16.    mExpand = value;  
  17.   }  
  18.  }  
  19.  public ContentView AssosiatedContent {  
  20.   get;  
  21.   set;  
  22.  }#endregion  
  23. }  
This completes the code of our user control which can be found in the ‘accodion.cs’ class in example code. Now let''s see how to use this control in a sample application. In the application there is an accordion containing 3 Items. First one is a list, second is static data and third is again list.

The ‘XamlExample’ page have used this control using following code in XAML:
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <ContentPage  
  3.     xmlns="http://xamarin.com/schemas/2014/forms"  
  4.     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
  5.     xmlns:ctrl="clr-namespace:AccordionEx;assembly=AccordionEx" x:Class="AccordionEx.XamlExample" Title="XAML Example">  
  6.     <ContentPage.Content>  
  7.         <ctrl:Accordion x:Name="MainOne" />  
  8.     </ContentPage.Content>  
  9. </ContentPage>  
And the following code in the code behind constructor of the page:
  1. public XamlExample ()  
  2. {  
  3.    InitializeComponent ();  
  4.    MainOne.DataSource = GetSampleData ();  
  5.    MainOne.DataBind ();  
  6. }  
The ‘CodeExample’ page has used this control directly in the code file using the following code:
  1. public CodeEaxmple()  
  2. {  
  3.     Title = "Code Example";  
  4.     var vAccordionSource = GetSampleData();  
  5.     var vAccordionControl = new Accordion(vAccordionSource);  
  6.     Content = vAccordionControl;  
  7. }  
In both the example pages method ‘GetSampleData’ is used to get the sample data to bind with the accordion user control. The code of ‘GetSampleData’ method is as follows:
  1. public List < AccordionSource > GetSampleData()  
  2. {  
  3.     var vResult = new List < AccordionSource > ();  
  4.    
  5.     #region First List View  
  6.     var vListOne = new List < SimpleObject > ();  
  7.     for (var iCount = 0; iCount < 6; iCount++)  
  8.     {  
  9.         var vObject = new SimpleObject()  
  10.         {  
  11.             TextValue = "ObjectNo-" + iCount.ToString(),  
  12.                 DataValue = iCount.ToString()  
  13.         };  
  14.         vListOne.Add(vObject);  
  15.     }  
  16.     var vListViewOne = new ListView()  
  17.     {  
  18.         ItemsSource = vListOne,  
  19.             ItemTemplate = new DataTemplate(typeof(ListDataViewCell))  
  20.     };  
  21.     vListViewOne.ItemTapped += OnListItemClicked;  
  22.      
  23.     #endregion# region Second List  
  24.     var vListTwo = new List < SimpleObject > ();  
  25.     var vObjectRavi = new SimpleObject()  
  26.     {  
  27.         TextValue = "S Ravi Kumar",  
  28.             DataValue = "1"  
  29.     };  
  30.     vListTwo.Add(vObjectRavi);  
  31.     var vObjectFather = new SimpleObject()  
  32.     {  
  33.         TextValue = "Father",  
  34.             DataValue = "2"  
  35.     };  
  36.     vListTwo.Add(vObjectFather);  
  37.     var vObjectTrainer = new SimpleObject()  
  38.     {  
  39.         TextValue = "Trainer",  
  40.             DataValue = "2"  
  41.     };  
  42.     vListTwo.Add(vObjectTrainer);  
  43.     var vObjectConsultant = new SimpleObject()  
  44.     {  
  45.         TextValue = "Consultant",  
  46.             DataValue = "2"  
  47.     };  
  48.     vListTwo.Add(vObjectConsultant);  
  49.     var vObjectArchitect = new SimpleObject()  
  50.     {  
  51.         TextValue = "Architect",  
  52.             DataValue = "2"  
  53.     };  
  54.     vListTwo.Add(vObjectArchitect);  
  55.     var vListViewTwo = new ListView()  
  56.     {  
  57.         ItemsSource = vListTwo,  
  58.             ItemTemplate = new DataTemplate(typeof(ListDataViewCell))  
  59.     };  
  60.     vListViewTwo.ItemTapped += OnListItemClicked;  
  61.  
  62.     #endregion  
  63.     #region StackLayout  
  64.   
  65.     var vViewLayout = new StackLayout()  
  66.     {  
  67.         Children = {  
  68.             new Label  
  69.             {  
  70.                 Text = "Static Content:"  
  71.             },  
  72.             new Label  
  73.             {  
  74.                 Text = "Name : S Ravi Kumar"  
  75.             },  
  76.             new Label  
  77.             {  
  78.                 Text = "Roles : Father,Trainer,Consultant,Architect"  
  79.             }  
  80.         }  
  81.     };  
  82.  
  83.     #endregion  
  84.     var vFirstAccord = new AccordionSource()  
  85.     {  
  86.         HeaderText = "First",  
  87.             HeaderTextColor = Color.Black,  
  88.             HeaderBackGroundColor = Color.Yellow,  
  89.             ContentItems = vListViewTwo  
  90.     };  
  91.     vResult.Add(vFirstAccord);  
  92.     var vSecond = new AccordionSource()  
  93.     {  
  94.         HeaderText = "Second ",  
  95.             HeaderTextColor = Color.White,  
  96.             HeaderBackGroundColor = Color.FromHex("#77d065"),  
  97.             ContentItems = vViewLayout  
  98.     };  
  99.     vResult.Add(vSecond);  
  100.     var vThird = new AccordionSource()  
  101.     {  
  102.         HeaderText = "Third",  
  103.             HeaderTextColor = Color.White,  
  104.             HeaderBackGroundColor = Color.Purple,  
  105.             ContentItems = vListViewOne  
  106.     };  
  107.     vResult.Add(vThird);  
  108.     return vResult;  
  109. }  
In the above GetSampleData() method as the ListView objects are created on runtime, they require a custom view cell (got from ‘ListDataViewCell’) which shows the ‘TextValue’ property of the ‘SimpleObject’ class in a Label inside Stacklayout. The code of both the classes are in ‘App.cs’ file so that they can be utilized from both the example pages.

The code of ‘ListDataViewCell’ and ‘SimpleObject’ class are as follows:
  1. public class ListDataViewCell: ViewCell  
  2. {  
  3.     public ListDataViewCell()  
  4.     {  
  5.         var label = new Label()  
  6.         {  
  7.             Font = Font.SystemFontOfSize(NamedSize.Default),  
  8.                 TextColor = Color.Blue  
  9.         };  
  10.         label.SetBinding(Label.TextProperty, new Binding("TextValue"));  
  11.         label.SetBinding(Label.ClassIdProperty, new Binding("DataValue"));  
  12.         View = new StackLayout()  
  13.         {  
  14.             Orientation = StackOrientation.Vertical,  
  15.                 VerticalOptions = LayoutOptions.StartAndExpand,  
  16.                 Padding = new Thickness(12, 8),  
  17.                 Children = {  
  18.                     label  
  19.                 }  
  20.         };  
  21.     }  
  22. }  
  23. public class SimpleObject  
  24. {  
  25.     public string TextValue  
  26.     {  
  27.         get;  
  28.         set;  
  29.     }  
  30.     public string DataValue  
  31.     {  
  32.         get;  
  33.         set;  
  34.     }  
  35. }  
Apart from the above code, sample application contains a ‘HomePage’ (written in XAML as I like it more) with two buttons to display the XAML Example and CodeExample page. The code of HomePage is as follows:

XAML Code:
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <ContentPage  
  3.     xmlns="http://xamarin.com/schemas/2014/forms"  
  4.     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="AccordionEx.HomePage" Title="Accordion Example" >  
  5.     <ContentPage.Resources>  
  6.         <ResourceDictionary>  
  7.             <Style TargetType="Button">  
  8.                 <Setter Property="BorderRadius" Value="10" />  
  9.                 <Setter Property="BorderWidth" Value="2" />  
  10.                 <Setter Property="WidthRequest" Value="150" />  
  11.                 <Setter Property="HeightRequest" Value="150" />  
  12.                 <Setter Property="HorizontalOptions" Value="Center" />  
  13.                 <Setter Property="VerticalOptions" Value="Center" />  
  14.                 <Setter Property="FontSize" Value="Medium" />  
  15.                 <Setter Property="TextColor" Value="Red" />  
  16.             </Style>  
  17.         </ResourceDictionary>  
  18.     </ContentPage.Resources>  
  19.     <ContentPage.Content>  
  20.         <StackLayout VerticalOptions = "Center">  
  21.             <Button Text="Xaml Page" Clicked="OnXamlClicked" />  
  22.             <Button Text="Code Page" Clicked="OnCodeClicked" />  
  23.         </StackLayout>  
  24.     </ContentPage.Content>  
  25. </ContentPage>  
Code Behind C# code:
  1. public partial class HomePage: ContentPage  
  2. {  
  3.     public HomePage()  
  4.     {  
  5.         InitializeComponent();  
  6.     }  
  7.     public void OnXamlClicked(object sender, EventArgs args)  
  8.     {  
  9.         Navigation.PushAsync(new XamlExample());  
  10.     }  
  11.     public void OnCodeClicked(object sender, EventArgs args)  
  12.     {  
  13.         Navigation.PushAsync(new CodeEaxmple());  
  14.     }  
  15. }  
The above written ‘HomePage’ is invoked in ‘App.cs’ constructor using Navigation Page, the code for same is as follows:
  1. public App ()  
  2. {  
  3.    // The root page of your application  
  4.    MainPage = new NavigationPage(new HomePage());  
  5. }  
This is how the sample application looks on emulators:

iPhone

Xamarin Android Player:

Xamarin Android Player


The source code of sample application containing accordion user control and pages using the control can be downloaded from Github.

In the above post I have created a bare bone, accordion user control which anyone can customize as per their requirements, let me know if I have missed anything.
 
Read more articles on Xamarin:


Similar Articles