I have a Model class Person
, which has some string properties. In the UI, there are some Entry
fields that, when a button is pressed, does the following in a ViewModel class:
creates a Person
object with the values from the fields populating the object's properties
adds the Person
object to an ObservableList<Person>
, which itself is created in the constructor of the ViewModel class
I then try to display some of the Person
object's properties in a CollectionView
list. A list of the string values. I'd like it to look something like this:
I know that both the Person
object is being created and the ObservableList<Person>
list is being populated, as I can see the values when I step through the code.
Person.cs
namespace BindingDemo.Model
{
public partial class Person : ObservableObject
{
#region PersonObjectProperties
[ObservableProperty]
private string firstName;
[ObservableProperty]
private string lastName;
[ObservableProperty]
private string email;
[ObservableProperty]
private string password;
#endregion PersonObjectProperties
public void ClearFields()
{
FirstName = string.Empty;
LastName = string.Empty;
Email = string.Empty;
Password = string.Empty;
}
}
}
ViewModel class:
using BindingDemo.Model;
namespace BindingDemo.ViewModel
{
public partial class MainPageViewModel : ObservableObject
{
public Person Person { get; } = new Person();
[ObservableProperty]
private ObservableCollection<Person> persons;
public MainPageViewModel()
{
persons = new ObservableCollection<Person>();
}
[RelayCommand]
private void AddPersonToListAndDb()
{
// every field must have a value
if (string.IsNullOrWhiteSpace(Person.FirstName) ||
string.IsNullOrWhiteSpace(Person.LastName) ||
string.IsNullOrWhiteSpace(Person.Email) ||
string.IsNullOrWhiteSpace(Person.Password))
return;
// add the Person object to the list of Person objects
Persons.Add(Person);
// clear the fields in the UI so the user can add another person
Person.ClearFields();
// add the Person object to a database
}
}
}
View class:
<?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="BindingDemo.MainPage"
Title="Create Person"
xmlns:viewmodel="clr-namespace:BindingDemo.ViewModel"
x:DataType="viewmodel:MainPageViewModel">
<Grid RowDefinitions="Auto, Auto, Auto, Auto, Auto,*"
Margin="10">
<Entry Grid.Row ="0"
Placeholder="Enter your first name"
PlaceholderColor="DarkGoldenrod"
FontAttributes="Bold"
Text="{Binding Person.FirstName}" />
<Entry Grid.Row="1"
Placeholder="Enter your last name"
PlaceholderColor="DarkGoldenrod"
FontAttributes="Bold"
Text="{Binding Person.LastName}" />
<Entry Grid.Row="2"
Placeholder="Enter your email address"
PlaceholderColor="DarkGoldenrod"
FontAttributes="Bold"
Text="{Binding Person.Email}" />
<Entry Grid.Row="3"
Placeholder="Enter your password"
PlaceholderColor="DarkGoldenrod"
FontAttributes="Bold"
IsPassword="True"
Text="{Binding Person.Password}" />
<Button Grid.Row="4"
Margin="10"
Text="Add to Database"
FontAttributes="Bold"
HorizontalOptions="Center"
Command="{Binding AddPersonToListAndDbCommand}"/>
<CollectionView Grid.Row="5"
ItemsSource="{Binding Persons}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="5">
<HorizontalStackLayout>
<Label Text="First name:" />
<Label Text="{Binding Person.FirstName}" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Last name:" />
<Label Text="{Binding Person.LastName}" />
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label Text="Email address: " />
<Label Text="{Binding Person.Email}" />
</HorizontalStackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
The problem is most likely in the Binding, but I don't know what else to try.
When I'm debugging, I hover my mouse over over the Text={Binding Person.FirstName}
to see the value of Person.FirstName
. Well, if I hover over Person
I can drill down into the object and all the correct values are there, but when I hover over FirstName
, it gives the error children could not be evaluated
.
To illustrate that the list of Person
objects is being created, here is an image of a watch, after I've created a few objects and put them into the list:
EDIT: this is my MainPage.xaml.cs code:
using BindingDemo.ViewModel;
namespace BindingDemo
{
public partial class MainPage : ContentPage
{
public MainPage(MainPageViewModel mainPageViewModel)
{
InitializeComponent();
BindingContext = mainPageViewModel;
}
}
}
because your ItemsSource
is an IEnumerable<Person>
, each row of the CollectionView
has a BindingContext
of the current Person
object
that means the binding expression in the DataTemplate
would be relative to a Person
object, ie
Text="{Binding FirstName}"
however, because you are using compiled bindings, you also need to specify the DataType
<DataTemplate x:DataType="model:Person">
you will also need to add an xmlns
declaration for model