Trigger EventHandler of one ViewModel in another ViewModel

mrw 201 Reputation points
2022-03-26T17:31:05.223+00:00

I have achieved desired result with MessagingCenter, but I have got an information from reading Xamarin articles that MessagingCenter is not the preferred way to trigger 30+ events. Additional to that I have to unsubscribe from MessagingCenter after action has been done. I want to have Settings page where I would have 30+ settings that have to be changed across whole application in different views. How I can inject SettingsViewModel into other ViewModels in Xamarin.Forms application?

SettingsViewModel.cs:

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

      this.BoxColor = Color.Red;

      this.ChangeCommand = new Command(this.ChangeColor);
    }

    private void ChangeColor()
    {
      this.BoxColor = Color.FromHex(this.BoxColorS);

      MessagingCenter.Send<Object, Color>(this, "boxColor", this.BoxColor);
    }

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

    private string _boxColorS;
    public string BoxColorS
    {
      get => Preferences.Get("BoxColor", "#17805d");
      set
      {
        Preferences.Set("BoxColor", value);
        this.ChangeColor();
        this.OnSettingsChanged();
        this.OnPropertyChanged();
      }
    }

    public event EventHandler<SettingsChangedEventArgs> SettingsChanged;
    private void OnSettingsChanged() => this.SettingsChanged?.Invoke(this, new SettingsChangedEventArgs(this.Settings));

    public Settings Settings { get; private set; }
  }
} 

HomeViewModel.cs:

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

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

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

      this.BoxColor = Color.Red;

      this.SettingsViewModel = new SettingsViewModel();
      this.SettingsViewModel.SettingsChanged += OnSettingsChanged;
    }

    private void OnSettingsChanged(object sender, SettingsChangedEventArgs e)
    {
      throw new NotImplementedException();
    }

    private Color _boxColor;
    public Color BoxColor
    {
      get => _boxColor;
      set
      {
        _boxColor = value;
        OnPropertyChanged();
      }
    }

    private ISettingsViewModel SettingsViewModel { get; }
  }
}

Should I somehow do all in MainViewModel? I mean:

namespace MessagingCenterApp.ViewModels
{
  public class MainViewModel : BaseViewModel
  {
    public MainViewModel()
    {
      this.SettingsViewModel = new SettingsViewModel();
      this.HomeViewModel = new HomeViewModel(this.SettingsViewModel);
    }

    public SettingsViewModel SettingsViewModel { get; set; }
    public HomeViewModel HomeViewModel { get; }
  }
}

Then initialized it in AppShell? I could not get this approach working.

Important! I don't want to use any MVVM framework! Only native behaviour.

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,293 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,230 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. pugthegreat 11 Reputation points
    2022-03-27T15:38:42.447+00:00

    I suppose you have one instance of each ViewModel at runtime always created. This is not a common approach, typically ViewModels are instantiated as you navigate between ViewPages and destroyed when popped from the navigation stack.

    If you do not want to use MessagingCenter, you could try to invoke events in MainViewModel from the SettingsViewModel, and then from the MainViewModel invoke events or call methods in the HomeViewModel.

    You can use Xamarin Community Toolkit DelegateWeakEventManager and WeakEventManager so that GC cleans off the event handlers subscriptions without the need to unsubscribe them when you no longer need them.

    With Dependency Injection, typically you don't inject ViewModels, usually you inject services into the constructors.

    It is hard to understand your question without context. If you want to handle configuration changes for the whole application from the ViewModel, you can handle them directly, there is no need to pass the configuration changes to another ViewModel.

    1 person found this answer helpful.
    0 comments No comments