Home > OS >  Trigger property changed in one ViewModel from another ViewModel (MessagingCenter)
Trigger property changed in one ViewModel from another ViewModel (MessagingCenter)

Time:03-24

I am trying to inform one ViewModel about changes in another ViewModel. For this matter I have tried to use MessagingCenter. However it looks like it does not work for some reason. Could someone drop some light, why I am not able to change colour of Label in one View from another?

HomeViewModel.cs:

using System;
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace MessagingCenterApp.ViewModels
{
  public class HomeViewModel : BaseViewModel
  {
    public HomeViewModel()
    {
      Title = "Home";

      MessagingCenter.Subscribe<Object, Color>(this, "gaugeColor", (sender, arg) =>
      {
        this._gaugeColor = arg;
      });

      this.GaugeColor = Color.White;
    }

    private Color _gaugeColor;
    public Color GaugeColor
    {
      get => _gaugeColor;
      set
      {
        _gaugeColor = value;
        OnPropertyChanged();
      }
    }
  }
}

SettingsViewModel.cs:

using MessagingCenterApp.Models;
using MessagingCenterApp.Views;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace MessagingCenterApp.ViewModels
{
  public class SettingsViewModel : BaseViewModel
  {
    public SettingsViewModel()
    {
      Title = "Settings";
      MessagingCenter.Send<Object, Color>(this, "gaugeColor", Color.FromHex(this.GaugeColor));

    }

    private string _gaugeColor;
    public string GaugeColor
    {
      get => Preferences.Get("GaugeColor", "#17805d");
      set
      {
        Preferences.Set("GaugeColor", value);
        this.OnPropertyChanged();
      }
    }
  }
}

EDIT:

Implementation without separate button:

SettingsViewModel.cs:

using MessagingCenterApp.Models;
using MessagingCenterApp.Views;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace MessagingCenterApp.ViewModels
{
  public class SettingsViewModel : BaseViewModel
  {
    public ICommand changeCommand { get; set; }
    public SettingsViewModel()
    {
      Title = "Settings";

      GaugeColor = Color.Red;

      changeCommand = new Command(changeColor);
    }

    private void changeColor()
    {
      GaugeColor = Color.FromHex(this.GaugeColorS);

      MessagingCenter.Send<Object, Color>(this, "gaugeColor", GaugeColor);
    }

    private Color _gaugeColor;
    public Color GaugeColor
    {
      get => _gaugeColor;
      set
      {
        _gaugeColor = value;
        this.OnPropertyChanged();
      }
    }

    private string _gaugeColorS;
    public string GaugeColorS
    {
      get => Preferences.Get("GaugeColor", "#17805d");
      set
      {
        Preferences.Set("GaugeColor", value);
        this.changeColor();
        this.OnPropertyChanged();
      }
    }
  }
}

HomeViewModel.cs:

using System;
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace MessagingCenterApp.ViewModels
{
  public class HomeViewModel : BaseViewModel
  {
    public HomeViewModel()
    {
      Title = "Home";

      MessagingCenter.Subscribe<Object, Color>(this, "gaugeColor", (sender, arg) =>
      {

        System.Diagnostics.Debug.WriteLine("received color = "   arg);
        this.GaugeColor = arg;
      });

      this.GaugeColor = Color.Red;
    }

    private Color _gaugeColor;
    public Color GaugeColor
    {
      get => _gaugeColor;
      set
      {
        _gaugeColor = value;
        OnPropertyChanged();
      }
    }
  }
}

CodePudding user response:

If you want to send a message to another page when the property in current page is reset, you should add a event trigger (for example clicking a 'Button' in page SettingsPage. Once the button is clicked,send a message to page HomePage).

I simplified your code,and it works properly on my side. You can modify it to suit your needs.

BaseViewModel.cs

  public class BaseViewModel : INotifyPropertyChanged
  {
    public IDataStore<Item> DataStore => DependencyService.Get<IDataStore<Item>>();

    bool isBusy = false;
    public bool IsBusy
    {
      get { return isBusy; }
      set { SetProperty(ref isBusy, value); }
    }

    string title = string.Empty;
    public string Title
    {
      get { return title; }
      set { SetProperty(ref title, value); }
    }

       public  bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(storage, value))
                return false;

            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }

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

        public event PropertyChangedEventHandler PropertyChanged;
    }

HomeViewModel.cs

public class HomeViewModel : BaseViewModel
  {

        private Color _gaugeColor;
        public Color GaugeColor
        {
            set { SetProperty(ref _gaugeColor, value); }

            get { return _gaugeColor; }
        }


        public HomeViewModel()
    {
      Title = "Home";

      MessagingCenter.Subscribe<Object, Color>(this, "gaugeColor", (sender, arg) =>
      {

          System.Diagnostics.Debug.WriteLine("received color = "   arg);
           this.GaugeColor = arg;
      });

      this.GaugeColor = Color.Red;
    }

    }

SettingsViewModel.cs

 public class SettingsViewModel : BaseViewModel
  {
    public ICommand changeCommand { get; set; }
    public SettingsViewModel()
    {
      Title = "Settings";

      GaugeColor = Color.Red;

      changeCommand = new Command(changeColor);
    }

        private void changeColor(object obj)
        {
            GaugeColor = Color.Yellow;

            MessagingCenter.Send<Object, Color>(this, "gaugeColor", GaugeColor);
        }

        Color  _gaugeColor;
        public Color GaugeColor
        {
            set { SetProperty(ref _gaugeColor, value); }

            get { return _gaugeColor; }
        }

    }

SettingsPage.xaml (here I add Button )

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MessagingCenterApp.Views.SettingsPage"
             Title="{Binding Title}"
             xmlns:vm="clr-namespace:MessagingCenterApp.ViewModels" 
             Background="#121212"
             x:Name="BrowseItemsPage">

  <ContentPage.BindingContext>
    <vm:SettingsViewModel />
  </ContentPage.BindingContext>

  <ContentPage.Resources>
    <ResourceDictionary>
      <Color x:Key="Accent">#0d1117</Color>
    </ResourceDictionary>
  </ContentPage.Resources>


    <StackLayout Orientation="Vertical">
      <Label Text="Gauge color:"></Label>
      <Entry Text="{Binding GaugeColor}" Keyboard="Numeric"></Entry>


      <Button  Text="change color"  Command="{Binding changeCommand}"/>
    </StackLayout>

</ContentPage>

Update:

I mean when I type or select new colour on Settings page, I would like to see new colour already

For this, you can add MessagingCenter.Send in SettingsViewModel as follows:

    Color  _gaugeColor;
    public Color GaugeColor
    {
        set { 
            
            SetProperty(ref _gaugeColor, value);

            MessagingCenter.Send<Object, Color>(this, "gaugeColor", GaugeColor);
        }

        get { return _gaugeColor; }
    }

And if you don't want the Button, you can remove the Button in SettingsPage.xaml.

  • Related