I want such type of ListView
with different ViewCell
types, different data, different header text (with custom header cell), but all in one ListView
:
**********************
* General info
**********************
| Category: Cabrio
| Type: Sportscar
**********************
* Available models
**********************
| Year: 2007
| Manufacturer: Chevrolet
| Model: Corvette
----------------------
| Year: 2009
| Manufacturer: Dodge
| Model: Charger
----------------------
Therefore I use this Grouping
object to hold the section (header) title and the list:
Grouping.cs:
public class Grouping<K, T> : ObservableCollection<T>
{
public K Key { get; private set; }
public Grouping(K key, IEnumerable<T> items)
{
Key = key;
foreach (var item in items)
this.Items.Add(item);
}
}
Taken from this not available link.
How can I use different data in my ListView
? Currently I have this non compiling code in my
MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
private ObservableCollection<Model.Grouping<string, object>> itemsGrouped;
public MainPage()
{
InitializeComponent();
this.itemsGrouped = new ObservableCollection<Grouping<string, object>>();
List<Category> categories = new List<Category>();
categories.Add(new Category("Cabrio", "Sportscar"));
this.itemsGrouped.Add(new Grouping<string, Category>("General info", categories));
List<CarInfo> cars = new List<CarInfo>();
cars.Add(new CarInfo("2007", "Chevrolet", "Corvette"));
cars.Add(new CarInfo("2009", "Dodge", "Charger"));
this.itemsGrouped.Add(new Grouping<string, CarInfo>("Available models", cars));
this.mainList.BindingContext = this.itemsGrouped;
}
}
Argument 1: cannot convert from 'TestGroupedListView.Model.Grouping<string, TestGroupedListView.Model.Category>' to 'TestGroupedListView.Model.Grouping<string, object>'
The general idea is to use a DataTemplateSelector
to be able to use different type of cells and use Grouping
to have different custom header titles.
Here is my full example project code:
Category.cs:
public class Category
{
public string CategoryName { get; set; }
public string TypeOfCar { get; set; }
public Category(string categoryName, string typeOfCar)
{
this.CategoryName = categoryName;
this.TypeOfCar = typeOfCar;
}
}
CarInfo.cs:
public class CarInfo
{
public string Year { get; set; }
public string Manufacturer { get; set; }
public string Name { get; set; }
public CarInfo(string year, string manufacturer, string name)
{
this.Year = year;
this.Manufacturer = manufacturer;
this.Name = name;
}
}
GeneralView.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestGroupedListView.CustomView.GeneralView">
<Grid x:Name="mainGrid" Padding="5" VerticalOptions="CenterAndExpand">
</Grid>
</ViewCell>
GeneralView.xaml.cs:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class GeneralView : ViewCell
{
private List<KeyValuePair<string, string>> dataList;
public GeneralView()
{
InitializeComponent();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
var generalInfo = BindingContext as Category;
if (generalInfo != null)
{
this.SetupView(generalInfo);
}
}
private void SetupView(Category item)
{
this.dataList = new List<KeyValuePair<string, string>>();
this.dataList.Add(new KeyValuePair<string, string>("Name", item.CategoryName));
this.dataList.Add(new KeyValuePair<string, string>("Type", item.TypeOfCar));
this.mainGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
this.mainGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
for (int i = 0; i < dataList.Count; i++)
{
this.mainGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
var keyLabel = new Label()
{
Text = dataList[i].Key
};
var valueLabel = new Label()
{
Text = dataList[i].Value
};
this.mainGrid.Children.Add(keyLabel, 0, i);
this.mainGrid.Children.Add(valueLabel, 1, i);
}
}
}
InfoItemView.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestGroupedListView.CustomView.InfoItemView">
<Grid x:Name="mainGrid" Padding="5" VerticalOptions="CenterAndExpand">
</Grid>
</ViewCell>
InfoItemView.xaml.cs:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class InfoItemView : ViewCell
{
private List<KeyValuePair<string, string>> dataList;
public InfoItemView()
{
InitializeComponent();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
var info = BindingContext as CarInfo;
if (info != null)
{
this.SetupView(info);
}
}
private void SetupView(CarInfo item)
{
this.dataList = new List<KeyValuePair<string, string>>();
this.dataList.Add(new KeyValuePair<string, string>("Year", item.Year));
this.dataList.Add(new KeyValuePair<string, string>("Manufacturer", item.Manufacturer));
this.dataList.Add(new KeyValuePair<string, string>("Name", item.Name));
this.mainGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
this.mainGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
for (int i = 0; i < dataList.Count; i++)
{
this.mainGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
var keyLabel = new Label()
{
Text = dataList[i].Key
};
var valueLabel = new Label()
{
Text = dataList[i].Value
};
this.mainGrid.Children.Add(keyLabel, 0, i);
this.mainGrid.Children.Add(valueLabel, 1, i);
}
}
}
In my real project the two cell types differ more, but you should get the idea.
ListViewGroupHeader.cs:
public class ListViewGroupHeader : Label
{
}
MyDataTemplateSelector.cs:
public class MyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate CarTemplate { get; set; }
public DataTemplate GeneralTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if (item is CarInfo)
{
return this.CarTemplate;
}
else if (item is Category)
{
return this.GeneralTemplate;
}
return new DataTemplate();
}
}
MainPage.xaml:
<?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:renderer="clr-namespace:TestGroupedListView.CustomRenderer;assembly=TestGroupedListView"
xmlns:customViews="clr-namespace:TestGroupedListView.CustomView;assembly=TestGroupedListView"
xmlns:local="clr-namespace:TestGroupedListView.Helper;assembly=TestGroupedListView"
x:Class="TestGroupedListView.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="carTemplate">
<customViews:InfoItemView />
</DataTemplate>
<DataTemplate x:Key="generalTemplate">
<customViews:GeneralView />
</DataTemplate>
<local:MyDataTemplateSelector x:Key="myDataTemplateSelector" CarTemplate="{StaticResource carTemplate}" GeneralTemplate="{StaticResource generalTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<ListView x:Name="mainList"
SeparatorColor="{StaticResource PrimaryLight}"
HasUnevenRows="True"
GroupDisplayBinding="{Binding Key}"
IsGroupingEnabled="True"
CachingStrategy="RecycleElement"
ItemTemplate="{StaticResource myDataTemplateSelector}">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<renderer:ListViewGroupHeader Text="{Binding Key}" Style="{StaticResource labelHeader}" HeightRequest="40" />
</DataTemplate>
</ListView.GroupHeaderTemplate>
<!--
<ListView.ItemTemplate>
<DataTemplate>
<customViews:InfoItemView />
</DataTemplate>
</ListView.ItemTemplate>
-->
</ListView>
</ContentPage>
That's the idea. Would that work? Or should I look for another solution? I didn't find something in Google about the exact same problem ... (perhaps the wrong keywords?)