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.
-
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.Flame239– Flame2392016-04-18 21:58:31 +00:00Commented Apr 18, 2016 at 21:58
-
i want something like this code4app.qiniudn.com/photo/51e3874c6803fac50d000001_1.png icons and description will come from jsonJosé Manuel Peña– José Manuel Peña2016-04-18 22:12:48 +00:00Commented Apr 18, 2016 at 22:12
-
That's just a grid - use a GridLayout for thatJason– Jason2016-04-18 22:22:10 +00:00Commented 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.Flame239– Flame2392016-04-18 22:23:40 +00:00Commented 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 listviewJosé Manuel Peña– José Manuel Peña2016-04-18 22:29:28 +00:00Commented Apr 18, 2016 at 22:29
2 Answers
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>
1 Comment
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.