Android – Xamarin.Forms – Grid – Change column number on device orientation portrait to landscape

Optimizing display device when the screen orientation id changing can be very interesting.

As part of the development my app Smartdrive  I wanted to display a table of statistics or the number of column changed after screen orientation.

In Portrait , the display should contain only 3 columns when in landscape we want 6 .

2 possible solutions :

1 – use a “Listview”; this will simply change the number of column by working only on the binding. This solution is efficient and effective but does not allow to align vertically the columns.

2 – Use a “GridView”; unlike the ” Listview ” vertical alignment is easy as pie. But it is not possible to change the number of columns thanks to the binding. (Crash risk ) .

I chose to use a ” GridView ” playing with the attribute ” macolonne.IsVisible ” to show or hide columns.

My approach does not use XAML.

1. I use a Class derived from ” Grid ” to generate my table:

 public class CustomGridStatistiques : Grid
    {
        public CustomGridStatistiques(IList stats)
        {
            // currency, volume etc.. symbols
            var unitD = new UnitesMesure().Distances().Where(d => d.Id == Helpers.Settings.UnitDistance).Select(p => p.Symbol).FirstOrDefault();
            var unitV = new UnitesMesure().Volumes().Where(d => d.Id == Helpers.Settings.UnitVolume).Select(p => p.Symbol).FirstOrDefault();

            CultureInfo cu = CultureInfo.CurrentCulture;
            NumberFormatInfo nfi = cu.NumberFormat;
            var symbol = nfi.CurrencySymbol;


            // Grid Définition 
            this.VerticalOptions = LayoutOptions.Start;
            this.HorizontalOptions = LayoutOptions.FillAndExpand;

            this.ColumnSpacing = 0;

            this.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); // Vehicule
            this.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); // Total travelles
            this.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); // Total fuel cost
            this.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); // Total coût entretien 
            this.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); // average fuel cuns.
            this.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(100, GridUnitType.Absolute) }); // cost for 1 Km or mile

            foreach (var stat in stats)
            {
                this.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            }
            var margin = new Thickness { Right = 5 };
            var marginstart = new Thickness { Left = 5 };

            // Headers Définition 
            this.Children.Add(new Label
            {
                Text = MobileSmartDrive.Resources.AppResources.label_Vehicule,
                HorizontalTextAlignment = TextAlignment.Start,
                Style = (Style)Application.Current.Resources["headerCol"],
                Margin = marginstart
            }, 0, 0);

            this.Children.Add(new Label
            {
                Text = MobileSmartDrive.Resources.AppResources.tab_header_TotalDistanceTraveled,
                HorizontalTextAlignment = TextAlignment.End,
                Style = (Style)Application.Current.Resources["headerCol"],
                Margin = margin
            }, 1, 0);

            this.Children.Add(new Label
            {
                Text = MobileSmartDrive.Resources.AppResources.tab_header_TotalFuel,
                HorizontalTextAlignment = TextAlignment.End,
                Style = (Style)Application.Current.Resources["headerCol"],
                Margin = margin
            }, 2, 0);

            this.Children.Add(new Label
            {
                Text = MobileSmartDrive.Resources.AppResources.tab_header_TotalRepair,
                HorizontalTextAlignment = TextAlignment.End,
                Style = (Style)Application.Current.Resources["headerCol"],
                Margin = margin
            }, 3, 0);
            this.Children.Add(new Label
            {
                Text = MobileSmartDrive.Resources.AppResources.tab_header_AverageConsumption,
                HorizontalTextAlignment = TextAlignment.End,
                Style = (Style)Application.Current.Resources["headerCol"],
                Margin = margin
            }, 4, 0);

            this.Children.Add(new Label
            {
                Text = string.Format(MobileSmartDrive.Resources.AppResources.tab_header_CostByUnit, unitD),
                HorizontalTextAlignment = TextAlignment.End,
                Style = (Style)Application.Current.Resources["headerCol"],
                Margin = margin
            }, 5, 0);


            // content to display 
            try
            {

                // a counter is used to switch the color of the lines
                int i = 1;
                foreach (var stat in stats)
                {
                    var labelVehicule = new Label
                    {
                        Text = stat.Vehicule,
                        HorizontalTextAlignment = TextAlignment.Start,
                        Style = (Style)Application.Current.Resources["header"] 
                    };

                    var labelDistanceParcouru = new Label
                    {
                        Text = string.Format(MobileSmartDrive.Resources.AppResources.format_Distance, stat.TotalKmParcouru, unitD),
                        HorizontalTextAlignment = TextAlignment.End 
                    };

                    var labelFuelCost = new Label
                    {
                        Text = string.Format(MobileSmartDrive.Resources.AppResources.format_SimpleCost, Math.Round(stat.CoutTotalCarburant, 2), symbol),
                        HorizontalTextAlignment = TextAlignment.End 
                    };

                    var labelCostRepair = new Label
                    {
                        Text = string.Format(MobileSmartDrive.Resources.AppResources.format_SimpleCost, stat.CoutTotalEntretien, symbol),
                        HorizontalTextAlignment = TextAlignment.End 
                    };

                    var labelAverageConso = new Label
                    {
                        Text = string.Format(MobileSmartDrive.Resources.AppResources.format_AverageConsumption, Math.Round(stat.ConsoMoyenne, 2), unitV, unitD),
                        HorizontalTextAlignment = TextAlignment.End 
                    };

                    var labelCostByUnit = new Label
                    {
                        Text = string.Format(MobileSmartDrive.Resources.AppResources.format_CostByDistance, Math.Round(stat.CoutAuKilometre, 2), symbol, unitD),
                        HorizontalTextAlignment = TextAlignment.End,
                        Style = (Style)Application.Current.Resources["importanteData"]
                    };

                    // Pyjama 
                    if (i % 2 == 1 && stats.Count > 1)
                    {
                        labelVehicule.BackgroundColor = (Color)Application.Current.Resources["alternateRowColor1"];
                        labelDistanceParcouru.BackgroundColor = (Color)Application.Current.Resources["alternateRowColor1"];
                        labelFuelCost.BackgroundColor = (Color)Application.Current.Resources["alternateRowColor1"];
                        labelCostRepair.BackgroundColor = (Color)Application.Current.Resources["alternateRowColor1"];
                        labelAverageConso.BackgroundColor = (Color)Application.Current.Resources["alternateRowColor1"];
                        labelCostByUnit.BackgroundColor = (Color)Application.Current.Resources["alternateRowColor1"];
                    }

                    // Add labels to the grid
                    this.Children.Add(labelVehicule, 0, i);
                    this.Children.Add(labelDistanceParcouru, 1, i);
                    this.Children.Add(labelFuelCost, 2, i);
                    this.Children.Add(labelCostRepair, 3, i);
                    this.Children.Add(labelAverageConso, 4, i);
                    this.Children.Add(labelCostByUnit, 5, i);
                    i++;
                }

            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
    }

2. I instantiate the Grid in my constructor Class Page ( ContentPage )

      
Grid gridStat;
public StatistiquesPage()
{
     gridStat = new CustomGridStatistiques(new List());            
     this.Content = gridStat;
}

The page is ready with a full display of all the columns regardless of the orientation.

Let’s do some magic

the management of orientation is done in the method “OnSizeAllocated” of the “ContentPage” Class.
In our case , we want to play with the columns 1 to 3. For this we will:

  1. Iterate over all children of the Grid
  2. Get the property ” Grid.ColumnProperty “
  3. Modify the attribute ” IsVisible ” for columns from 1 to 3
 
protected override void OnSizeAllocated(double width, double height)
{
        base.OnSizeAllocated(width, height);           

        // It runs through the GRID elements
        foreach (View child in gridStat.Children)
        {
            // We use ColumnProperty to find the column to interact with
            if ((int)child.GetValue(Grid.ColumnProperty) > 0 && (int)child.GetValue(Grid.ColumnProperty) <= 3)
            {
                // If the width is greater than the height , we are in Landscape
                // In this case it shows all the columns
                child.IsVisible = (width > height);
            }
      }
}

It’s done, the number of column changes according to the orientation while being perfectly aligned vertically.

Statistiques Portrait Xamarin Grid
3 Columns displayed in portrait orientation

Statistiques Paysage Xamarin Grid
6 columns displayed in landscape orientation

If you found this tutorial useful please comment and share. Enjoy !

Leave a Reply

Your email address will not be published. Required fields are marked *