This issue is meant to be used to track all the feature requests and discussions regarding API changes or other tweaks for the Microsoft.Toolkit.Mvvm package, which was introduced with #3230 and recently announced. 馃帀
Find initial docs and samples here at https://aka.ms/mvvmtoolkit.
If you want more info on the package, you're welcome to browse our samples and preview documentation (here), watch the initial presentation by @michael-hawker and I (here) or watch Michael's presentation during the UNO Conf (here).
Will be updating this list with all the current (work in progress) changes that are being included into a PR.
ObservableValidator.GetErrors methodWeakReferenceMessenger on .NET Standard 2.0Ioc class (see docs)ObservableObject.OnPropertyChanged(PropertyChangedEventArgs) overloadObservableObject.OnPropertyChanging(PropertyChangingEventArgs) overloadOnPropertyChanged and OnPropertyChanging overloads with a string param are no longer virtualIAsyncRelayCommand.CanBeCanceled propertyIAsyncRelayCommand propertiesObservableValidator.HasErrors propertyTrySetProperty methods to ObservableValidatorObservableValidator class for INotifyDataErrorInfoSetPropertyAndNotifyOnCompletion signature (7-14x faster, 60% memory usage reduction)Expression<Func<T>> argsSetProperty<T, TModel> overload to allow stateless delegates and better performanceAsyncRelayCommand types (and interfaces)Ioc class and Microsoft.Extensions.DependencyInjection dependencyWeakReferenceMessenger typeMessenger to StrongReferenceMessengerStrongReferenceMessengerIMessenger.Register signature to remove closures (35% memory usage reduction, no this captured)IMessenger.Cleanup API to help with extensibilityObservableRecipient to WeakReferenceMessengerPosting an update here regarding the changes to the SetPropertyAndNotifyOnCompletion method.
The signature now has a custom delegate instead of the ref + Expression<Func<T>> pair, which gives us:
Code changes:
private Task myTask;
public Task MyTask
{
// BEFORE
set => SetPropertyAndNotifyOnCompletion(ref myTask, () => myTask, value);
// AFTER
set => SetPropertyAndNotifyOnCompletion(() => ref myTask, value);
}
And some benchmarks:

Hey @nanney54, let's continue here from your original comment in the issue for the Preview1.
I've come up with a solution I liked to add cancellation support to the async commands, so this new feature should be available in the next preview of the MVVM Toolkit, and of course you can also try it out already through the CI preview build 馃槃
The changes were added in https://github.com/windows-toolkit/WindowsCommunityToolkit/pull/3429/commits/06cc52062cf2b7e7555da43dd83c96f5a3dbadca, here's the new IAsyncRelayCommand interface:
/// <summary>
/// An interface expanding <see cref="IRelayCommand"/> to support asynchronous operations.
/// </summary>
public interface IAsyncRelayCommand : IRelayCommand, INotifyPropertyChanged
{
/// <summary>
/// Gets the last scheduled <see cref="Task"/>, if available.
/// This property notifies a change when the <see cref="Task"/> completes.
/// </summary>
Task? ExecutionTask { get; }
/// <summary>
/// Gets a value indicating whether running operations for this command can be canceled.
/// </summary>
bool CanBeCanceled { get; }
/// <summary>
/// Gets a value indicating whether a cancelation request has been issued for the current operation.
/// </summary>
bool IsCancellationRequested { get; }
/// <summary>
/// Gets a value indicating whether the command currently has a pending operation being executed.
/// </summary>
bool IsRunning { get; }
/// <summary>
/// Provides a more specific version of <see cref="System.Windows.Input.ICommand.Execute"/>,
/// also returning the <see cref="Task"/> representing the async operation being executed.
/// </summary>
/// <param name="parameter">The input parameter.</param>
/// <returns>The <see cref="Task"/> representing the async operation being executed.</returns>
Task ExecuteAsync(object? parameter);
/// <summary>
/// Communicates a request for cancelation.
/// </summary>
/// <remarks>
/// If the underlying command is not running, or if it does not support cancelation, this method will perform no action.
/// Note that even with a successful cancelation, the completion of the current operation might not be immediate.
/// </remarks>
void Cancel();
}
Both AsyncRelayCommand and AsyncRelayCommand<T> now include additional constructors taking a Func<CancellationToken, Task> delegate, so that you can also wrap asynchronous methods using the token parameter to monitor whether a cancellation has been requested. The commands will take of creating and handling those tokens, so you'll just need to use that Cancel method if you want to request a cancellation, and that request will be relayed to the wrapped method through that token. If you just wrap a method taking no token, the behavior is unchanged compared to commands today.
Here's a small example:
async Task<string> FooAsync(CancellationToken token)
{
await Task.Delay(10000, token); // Delay with the token
return "Hello world";
}
// Create a new async command wrapping the async method with
// the token parameter. The C# compiler will automatically pick the
// right constructor depending on the signature of the method we
// are using here (depending on whether it takes a token or not).
var command = new AsyncRelayCommand(FooAsync);
// Use the command as usual...
Let us know what you think! 馃槉
It was fast ! did you have a git stash ready on your PC before my post ? 馃榿
This rewrite of MvvmLight sounds like an excellent idea! We've been left with no official .NET Core support for quite some time. And IMO, this should have been part of the .NET Framework from the start. WPF/UPF development has been a mess because many required bricks were missing and everybody was hacking around it on their own.
If you rethink a minimalistic support, however, I'd like to question the Messaging feature itself. In my mind, let's say I want a "Notifications" section in my app, I would create a Singleton class that manages those communications in a strongly-typed way. Are there cases where using Messaging is actually a recommended design? Please help me understand.
Another thing that frustrated me with WPF is that lots of required basic bricks were missing and I had to search the web for hacks and solutions all the time until I built a basic library of fundamental tools that should have been in the framework.
PushBinding, Int32Extension, MethodBinding and perhaps a few other classes should be part of the fundamental framework IMO. With the idea of keeping it basic.
Hey @mysteryx93 - glad to see your enthusiasm for this project! 馃槉
This library is indeed positioned to help fill those gaps you mentioned in the BCL, as well as providing basic build blocks for people using MVVM. Since we're going full .NET Standard here, the issue of supporting different runtimes is also indirectly solved by this approach.
"In my mind, let's say I want a "Notifications" section in my app, I would create a Singleton class that manages those communications in a strongly-typed way."
That's... Exactly what the messenger is 馃槃
We're in the process of writing down full docs for all the existing APIs, in the meantime you can check out the preview docs and the sample app at https://aka.ms/mvvmtoolkit.
As for the other components you mentioned, we don't currently plan to include things like that, because the package itself is completely platform agnostic. For instance, that method binding you mentioned for WPF is effectively replaced by just x:Bind to functions on UWP. Or, you could just create a command to wrap the target method, on whatever framework you're using.
Because of that, we will not include any framework-specific APIs in this package, but you're free to integrate it into your own codebase by adding your custom extensions where you feel the need 馃憤
That's... Exactly what the messenger is 馃槃
My point is... IMO that's a very niche usage that I haven't found a use for yet, and I don't see what it has to do with MVVM either. Perhaps some application can use it, but why is it a core MVVM feature?
We're in the process of writing down full docs for all the existing APIs, in the meantime you can check out the preview docs and the sample app at aka.ms/mvvmtoolkit.
WOW! MVVM Docs!?? That would be a nice supplement!!
(in the meantime I found out that there is some PRISM doc that can serve to understand MvvmLight)
btw the link you posted is broken
Mmh not sure why that link wasn't working, I've fixed it now (also, it was in the first post in this issue too) 馃槃
My point is... IMO that's a very niche usage that I haven't found a use for yet, and I don't see what it has to do with MVVM either. Perhaps some application can use it, but why is it a core MVVM feature?
You can argue it's not a part of MVVM per se, yes. But it's still a useful feature to have, especially since it's fully self-contained in the library, so we can include it without having to pull other dependencies in, so there's no cost in doing that. Also, MvvmLight offered that too, so it only seemed natural to include a migration path for those users as well. There aren't other exactly equivalent services available right now (not that I'm aware of), so we figured it'd be a good idea to provide an implementation ready to use out of the box for those that are interested. If you don't need it, you can just not use it. All the components in the MVVM Toolkit are completely modular, so you're free to pick up and choose what parts to use without being forced into any of them 馃憤
I'm also curious about one thing. MvvmLight had a special RelayCommand for WPF otherwise the default one wouldn't work correctly, and this ugly work-around was the only solution they found.
How would this situation be handled by this framework? I think the problem had to do with command enabled/disabled status being properly updated on the UI.
So, I've investigated that a bit and also looked at the source for RelayCommand in MvvmLight.
I don't think it's possible to offer proper support for WPF in the MVVM Toolkit, no. To be honest, I would argue the solution in MvvmLight isn't ideal either, but I can understand why they did that - it seems the problem is with WPF itself.
Apparently controls in WPF rely on the CommandManager class to raise events to notify controls about the CanExecute change, so that they can update their visual style. For instance, this is not needed at all on frameworks such as UWP (WinUI in general), as each control just does the necessary UI updates from the handler it adds to the command directly.
The issue with having a reference to that CommandManager directly within the command is that this forces the entire backend to be dependent on a specific UI framework, so it basically single handedly breaks the whole modularization in your codebase. Sure, you could work around this with a multi-targeted project and with compiler directives specific for WPF, but again that'd also need the actual MVVM library to multi-target, so that the resolved RelayCommand object on WPF would be the one with framework support. That's the approach that MvvmLight took, but not the one we're using it in the MVVM Toolkit.
This seems to just be a fundamental issue with how WPF is structured, and I'm not seeing much we can do on our side, with the current architecture consisting in the whole package to be completely framework agnostic (.NET Standard).
In theory, if you're absolutely just in need to work on WPF, you could write your own WPF-specific commands by just wrapping the ones in the MVVM Toolkit (I made an example here for RelayCommand), then multi-targeting in your backend project and using this one in the WPF target. That'd potentially be verbose, and would be less fun to maintain, but that'd work. Unfortunately I don't see us adding anything to the MVVM Toolkit per se to specifically support this scenario in WPF though 馃槙
cc. @michael-hawker
There are various things that are frustrating about WPF, like fundamentally-needed features that aren't there and require every user to hack around them -- meaning a SHARP learning curve for every new dev until they develop their own toolkit. After that it goes a lot smoother once the basics are working.
This is just one example of things that are broken or missing in WPF. If you can't solve it, then a work-around should be clearly documented.
I wish WPF was carefully designed like ASP.NET Core! Which is a breeze to use in comparison.
Btw it's MUCH easier to learn Dependency Injection using ASP.NET Core than using WPF!!
@Sergio0694 do you know if there was an open WPF issue open on their repo about this scenario? I feel like this is one of those cases where it'd be great if the platform could be improved based on what we've learned moving forward. If anything, it'd at least be good to have something to point to.
I've searched for WPF or command related issues in MvvmLight but couldn't find anything specifically about this behavior in the RelayCommand classes unfortunately. Also tried git blame to check the commits that added those WPF-specific lines, but they had no issue linked to them. I guess we could ask Laurent if he remembers the conversation/scenario that specifically led to him implementing the feature that way. From here I'd say this definitely looks like a shortcoming on the WPF end, with MvvmLight that just did the best it could to make it work properly for devs that didn't mind keeping their MVVM backend in the same project 馃
Opened a new (draft) PR with changes for the MVVM Toolkit Preview 4 (or final release, not sure), it's #3527.
I've updated the first post in this issue with a link to that, and I'll be reporting all new changes there as well so that it'll be easy to check the progress of the new PR, as we did for the Preview 3. Feel free to leave any commends or feedbacks in this issue! 馃槃
Will this support MVVM pattern for Blazor? Doing a bit of research, there are quite a few challenges to implement real MVVM with Blazor. Changes notification is a big one; lack of ICommand support is another, then no real databinding.
@mysteryx93
I tried it and this library works just fine with MVVM and Blazor.
This issue contains an example
https://github.com/dotnet/aspnetcore/issues/26677
Reopening this to track changes for the next preview that are being done in https://github.com/windows-toolkit/WindowsCommunityToolkit/pull/3562 馃殌
I think you should reconsider the decision to have WeakReferenceMessenger.Default auto magically injected through the ObservableRecipient default constructor.
It seems like a convenience for users which want to use WeakReferenceMessenger.Default everywhere. But for people which are working on more complex application, which will not use WeakReferenceMessenger.Default, it is trap, as it hides the choice of messenger you are doing when you inherit from ObservableRecipient.
We experienced in my team similar issues using MVVMLight which had a quite similar building through a default constructor passing null and then in the Messenger getter it would default to Messenger.Default.
It happened quite often to have developers inheriting from ViewModelBase forgetting to pass the correct Messenger that we ended up implementing a DummyMessenger throwing for all access. It replaced Messenger.Default so that we were sure that the correct Messenger was used or the developer will immediately see at run time.
By having only one constructor for ObservableRecipient in which developers will have to specify IMessenger you apply the principle of least surprise and avoid any problem. So, I would not have any default!
If we continue the discussion even further, then why not StrongReferenceMessenger? Especially, when I read that you recommend "It is still good practice to unsubscribe them though, to improve performances" while using WeakReferenceMessenger.Default. I want to make clear that I don't recommend this neither, I definitely would like that there is no default, again so that developers have no surprise.
Following on from the comment by @laurentkempe, is it possible to have the best of both worlds by having a single constructor with a default value for the parameter?
protected ObservableRecipient(IMessenger messenger = WeakReferenceMessenger.Default)
{
...
}
If I was concerned about developers in a team calling the constructor and not passing a specific messenger, I'd add an analyzer to the project to check for this.
Having the StrongReferenceMessenger forces users to do more work for a benefit they may not see or be able to measure.
Defaulting to using WeakReferenceMessenger.Default means developers get something that works with minimal effort. This also makes migrating from other libraries easier.
If the developer wants to improve performance they can unsubscribe from messages.
If they then want to optimize performance even further they can change to use the Strong Reference version but they have to do more work for those benefits.
@mrlacey Unfortunately that is not a valid C# signature, as values for default parameters need to be compile-time constants.
In this case WeakReferenceMessenger.Default is just a statically initialized property, so that doesn't qualify 馃槦
I agree that using the WeakReferenceMessenger type as the default is the right choice though (as much as I like the StrongReferenceMessenger too, for obvious reasons 馃槃). While that is much faster and more efficient, it does require more attention to use and could result quite tricky for developers migrating an app from eg. MvvmLight, which uses weak references to track message recipients. Developers are still able to manually opt-in into the faster one assuming they'll take the time to make sure it's properly setup - making that a deliberate decision reduces the risk of unwanted surprises (ie. sneaky memory leaks).
is it possible to have the best of both worlds by having a single constructor with a default value for the parameter?
@mrlacey Even if that would work, and it doesn't because WeakReferenceMessenger.Default is not compile time constant (oh I see now @Sergio0694 replied to it also), it is not a great idea to have those hidden defaults in a framework like MVVM toolkit. I feel the same about WeakReferenceMessenger.Default and StrongReferenceMessenger.Default, why providing a Default, such a singleton could be implemented in user's solution in a couple of minutes. where it is needed.
I agree that using the
WeakReferenceMessengertype as the default is the right choice though
My point @Sergio0694 was not about WeakReferenceMessenger.Default or StrongReferenceMessenger.Default I would like to put emphasis on the choices you are making for your users! I don't think it is the role of a toolkit like MVVM Toolkit to make those default choices. Your point to ease developer's life to port from MVVMLight is a valid point, but should we carry, in a new toolkit, the drawbacks of an older one? I was involved in MVVMLight when Laurent Bugnion started it, and I am an old user of it and still use it. I would like that some of the not-so-great design decisions from the past doesn't end up in MVVM Toolkit.
And the two, I currently see, are the default injection of WeakReferenceMessenger.Default behind the user of ObservableRecipient constructor and both WeakReferenceMessenger.Default and StrongReferenceMessenger.Default which are singletons and should not be in the toolkit cause of this doesn't lead developers to make a conscious decision.
Thanks @laurentkempe, I do like the idea of forcing the decision for those upgrading. I mean there is a benefit to using the new model; we just want to keep it simple as well for those onboarding to MVVM for the first time. Not having to worry about the lifetime of the messenger is a benefit from that standpoint.
This is also not the default case as usually ObservableObject will be used unless they want to add messaging capabilities.
In your cases of using MVVMLight in the past, would you typically have a base class setting up the messenger? If so, then having the extra code only being in one place vs. several for an upgrade path wouldn't be a big overhead. So far our migration guide seems fairly straight-forward.
@jamesmcroft you've done a lot with MVVMLight as well, thoughts?
You are welcome @michael-hawker I am trying to help!
I totally understand the point easing the onboarding of newcomers and see it has a valid point and has you said the default case is usually ObservableObject! Nvertheless for more complex applications people will be using ObservableRecipient and I those ones need to make a conscious decision about using WeakReferenceMessenger or StrongReferenceMessenger.
In your cases of using MVVMLight in the past, would you typically have a base class setting up the messenger?
Yes, we have a complex application which rely on multiple messengers and we are usingViewModelBasefrom MVVMLight as our base class. So, yes, all our ViewModels set theMessengerthrough the base constructor ofViewModelBase. We use the basic principle of dependency injection and compose our application (see one much simpler example here) from the top without relying on service locator or singletons likeWeakReferenceMessenger.Defaultwhich makes writing tests more complex.
So far our migration guide seems fairly straight-forward.
For sure as you replicate the way MVVMLight was doing it but with its drawback too!
I recently found out that this is going to be a thing and so i checked out the most recent public preview 4.
I don't really have complains and like what i see, how ever i'd like to see 2 things added, if possible:
OnChanged callback in ObservableObject.SetProperty, but i'm missing a OnChanging callback in the same method. I'm aware that i can achieve similar funcionality by subscribing to the corresponding eventhandler that comes with the class. However having it there by default would be nice.IMessenger implementations have ways to clean up any resources. I'd like to be able to clean them with something that implements IDisposable, so that it neatly integrates with existing code.Are you able to make both ObservableObject and ObservableRecipient interfaces as well? It's sometimes difficult to use these due to multiple inheritance so having IObservableObject and IObservableRecipient is much easier.
Most helpful comment
I think you should reconsider the decision to have
WeakReferenceMessenger.Defaultauto magically injected through theObservableRecipientdefault constructor.It seems like a convenience for users which want to use
WeakReferenceMessenger.Defaulteverywhere. But for people which are working on more complex application, which will not useWeakReferenceMessenger.Default, it is trap, as it hides the choice of messenger you are doing when you inherit fromObservableRecipient.We experienced in my team similar issues using MVVMLight which had a quite similar building through a default constructor passing
nulland then in the Messenger getter it would default toMessenger.Default.It happened quite often to have developers inheriting from
ViewModelBaseforgetting to pass the correctMessengerthat we ended up implementing aDummyMessengerthrowing for all access. It replacedMessenger.Defaultso that we were sure that the correctMessengerwas used or the developer will immediately see at run time.By having only one constructor for
ObservableRecipientin which developers will have to specifyIMessengeryou apply the principle of least surprise and avoid any problem. So, I would not have any default!If we continue the discussion even further, then why not
StrongReferenceMessenger? Especially, when I read that you recommend "It is still good practice to unsubscribe them though, to improve performances" while usingWeakReferenceMessenger.Default. I want to make clear that I don't recommend this neither, I definitely would like that there is no default, again so that developers have no surprise.