Skip to content
Skillpedia
APIs & servers (backend)40k · #5246

mvvm-toolkit-messenger

Lets your agent allow parts of an app to talk to each other. It uses a messaging system for decoupled communication.

What it does

This code allows different parts of your program to talk to each other without knowing each other directly. It's like a radio system where one part sends a message, and other interested parts receive it.

When to use it

  • ✓When two parts of the program need to notify each other about something (e.g., login, theme change).
  • ✓When one part needs to ask another for information and wait for a reply.
  • ✓To organize communication within specific groups, like for a particular window.

Example of what to ask your AI

“Notify me when dark mode is activated.”

Want more? Open the sections below.

How to use

The easiest way: copy the message below and paste it into Claude, ChatGPT, Cursor or any AI. It reads the skill and follows the instructions.

Skill link

https://github.com/github/awesome-copilot/blob/main/skills/mvvm-toolkit-messenger/SKILL.md

Ready-to-paste message

Use this skill to help me: https://github.com/github/awesome-copilot/blob/main/skills/mvvm-toolkit-messenger/SKILL.md Read the SKILL.md file at that link and follow its instructions for my task.
How it works

It uses a 'publisher/subscriber' pattern. One part 'publishes' a message, and other parts 'subscribe' to receive these messages. Messages can be sent with weak references so they don't prevent the program from cleaning up memory.

Installation

Easiest way: copy the skill link and paste it into Claude, ChatGPT or Cursor asking it to follow the instructions.

In the terminal (Claude Code, Codex, Cursor), in your project folder:

npx skills add github/awesome-copilot --skill mvvm-toolkit-messenger

Or download just the file:

curl -L https://raw.githubusercontent.com/github/awesome-copilot/main/skills/mvvm-toolkit-messenger/SKILL.md -o SKILL.md
Technical content (original SKILL.md)

Original text by the author, straight from GitHub.

CommunityToolkit.Mvvm Messenger

Pub/sub messaging for ViewModels (or any objects) without forcing a shared reference graph. Part of CommunityToolkit.Mvvm 8.x.

TL;DR. Default to WeakReferenceMessenger.Default. Register handlers with the (recipient, message) lambda and the static modifier so you never capture this. Inherit from ObservableRecipient and toggle IsActive at activation/deactivation to get automatic register/unregister.


When to use this skill

  • Two or more ViewModels need to react to an event (login, theme change, save, navigation) without holding references to each other
  • A ViewModel needs to ask another VM for a value (request/reply)
  • You're scoping events to a sub-system or window with channel tokens
  • Diagnosing "my handler never fires" or weak-reference recipient lifetime problems

For source generators, base classes, and commands see the mvvm-toolkit skill. For DI wiring (registering an IMessenger instance), see mvvm-toolkit-di.


Choose an implementation

Type When
WeakReferenceMessenger.Default Default. Recipients held weakly — eligible for GC even while registered. Internal trimming runs during full GCs; no manual Cleanup() needed.
StrongReferenceMessenger.Default Profiler shows the messenger is hot and allocation matters. Recipients are pinned until you Unregister. Forgetting unregistration leaks them.
Custom IMessenger instance Per-window/per-scope (e.g., one messenger per app window). Construct directly, inject via DI.

ObservableRecipient's parameterless constructor uses WeakReferenceMessenger.Default. Pass a different IMessenger to its constructor to override.


Define a message

The toolkit ships base classes; any class works.

using CommunityToolkit.Mvvm.Messaging.Messages;

// Single-payload broadcast
public sealed class LoggedInUserChangedMessage(User user)
    : ValueChangedMessage<User>(user);

// Custom shape (records are great for this)
public sealed record ThemeChangedMessage(AppTheme NewTheme);

// Empty signal
public sealed record RefreshRequestedMessage;

Register a recipient

Lambda style (recommended)

WeakReferenceMessenger.Default.Register<MyViewModel, ThemeChangedMessage>(
    this,
    static (recipient, message) => recipient.OnThemeChanged(message.NewTheme));

The static modifier prevents accidental closure allocation and keeps this out of the lambda — use the recipient parameter instead.

IRecipient<TMessage> interface style

public sealed class MyViewModel : ObservableRecipient,
    IRecipient<ThemeChangedMessage>,
    IRecipient<RefreshRequestedMessage>
{
    public void Receive(ThemeChangedMessage message) { /* ... */ }
    public void Receive(RefreshRequestedMessage message) { /* ... */ }
}

ObservableRecipient.OnActivated() calls Messenger.RegisterAll(this), which subscribes every IRecipient<T> interface implemented by the type. If you're not using ObservableRecipient, register manually:

WeakReferenceMessenger.Default.RegisterAll(this);

Send a message

WeakReferenceMessenger.Default.Send(new ThemeChangedMessage(AppTheme.Dark));

// Empty payloads use the parameterless overload:
WeakReferenceMessenger.Default.Send<RefreshRequestedMessage>();

Channels (tokens)

Scope messages to a sub-system or window with a token (any equatable value — int, string, Guid):

const int LeftPaneChannel = 1;

WeakReferenceMessenger.Default.Register<MyViewModel, RefreshRequestedMessage, int>(
    this, LeftPaneChannel,
    static (r, _) => r.RefreshLeft());

WeakReferenceMessenger.Default.Send(new RefreshRequestedMessage(), LeftPaneChannel);

Messages sent without a token use the default shared channel — they are not delivered to channel-scoped recipients.


Request / reply

For ask-style scenarios where a recipient provides a value back to the sender, use the RequestMessage<T> family.

Sync request

public sealed class CurrentUserRequest : RequestMessage<User> { }

WeakReferenceMessenger.Default.Register<UserService, CurrentUserRequest>(
    this,
    static (r, m) => m.Reply(r.CurrentUser));

User user = WeakReferenceMessenger.Default.Send<CurrentUserRequest>();

The implicit conversion from CurrentUserRequest to User throws if no

…

FAQ

What's the difference between 'WeakReferenceMessenger' and 'StrongReferenceMessenger'?

'WeakReferenceMessenger' is the default and allows the program to release the memory of message receivers if they are no longer needed. 'StrongReferenceMessenger' keeps the reference active until explicitly removed, which can cause memory leaks if not managed properly.

What is 'ObservableRecipient'?

It's a class that simplifies automatically registering and unregistering for messages when a component is active or inactive, making message lifecycle management easier.

How do I send a message?

You use the Messenger's 'Send' method, passing the message you want to send. For example: `WeakReferenceMessenger.Default.Send(new MyMessage());`

Details

Original description by the author

CommunityToolkit.Mvvm Messenger pub/sub for decoupled communication between ViewModels (or any objects). Covers WeakReferenceMessenger vs StrongReferenceMessenger, IRecipient<TMessage>, RequestMessage<T> / AsyncRequestMessage<T> / CollectionRequestMessage<T>, ValueChangedMessage<T>, channels (tokens), and the ObservableRecipient activation lifecycle. Use across WPF, WinUI 3, .NET MAUI, Uno, and Av

repo: github/awesome-copilot

Popularity

GitHub stars
40k
Ranking position
#5246
License
MIT
Updated
10/15/2026

Is this skill useful?

0

Sign in to vote.

Comments

  • …

More skills from this repository

Similar skills