Milèstre BV
Sep 10, 2018

Xamarin Forms: dynamic list


When you do have a page that makes use of a ScrollView you can not use a ListView in it. It will result in strange presentation. For instance using this 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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"

             prism:ViewModelLocator.AutowireViewModel="True"

             xmlns:i18n="clr-namespace:TenCate_App.Helpers;assembly=TenCate_App"

             xmlns:mr="clr-namespace:MR.Gestures;assembly=MR.Gestures"

             xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"

             x:Class="TenCate_App.Views.ProductPage" x:Name="ProductPageRef"

             Title="{i18n:Translate ProductPage_Title}" ControlTemplate="{StaticResource PageTemplate}"

             NavigationPage.BackButtonTitle="">

    <ScrollView>

        <Grid Padding="0">

            <Grid.RowDefinitions>

                <RowDefinition Height="90" />

                <RowDefinition Height="35" />

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

 

            <StackLayout Grid.Row="2" Padding="0" BackgroundColor="White" VerticalOptions="FillAndExpand">

                <Label Margin="10" Text="{Binding ProductItem.Name}" Style="{StaticResource NameLabelStyle}" />

                <Label Margin="10" Text="{i18n:Translate ProductDescription}" Style="{StaticResource SectionLabelStyle}" />

                <Label Margin="10,0,10,0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" LineBreakMode="WordWrap"

                            Text="{Binding ProductItem.Body}" Style="{StaticResource InfoLabelValueStyle}" />

                <Label Margin="10,10,10,0" Text="{i18n:Translate ProductBenefits}" Style="{StaticResource SectionLabelStyle}" />

                <ListView x:Name="lstFeaturess" VerticalOptions="FillAndExpand" Margin="0" HeightRequest="{Binding ProductItem.FeaturesHeight}"

                          SeparatorVisibility="None" ItemsSource="{Binding ProductItem.FeatureItems}" HasUnevenRows="True">

                    <ListView.ItemTemplate>

                        <DataTemplate>

                            <ViewCell>

                                <Grid Padding="10,0,14,0">

                                    <Grid.ColumnDefinitions>

                                        <ColumnDefinition Width="20" />

                                        <ColumnDefinition Width="*" />

                                    </Grid.ColumnDefinitions>

                                    <Image Grid.Column="0" HeightRequest="12" HorizontalOptions="Start" Source="arrow_right_active.png" />

                                    <Label Grid.Column="1" Text="{Binding Title}" Style="{StaticResource InfoLabelStyle}" />

                                </Grid>

                            </ViewCell>

                        </DataTemplate>

                    </ListView.ItemTemplate>

                </ListView>

will result in this presentation:

Using ListView in a ScrollView

To be able to present a dynamic list we are going to use a custom Grid control where the rows are create dynamically.

The XAML of this new custom grid control looks like:

<?xml version="1.0" encoding="UTF-8"?>

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

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

     x:Class="TenCate_App.Views.DynamicGridView"

     x:Name="DynamicGrid" RowSpacing="0">

 

    <Grid.ColumnDefinitions>

        <ColumnDefinition Width="20"/>

        <ColumnDefinition Width="*"/>

    </Grid.ColumnDefinitions>

 

</Grid>

And the code behind:

using System.Collections.Generic;

using TenCate_App.Models;

using Xamarin.Forms;

 

namespace TenCate_App.Views

{

    public partial class DynamicGridView : Grid

    {

        public DynamicGridView()

        {

            InitializeComponent();

        }

 

        public static readonly BindableProperty DynamicRowsProperty =

            BindableProperty.Create(nameof(DynamicRows), typeof(List<Item>), typeof(DynamicGridView), null,

                BindingMode.OneWay, null, OnDynamicRowsChanged);

 

        private static void OnDynamicRowsChanged(BindableObject bindable, object oldvalue, object newvalue)

        {

            var control = (DynamicGridView)bindable;

            if (control != null)

            {

                if (newvalue is List<Item> dynamicRows)

                {

                    var rowNumber = -1;

                    Style imageStyle = Application.Current.Resources["ItemIconStyle"] as Style;

                    Style labelStyle = Application.Current.Resources["ItemLabelStyle"] as Style;

                    foreach (var dynamicRow in dynamicRows)

                    {

                        var image = new Image {Source = "arrow_right_active.png"};

                        image.Style = imageStyle;

                        var valueLabel = new Label { Text = dynamicRow.Title };

                        valueLabel.Style = labelStyle;

 

                        rowNumber++;

                        control.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });

                        control.Children.Add(image, 0, rowNumber);

                        control.Children.Add(valueLabel, 1, rowNumber);

                    }

                }

            }

        }

 

        public List<Item> DynamicRows

        {

            get => (List<Item>)GetValue(DynamicRowsProperty);

            set => SetValue(DynamicRowsProperty, value);

        }

    }

}



Now we are going to use this custom Grid control in our page withe the ScrollView:
 

<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"

             prism:ViewModelLocator.AutowireViewModel="True"

             xmlns:i18n="clr-namespace:TenCate_App.Helpers;assembly=TenCate_App"

     xmlns:localCtrl="clr-namespace:TenCate_App.Views;assembly=TenCate_App"

             xmlns:mr="clr-namespace:MR.Gestures;assembly=MR.Gestures"

             xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"

             x:Class="TenCate_App.Views.ProductPage" x:Name="ProductPageRef"

             Title="{i18n:Translate ProductPage_Title}" ControlTemplate="{StaticResource PageTemplate}"

             NavigationPage.BackButtonTitle="">

    <ScrollView>

        <Grid Padding="0">

            <Grid.RowDefinitions>

                <RowDefinition Height="90" />

                <RowDefinition Height="35" />

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

 

            <StackLayout Grid.Row="2" Padding="0" BackgroundColor="White" VerticalOptions="FillAndExpand">

                <Label Margin="10" Text="{Binding ProductItem.Name}" Style="{StaticResource NameLabelStyle}" />

                <Label Margin="10" Text="{i18n:Translate ProductDescription}" Style="{StaticResource SectionLabelStyle}" />

                <Label Margin="10,0,10,0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" LineBreakMode="WordWrap"

                            Text="{Binding ProductItem.Body}" Style="{StaticResource InfoLabelValueStyle}" />

                <Label Margin="10,10,10,0" Text="{i18n:Translate ProductBenefits}" Style="{StaticResource SectionLabelStyle}" />

                <localCtrl:DynamicGridView DynamicRows="{Binding ProductItem.FeatureItems}"></localCtrl:DynamicGridView>


And this will be the presentation:

Dynamic List based on a Grid Control
About us

Milèstre is a digital development agency based in Maastricht and operating all over the world. Since 2003 we build software solutions together with established companies and great startups. During the years we have developed a process that enables us to transform ideas into meaningful, intelligent and designfull experiences for mobile and web. A process where strategy, design, development and user experience are playing an important rol.

 

Contact us

Milestre BV
Ambyerstraat Zuid 82
6225 AJ Maastricht
Netherlands

Tel: +31(0)43 - 4070780
Email: info@milestre.nl
Recent Posts

© Copyright 2019 - Milestre BV
Top