1

I want to create a customListView in Xamarin Forms that shows 3 columns and N rows (I will get the source from a web service). Each cell has an icon and a description. I have found several examples but no one uses columns.

7
  • what you mean by "columns"? If they should scroll independently, just create 3 listviews and place them one after another. If they shouldnt, just place separators between items in a row. Commented Apr 18, 2016 at 21:58
  • i want something like this code4app.qiniudn.com/photo/51e3874c6803fac50d000001_1.png icons and description will come from json Commented Apr 18, 2016 at 22:12
  • That's just a grid - use a GridLayout for that Commented Apr 18, 2016 at 22:22
  • Okay. Simplest way is to use Grid. Wrap it into ScrollView, if grid height exceed page's height. But if you want to use ListView anyway, just place 3 items in one row. Commented Apr 18, 2016 at 22:23
  • Well i wont have always complete row items i can have 5 items or 7 so it will have 3 items in first row and 2 in second row, let me try using listview Commented Apr 18, 2016 at 22:29

2 Answers 2

2

Depending on what you want, you can create a ListView then have a Grid for each Item, where you define the 3 columns. This will allow you to keep the extra features of the ListView like PullToRefresh.

If you want an automatic GridView type control, I did build one off ChaseFlorell's example as shown in this forum post: https://forums.xamarin.com/discussion/61925/how-to-make-the-dynamic-grid-view-and-make-it-clickable

This way it makes it data bindable, rather than having to explicitly define each one.

First is the Grid Control

<Grid xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="Mobile.Controls.GridView">

</Grid>

public partial class GridView : Grid
{
    public GridView()
    {
        InitializeComponent();

        for (var i = 0; i < MaxColumns; i++)
            ColumnDefinitions.Add(new ColumnDefinition());
    }

    public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create<GridView, object>(p => p.CommandParameter, null);
    public static readonly BindableProperty CommandProperty = BindableProperty.Create<GridView, ICommand>(p => p.Command, null);
    public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create<GridView, IEnumerable<object>>(p => p.ItemsSource, null, BindingMode.OneWay, null, (bindable, oldValue, newValue) =>  { ((GridView)bindable).BuildTiles(newValue); });

    private int _maxColumns = 2;
    private float _tileHeight = 0;

    public Type ItemTemplate { get; set; } = typeof(DocumentTypeTemplate);

    public int MaxColumns
    {
        get { return _maxColumns; }
        set { _maxColumns = value; }
    }

    public float TileHeight
    {
        get { return _tileHeight; }
        set { _tileHeight = value; }
    }

    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public IEnumerable<object> ItemsSource
    {
        get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value);  }
    }

    public void BuildTiles(IEnumerable<object> tiles)
    {
        try
        {
            if (tiles == null || tiles.Count() == 0)
                Children?.Clear();

            // Wipe out the previous row definitions if they're there.
            RowDefinitions?.Clear();

            var enumerable = tiles as IList ?? tiles.ToList();
            var numberOfRows = Math.Ceiling(enumerable.Count / (float)MaxColumns);

            for (var i = 0; i < numberOfRows; i++)
                RowDefinitions?.Add(new RowDefinition { Height = TileHeight });

            for (var index = 0; index < enumerable.Count; index++)
            {
                var column = index % MaxColumns;
                var row = (int)Math.Floor(index / (float)MaxColumns);

                var tile = BuildTile(enumerable[index]);

                Children?.Add(tile, column, row);
            }
        }
        catch { // can throw exceptions if binding upon disposal
        }
    }

    private Layout BuildTile(object item1)
    {
        var buildTile = (Layout)Activator.CreateInstance(ItemTemplate, item1);
        buildTile.InputTransparent = false;

        var tapGestureRecognizer = new TapGestureRecognizer
        {
            Command = Command,
            CommandParameter = item1,
            NumberOfTapsRequired = 1                
        };

        buildTile?.GestureRecognizers.Add(tapGestureRecognizer);


        return buildTile;
    }
}

Then define a template

<Grid xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Mobile.TypeTemplate">

    <Label Text="{Binding Name}" />

</Grid>

public partial class TypeTemplate : Grid
    {
        public TypeTemplate()
        {
            InitializeComponent();
        }

        public TypeTemplate(object item)
        {
            InitializeComponent();
            BindingContext = item;
        }

    }

Then use the control

 <control:GridView HorizontalOptions="FillAndExpand"
                    Grid.Row="1"
                    VerticalOptions="FillAndExpand"
                    RowSpacing="20"
                    ColumnSpacing="20"
                    MaxColumns="2"
                    ItemsSource="{Binding ListOfData}"
                    CommandParameter="{Binding}"
                    Command="{Binding ClickCommand}"
                    IsClippedToBounds="False">
    <control:GridView.TileHeight>
      <OnPlatform x:TypeArguments="x:Single"
                  iOS="60"
                  Android="60"
                  WinPhone="90" />
    </control:GridView.TileHeight>
  </control:GridView>
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks this very simple and useful for IOS but in Android case it's not working for me, It's overlapping the other layout with large collection of data
0

A much easier solution than the proposed is to use the DevExpress Grid control. It has support for headers, row virtualization, pull-to-refresh, load on demand and a bunch of other useful features.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.