Reputation: 741
I have been able bind a Grid (size 2x2) to an ObservableCollection (size 4) but the code has a lot of duplication has each cell is manually binded to an index of the collection.
I need to increase the size of the grit to 7x7 so I would prefer a better way to do that. I've looked at:
but dataTemplate seems to work on CollectionView not a Grid and the ContentView need binding for each Parameter which does not reduce complexity much.
Does anyone has an idea / suggestion to improve this?
TestPage
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Views.TestPage"
xmlns:viewmodels="clr-namespace:Maui.ViewModels"
x:DataType="viewmodels:TestPageViewModel"
Title="TestMap">
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Frame Grid.Row="0"
Grid.Column="0"
BackgroundColor="Blue">
<StackLayout>
<Label Text="{Binding Persons[0].Name}" />
<Label Text="{Binding Persons[0].Age}" />
<Label Text="{Binding Persons[0].Location}" />
</StackLayout>
</Frame>
<Frame Grid.Row="0"
Grid.Column="1"
BackgroundColor="Red">
<StackLayout>
<Label Text="{Binding Persons[1].Name}" />
<Label Text="{Binding Persons[1].Age}" />
<Label Text="{Binding Persons[1].Location}" />
</StackLayout>
</Frame>
<Frame Grid.Row="1"
Grid.Column="0"
BackgroundColor="Green">
<StackLayout>
<Label Text="{Binding Persons[2].Name}" />
<Label Text="{Binding Persons[2].Age}" />
<Label Text="{Binding Persons[2].Location}" />
</StackLayout>
</Frame>
<Frame Grid.Row="1"
Grid.Column="1"
BackgroundColor="Yellow">
<StackLayout>
<Label Text="{Binding Persons[3].Name}" />
<Label Text="{Binding Persons[3].Age}" />
<Label Text="{Binding Persons[3].Location}" />
</StackLayout>
</Frame>
</Grid>
</ContentPage>
TestPageViewModel
public partial class TestPageViewModel : ObservableObject
{
public ObservableCollection<Person> Persons { get; set; }
public TestPageViewModel()
{
Persons = new ObservableCollection<Person>
{
new Person { Name="John", Age=21, Location="US" },
new Person { Name="Harry", Age=45, Location="Ireland" },
new Person { Name="Robert", Age=32, Location="France" },
new Person { Name="Juan", Age=68, Location="Spain" },
};
}
}
Model
public partial class Person : ObservableObject
{
[ObservableProperty]
public string name;
[ObservableProperty]
public int age;
[ObservableProperty]
public string location;
}
Upvotes: 0
Views: 2143
Reputation: 8914
Since Grid
doesn't support binding to collections for dynamic population, neither by exposing an ItemsSource
property nor by using BindableLayout.ItemsSource
, all you're left with is using another type of layout for the collection, such as a VerticalStackLayout
, FlexibleLayout
or a CollectionView
(I recommend avoiding ListView
for this) and then using a Grid
inside of the DataTemplate
for each row.
In your case I recommend using the CollectionView, because it allows different layout styles, such as a Grid with two columns (which seems to be what you're trying to achieve based on your code):
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Views.TestPage"
xmlns:viewmodels="clr-namespace:Maui.ViewModels"
x:DataType="viewmodels:TestPageViewModel"
Title="TestMap">
<CollectionView
ItemsSource="{Binding Persons}"
ItemsLayout="VerticalGrid, 2">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Person">
<controls:PersonView Person="{Binding .}"/>
<!-- OR -->
<!--
<Frame
BackgroundColor="Green">
<StackLayout>
<Label Text="{Binding Name}" />
<Label Text="{Binding Age}" />
<Label Text="{Binding Location}" />
</StackLayout>
</Frame>
-->
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
Upvotes: 0
Reputation: 741
Thanks Jason, I followed your advice and I have created a custom view. There are still some duplicate code but it's much more manageable now and less error prone.
I'm posting my code here for future dev who might need this. Updvote if it helped you :D
PersonView
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.PersonView"
x:Name="this">
<Frame BindingContext="{x:Reference this}"
BackgroundColor="{Binding BackgroundColor}">
<StackLayout>
<Label Text="{Binding Person.Name}" />
<Label Text="{Binding Person.Age}" />
<Label Text="{Binding Person.Location}" />
</StackLayout>
</Frame>
</ContentView>
public partial class PersonView : ContentView
{
public static readonly BindableProperty PersonProperty = BindableProperty.Create(nameof(Person), typeof(Person), typeof(PersonView), new Person());
public Person Person
{
get => (Person)GetValue(PersonView.PersonProperty);
set => SetValue(PersonView.PersonProperty, value);
}
public PersonView()
{
InitializeComponent();
}
}
Test Page
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Views.TestPage"
xmlns:viewmodels="clr-namespace:Maui.ViewModels"
x:DataType="viewmodels:TestPageViewModel"
Title="TestMap"
xmlns:controls="clr-namespace:Maui.Controls">
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<controls:PersonView Person="{Binding Persons[0]}" BackgroundColor="Blue" Grid.Row="0" Grid.Column="0" />
<controls:PersonView Person="{Binding Persons[1]}" BackgroundColor="Red" Grid.Row="0" Grid.Column="1" />
<controls:PersonView Person="{Binding Persons[2]}" BackgroundColor="Green" Grid.Row="1" Grid.Column="0" />
<controls:PersonView Person="{Binding Persons[3]}" BackgroundColor="Yellow" Grid.Row="1" Grid.Column="1" />
</Grid>
</ContentPage>
TestPageViewModel
using CommunityToolkit.Mvvm.ComponentModel;
using Maui.Models;
using System.Collections.ObjectModel;
namespace Maui.ViewModels;
public partial class TestPageViewModel : ObservableObject
{
public ObservableCollection<Person> Persons { get; set; }
public TestPageViewModel()
{
Persons = new ObservableCollection<Person>
{
new Person { Name="John", Age=21, Location="US" },
new Person { Name="Harry", Age=45, Location="Ireland" },
new Person { Name="Robert", Age=32, Location="France" },
new Person { Name="Juan", Age=68, Location="Spain" },
};
}
}
Model
public partial class Person : ObservableObject
{
[ObservableProperty]
public string name;
[ObservableProperty]
public int age;
[ObservableProperty]
public string location;
}
Upvotes: 0