I am learning .NET MAUI at the moment, and I have run into an issue with data binding. I have read the section on data binding on the Microsoft docs, but I am still none the wiser of why it isn't working.
I can display the button and the second label. However as soon as I bind it to a property on the view model it does not display. If I use the Binding . it displays the MauiTestApp.ViewModel.MainViewModel. What I can glean from that is that the Data context is correct, and that I should just be able to access the property's.
Here is my View Model:
using MauiTestApp.Model;
using System.Collections.ObjectModel;
namespace MauiTestApp.ViewModel
{
public partial class MainViewModel : ObservableObject
{
public ObservableCollection<Monkey> Monkeys = new ObservableCollection<Monkey>();
public String s = "Hello world";
public MainViewModel()
{
Monkeys.Add(new Monkey("Bob", 20));
Monkeys.Add(new Monkey("Steve", 10));
Monkeys.Add(new Monkey("Joey", 14));
}
public void AddMonkey()
{
Monkeys.Add(new Monkey("Harry", 23));
}
}
}
And here is the XAML
<?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"
xmlns:local="clr-namespace:MauiTestApp"
xmlns:viewModel="clr-namespace:MauiTestApp.ViewModel"
xmlns:model="clr-namespace:MauiTestApp.Model"
x:Class="MauiTestApp.MainPage">
<ContentPage.BindingContext>
<viewModel:MainViewModel/>
</ContentPage.BindingContext>
<ScrollView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Text="Press me" Pressed="Button_Pressed"></Button>
<Label Grid.Row="1" Text="{Binding s}" TextColor="Purple" ></Label>
<Label Grid.Row="2" Text="Hello world" TextColor="Purple"></Label>
</ScrollView>
</ContentPage>
And the XAML code behind:
namespace MauiTestApp
{
public partial class MainPage : ContentPage
{
public MainViewModel viewModel;
public MainPage(MainViewModel vm)
{
InitializeComponent();
viewModel = vm;
}
private void Button_Pressed(object sender, EventArgs e)
{
viewModel.AddMonkey();
}
}
}
I'm sorry if it is a basic question, but I am completely stuck at this point.
Thanks for the help!!!
The reason you don't see any Text in the Label is because it is not a property. You need to use MVVM Toolkit to make your life easier. Read more here
So your binding in the ViewModel should look like this
[ObservableProperty]
private string s = "Hello world";
Not that it is a private field with a small s. With the ObservableProperty it is generated into a proper Property that is public and assigned using capital S. Important to always do this. Never use the private field. Always the property.
Now there are a lot of problems with your code in general so if you don't mind I will give you a possible solution to what I could translate from your code. I hope it helps.
Firstly, your complete viewmodel can look like this. Notice the Method with the [RelayCommand]
. It will take your method and make it into a bindable command. Also it follow a naming convention. So it must never end with command. To bind to it we add Command in the view. By doing this we decouple our viewmodel from all reference to MUAI references.
public partial class MonkeyViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Monkey> _monkeys;
[ObservableProperty]
private string s = "Hello world";
public MonkeyViewModel()
{
Monkeys = new()
{
new Monkey
{
Name = "Bob",
Location = 20
},
new Monkey
{
Name = "Steve",
Location = 10
},
new Monkey
{
Name = "Joey",
Location = 14
},
};
}
[RelayCommand]
public void AddMonkey()
{
var harryMonkey = new Monkey
{
Name = "Harry",
Location = 23
};
Monkeys.Add(harryMonkey);
}
}
If we look at your view. It could look like this, showing a list of moneys and your buttons.
<VerticalStackLayout>
<CollectionView EmptyView = "No data" ItemsSource="{Binding Monkeys}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:Monkey">
<Grid Padding = "10">
<Grid.RowDefinitions>
<RowDefinition Height = "*" />
<RowDefinition Height = "*" />
<RowDefinition Height = "Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Text="{Binding Name}" />
<Label Grid.Row="1" Text= "{Binding Location}" />
<BoxView
Grid.Row= "2"
BackgroundColor= "Gray"
HeightRequest= "1" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button
Command= "{Binding AddMonkeyCommand}"
Text= "Press me" />
<Label
Text= "{Binding S}"
TextColor= "Purple" />
</VerticalStackLayout>
Code Behind. Never forget to bind to your BindingContext
, either in here or like you did, in the xaml. If not your view will not bind to anything.
public partial class MainPage : ContentPage
{
private readonly MonkeyViewModel _monkeyViewModel;
public MainPage(MonkeyViewModel monkeyViewModel)
{
_monkeyViewModel = monkeyViewModel;
InitializeComponent();
BindingContext = _monkeyViewModel;
}
}