user547794
user547794

Reputation: 14521

Communicating between ViewModels with Events

I am trying to communicate events from my child ViewModel back to the parent. The child viewmodel's view is a separate window that I believe I cannot pass constructor arguments to. A button on this view needs to trigger a method on the parent ViewModel.

Child ViewModel:

public ConnectViewModel(ConnectEvents connectEvents)
{
    ConnectEvents = connectEvents;
}

Parent ViewModel

public MainWindowViewModel()
{
    ConnectEvents connectEvents = new ConnectEvents();
    ConnectViewModel = new ConnectViewModel(connectEvents);
    connectEvents.ThrowEvent += ConnectToServer;
}

How can I communicate between these two? Is there a better system, or how can I allow the parents to subscribe to the child?

Upvotes: 5

Views: 6943

Answers (4)

Good Night Nerd Pride
Good Night Nerd Pride

Reputation: 8500

Here's an example the utilizes the WeakEventManager mentioned in the accepted answer.

namespace Stuff {
    using System;
    using System.Windows;

    // An "event bus" public to all view models.
    public sealed class Events {
        public static Events Instance { get; } = new Events();

        public event EventHandler<EventArgs>? SomethingHappend;

        private Events() { }

        public static void RaiseSomethingHappend(object? sender = null)
            => Instance.SomethingHappend?.Invoke(sender ?? Instance, EventArgs.Empty);
    }

    // A view model that wants to listen to one or more events.
    public class Listener {
        public Listener() {
            WeakEventManager<Events, EventArgs>.AddHandler(
                source: Events.Instance,
                eventName: nameof(Events.SomethingHappend),
                Handle);
        }

        private void Handle(object? sender, EventArgs args) { }
    }

    // A view model that raises events.
    public class Source {
        public void Foo() {
            Events.RaiseSomethingHappend();
        }
    }
}

Upvotes: 1

stukselbax
stukselbax

Reputation: 5935

You have a lot of choices. You can use custom event, you can use delegate directly, you can subscribe in your parent ViewModel to the PropertyChanged or CollectionChanged event, using either ordinary subscription, or Weak Event Pattern.

I prefer the last one because there is no need to unsubscribe from.

Upvotes: 8

Mohd Ahmed
Mohd Ahmed

Reputation: 1482

You can make your own EventAggregator

public static class DumbAggregator
{
  public static void BroadCast(string message)
  {
   if (OnMessageTransmitted != null)
       OnMessageTransmitted(message);
  }

 public static Action<string> OnMessageTransmitted;
}

Usage:

  public class MySender
  {
    public void SendMessage()
    {
      DumbAggregator.BroadCast("Hello There!");
    }
  }

public class MySubscriber
{
  public MySubscriber()
  {
   DumbAggregator.OnMessageTransmitted += OnMessageReceived;
  }

 private void OnMessageReceived(string message)
 {
  MessageBox.Show("I Received a Message! - " + message);
 }
}

and with the help of this you can communicate with your view models

Upvotes: 7

NebulaSleuth
NebulaSleuth

Reputation: 841

Communicate 'events' by using events

In your ConnectViewModel...

public ConnectViewModel(ConnectEvents connectEvents)
{
    public event EventHandler<EventArgs> SomethingHappenedEvent;

     ...

     private void DoSomething()
     {
          if (SomethingHappenedEvent != null)
          {
              SomethingHappenedEvent(this, newEventArgs());
          }
     }

     RelayComand _somethingCommand;
     public ICommand SomethingHappenedCommand
     {   
          get
          {
              if (_someethingCommand == null)
                   _somethingCommand = new RelayCommand(DoSomething)
           }
     }
}

and in your MainWindowViewModel

public MainWindowViewModel()
{
    ConnectEvents connectEvents = new ConnectEvents();
    ConnectViewModel = new ConnectViewModel(connectEvents);
    ConnectViewModel.SomethingHappenedEvent += HandleSomethingHappened;
    connectEvents.ThrowEvent += ConnectToServer;
}
private void HandleSomethingHappened(object sender, EventArgs e)
{
    // Now your mainviewmodel knows that something happened
}

Upvotes: 2

Related Questions