Search code examples
mauisyncfusion

Maui Gui does not react to changed values in the viewmodel


I got a List in the ViewModel and the UI is not reacting to changes of property values. It is about the button (SfChip) background colour, which is not updated. (a selected button is red, all others are white).

Here is the XAML:

<syncfusion:SfChipGroup ItemsSource="{Binding PageButtonList}" >
    <syncfusion:SfChipGroup.ItemTemplate>
        <DataTemplate x:DataType="models:PageModel">
            <syncfusion:SfChip Text="{Binding Title}"
                               Background="{Binding IsCurrentPage, Converter={StaticResource BoolToColorConverter}}"
                               TextColor="Black" />
        </DataTemplate>
    </syncfusion:SfChipGroup.ItemTemplate>
</syncfusion:SfChipGroup>

The Background color is bound to a bool using a BoolToColorConverter:

<toolkit:BoolToObjectConverter x:Key="BoolToColorConverter" 
               TrueObject="{x:Static Colors.Red}"
               FalseObject="{x:Static Colors.White}"/>

The boolean value of IsCurrentPage is initially true, so the colour is red. Every time I add a PageModel element to the PageButtonList, I set all IsCurrentPage properties to false, only the last one is true.

private void UpdatePageButtonList()
{
    foreach (var pageModel in PageButtonList)
    {
        pageModel.IsCurrentPage = pageModel.Id == CurrentPageNumber;
    }
}

Even if list entries are gradually added and more SfChip's are displayed, they all remain on red. I can set a breakpoint in the method UpdatePageButtonList() and see that IsCurrentPage is set to false for the first list entries.

Probably the UI does not recognise the change of the property or the converter does not work.

Why does the SfChip's background colour not turn white?

public class PageModel
{
    public int Id { get; set; }

    public string Title => (Id + 1).ToString();
        
    public bool IsCurrentPage { get; set; }

    public PageModel(int id)
    {
        Id = id;
        IsCurrentPage = true;
    }
}

Solution

  • The problem is that your IsCurrentPage property doesn't invoke a PropertyChanged event and thus the UI doesn't get notified about the change.

    In MAUI, all properties that can change their values and are used in binding expressions that need to update the UI when the value is updated, need to be observable. This applies to ViewModels and Models alike.

    When you use a Model class in your ViewModel, e.g. in a List<PageModel> or ObservableCollection<PageModel> and you bind to a property of said PageModel, e.g. IsCurrentPage, then this property must be observable.

    Make sure that your PageModel class implements the INotifyPropertyChanged interface, either by doing so manually or by using the Source Generators from the MVVM Community Toolkit:

    Without Source Generators

    public class PageModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;  
    
        private void OnPropertyChanged([CallerMemberName] string propertyName = "")  
        {  
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }   
    
        public int Id { get; set; }
    
        public string Title => (Id + 1).ToString();
        
        private bool _isCurrentPage;
        public bool IsCurrentPage
        { 
            get => _isCurrentPage;
            set
            {
                if(value == _isCurrentPage) return;
                _isCurrentPage = value;
                OnPropertyChanged();
            }
        }
    
        public PageModel(int id)
        {
            Id = id;
            IsCurrentPage = true;
        }
    }
    

    With Source Generators

    public partial class PageModel : ObservableObject
    {
        public int Id { get; set; }
    
        public string Title => (Id + 1).ToString();
        
        [ObservableProperty]
        private bool _isCurrentPage; //becomes IsCurrentPage public property
    
        public PageModel(int id)
        {
            Id = id;
            IsCurrentPage = true;
        }
    }