Search code examples
.net-mauicommunity-toolkit-mvvm

MAUI binding issue


I have a MAUI mobile app that uses CommunityToolkit.Mvvm. On a page, there is a ListView:

        <ListView 
            Grid.Row="0"
            ItemsSource="{Binding Features}">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="viewmodel:FeatureListItem">
                    <ViewCell>
                        <Grid
                            RowDefinitions="*,1"
                            ColumnDefinitions="10*,*">
                            <Label x:Name="txtDescription" Text="{Binding DescriptionText}" BackgroundColor="Transparent" TextColor="Black" Grid.Row="0" Grid.Column="0" />
                            <Label x:Name="txtIcon" Text="{Binding IconText}" BackgroundColor="Transparent" TextColor="Black" Style="{StaticResource ToolbarItemIconStyle}" Grid.Row="0" Grid.Column="1" />

            ...

Features is a list of FeatureListItem:

public class FeatureListItem
{
    public string DescriptionText { get; set; }
    public string IconText { get; set; }
    public IRelayCommand Command { get; set; }

    public FeatureListItem(string descriptionText, string iconText, IRelayCommand command)
    {
        DescriptionText = descriptionText;
        IconText = iconText;
        Command = command;
    }
}

In the viewmodel constructor, the Features list is initialized :

        Features = new List<FeatureListItem>
        {
            new FeatureListItem(ToggleLoginText, ToggleLoginIcon, ToggleLoginCommand),
            ...
        };

        IsLoggedIn = App.UserContext?.IsLoggedOn ?? false;
        ToggleLoginIcon = IsLoggedIn ? MaterialDesignFontHelper.Logout : MaterialDesignFontHelper.Login;
        ToggleLoginText = IsLoggedIn ? _textSignOut : _textSignIn;

Properties used in the code above:

    [ObservableProperty]
    private List<FeatureListItem> _features;

    [ObservableProperty]
    private string _toggleLoginIcon = MaterialDesignFontHelper.Login;

    private const string _textSignIn = "Sign In";
    private const string _textSignOut = "Sign Out";

    [ObservableProperty]
    private string _toggleLoginText = _textSignIn;

The problem is that when ToggleLoginText and ToggleLoginIcon are changed programmatically, txtDescription and txtIcon in the UI do not change. I guess, it is because DescriptionText and IconText in FeatureListItem are not observable. I tried to derive FeatureListItem from ObservableObject and make the properties observable:

public partial class FeatureListItem : ObservableObject
{
    [ObservableProperty]
    private string _descriptionText;
    [ObservableProperty]
    private string _iconText;

But it did not help. What am I missing?


Solution

  • As @ToomakerSteve mentioned in his comment, I was observing and changing wrong properties. As _toggleLoginIcon and _toggleLoginText are used only once to initialize FeatureListItem with its properties, no need to observe or change them. Instead, I had to change properties of FeatureListItem _descriptionText and _iconText:

                FeatureListItem loginFeature = Features.FirstOrDefault(); // Login feature is the first element in the list
    
                loginFeature.IconText = IsLoggedIn ? MaterialDesignFontHelper.Logout : MaterialDesignFontHelper.Login;
                loginFeature.DescriptionText = IsLoggedIn ? _textSignOut : _textSignIn;
    

    This way the UI is updated properly.