So I am trying to work out data binding in a ContentView with a view model. I thought this should be pretty easy since MVVM is supposed to be the thing for MAUI but maybe I am missing something. The current solution is based on Databinding issue
So I have a simple ContentView like this:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModel="clr-namespace:MyProject.ViewModels"
x:Name="view"
x:Class="MyProject.Components.ContentViewComponent">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="1" Grid.Column="1"
//This NEVER picks up the value of Title >> Why??
Text="{Binding VM.Title, Source={x:Reference view}}"
FontSize="24"
FontAttributes="Bold"/>
</Grid>
</ContentView>
And the Code-Behind for my simple ContentView:
using MyProject.ViewModels;
namespace MyProject.Components;
public partial class ContentViewComponent: ContentView
{
internal MyViewModel VM { get; set; }
public static readonly BindableProperty TProperty = BindableProperty.Create(nameof(T), typeof(string), typeof(MetricImperialDropdownConverter), string.Empty, propertyChanged : TitleChanged);
private static void TitleChanged(BindableObject bindable, object oldvalue, object newvalue)
{
//This fires and sets Title to T
((ContentViewComponent)bindable).VM.Title = (string)newvalue;
}
//I want to be able to set this when reusing the component
public string T
{
get => (string)GetValue(TProperty);
set => SetValue(TProperty, value);
}
public MetricImperialDropdownConverter()
{
VM = new MyViewModel();
InitializeComponent();
}
}
And then I have a ViewModel for that like this:
using System.ComponentModel;
namespace MyProject.ViewModels
{
public class MetricImperialDropdownConverterViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnProperyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private string _title = string.Empty;
public string Title
{
get { return _title; }
set { _title = value; OnProperyChanged(nameof(Title)); }
}
}
And then to use this and pass in a value:
<?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:components="clr-namespace:MyProject.Components"
x:Class="MyProject.Pages.SomePage"
x:Name="this">
<VerticalStackLayout BindingContext="{x:Reference this}">
//This works and sets T correctly
<components:ContentViewCompontent T="Here is my Title"/>
</VerticalStackLayout>
</ContentPage>
T
for the component is correctly set.
On setting T
, the Title
property in my ViewModel, VM
, is through the PropertyChanged
event.
But the UI is never updated with the value for Title
.
I assume it is because the UI doesn't respond to events that happen outside their own context.
But what should I do in this case?? How can I get the UI to update correctly??