I’m working on a WPF MVVM application that has grown beyond just one or two User Controls. I now have multiple ViewModels that need to react to each other’s actions (e.g., selecting a customer in one list should update a detail view and a related properties list).
I’m trying to keep the architecture clean, testable, and maintainable — without creating tight coupling between ViewModels.
From my research, I see several possible approaches:
1. Parent ViewModel as coordinator
A single “parent” ViewModel owns the child ViewModels and wires them together via events or method calls:
public class MainViewModel
{
public CustomersListViewModel CustomersList { get; }
public CustomerDetailViewModel CustomerDetail { get; }
public MainViewModel(CustomersListViewModel list, CustomerDetailViewModel detail)
{
CustomersList = list;
CustomerDetail = detail;
CustomersList.CustomerSelected += async id => await CustomerDetail.LoadAsync(id);
}
}
Pros: Simple, explicit, easy to test
Cons: The parent can become a “God object” if too much logic is centralized
2. Messenger / EventAggregator
Using IMessenger from CommunityToolkit.Mvvm or a similar event aggregator to decouple ViewModels:
public record CustomerSelectedMessage(Guid CustomerId);
public class CustomersListViewModel
{
private readonly IMessenger _messenger;
public CustomersListViewModel(IMessenger messenger) => _messenger = messenger;
public void SelectCustomer(Guid id) =>
_messenger.Send(new CustomerSelectedMessage(id));
}
public class CustomerDetailViewModel : IRecipient<CustomerSelectedMessage>
{
public CustomerDetailViewModel(IMessenger messenger) => messenger.Register(this);
public void Receive(CustomerSelectedMessage msg) =>
LoadAsync(msg.CustomerId);
}
Pros: No direct references, easy to add more listeners later
Cons: Event flow can be harder to trace/debug
3. Shared service for state
A service holds shared state (e.g., currently selected customer):
public class SelectionService
{
public Guid? SelectedCustomerId { get; set; }
}
public class CustomersListViewModel
{
private readonly SelectionService _selection;
public CustomersListViewModel(SelectionService selection) => _selection = selection;
public void SelectCustomer(Guid id) => _selection.SelectedCustomerId = id;
}
public class CustomerDetailViewModel
{
private readonly SelectionService _selection;
public CustomerDetailViewModel(SelectionService selection) => _selection = selection;
public async Task LoadFromSelectionAsync()
{
if (_selection.SelectedCustomerId is Guid id)
await LoadAsync(id);
}
}
Pros: Simple, especially for persistent/global state
Cons: Not event-driven — may require polling or additional events
Question:
For a medium-to-large WPF MVVM application, what is the most common and best practice approach for ViewModel-to-ViewModel communication?
Should I stick to one pattern or mix them depending on the scenario?