xamlxamarinxamarin.formsmvvmxamarin-community-toolkit

Updating a property in a viewmodel of popup doesn't update the UI


As in the title I have a problem where updating a property in a viewmodel of popup doesn't update the UI. I use popups from xamarin community toolkit. I'm using a command that does this task:

        async Task ShowPopup()
    {
        MessagingCenter.Send(AnimeGroupObservable, "AnimeGroups");
        Shell.Current.ShowPopup(new MediaListGroupsPopup());
    }

It sends a message with payload and shows popup. This is popup viewmodel:

    using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Input;
using OtakuApp.Models;
using Xamarin.Forms;

namespace OtakuApp.ViewModels
{
    class MediaListGroupsPopupViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public ObservableCollection<Group> _AnimeGroups = new ObservableCollection<Group>();
        public ObservableCollection<Group> AnimeGroups
        {
            get => _AnimeGroups;
            set
            {
                if (_AnimeGroups == value)
                    return;

                _AnimeGroups = value;
                OnPropertyChanged();
            }
        }

        public String _label;

        public String label
        {
            get => _label;
            set
            {
                if (value == _label)
                    return;

                _label = value;
                OnPropertyChanged();
            }
        }


        public MediaListGroupsPopupViewModel()
        {
            MessagingCenter.Subscribe<ObservableCollection<Group>>(this, "AnimeGroups", (AnimeGroupObservable) =>
            {
                Console.WriteLine(AnimeGroupObservable[0].Name);
                label = AnimeGroupObservable[1].Name;

                MessagingCenter.Unsubscribe<ObservableCollection<Group>>(this, "AnimeGroups");
            });
        }
    }
}

I'm planning on having a small collection view of labels to select from. But right now I'm struggling to update one label just for testing purposes, so you can imagine that I've tried collection view and it didn't work. Setting _label to something manually in the code shows that binding works. It's just not updating for some reason.

Popup xaml file:

    <?xml version="1.0" encoding="utf-8" ?>
<xct:Popup
    x:Class="OtakuApp.Popups.MediaListGroupsPopup"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
    Size="300,300">
    <StackLayout>
        <Label Text="{Binding label}" />
    </StackLayout>

</xct:Popup>

So right now I have two problems:

  1. Label doesn't update. It's binded to a property that has INotifyPropertyChanged
  2. Weirdly this subscription happens only the second time (and after that too, just not the first time) I open up a popup. Is this because it's in the constructor? If yes, what's the correct way to deal with it?

Also a small question - I have unsubscribe at the end of subscription. When I didn't have it and I printed out AnimeGroupObservable[0].Name, the first time it was printed one time, the second time I open up the popup two times etc. Is the unsubscribe at the end the correct way to fix this?


Solution

  • since you are passing a single parameter to a single page, using the constructor would be much simpler than MessagingCenter (which is great, but overkill for this scenario)

    when creating the page, pass the parameter in the constructor

    Shell.Current.ShowPopup(new MediaListGroupsPopup(AnimeGroupObservable));
    

    then modify the page constructor to accept the parameter

    public MediaListGroupsPopup(ObservableCollection<Group> groups)
    {
      // you did't show how you create your VM, but I assume it's something like this
      this.BindingContext = new MediaListGroupsPopupViewModel(groups);
    }
    

    then modify your VM constructor

    public MediaListGroupsPopupViewModel(ObservableCollection<Group> groups)
    {
      label = groups[1].Name;
    }
    

    if you really are only using a single string value, you could just pass that instead of the entire ObservableCollection