CommunityToolkit / dotnet

.NET Community Toolkit is a collection of helpers and APIs that work for all .NET developers and are agnostic of any specific UI platform. The toolkit is maintained and published by Microsoft, and part of the .NET Foundation.
https://docs.microsoft.com/dotnet/communitytoolkit/?WT.mc_id=dotnet-0000-bramin
Other
2.99k stars 294 forks source link

Allow from a command to be able to observe properties and automatically call NotifyCanExecuteChanged #755

Closed bdovaz closed 1 year ago

bdovaz commented 1 year ago

Overview

This functionality already exists in Prism:

https://prismlibrary.com/docs/commands/commanding.html#observesproperty

When transitioning to RelayCommand I missed it and had to make Prism and CommunityToolkit coexist at the same time to be able to use both RelayCommand and DelegateCommand with ObservesProperty().

API breakdown

Relevant Prism links:

https://github.com/PrismLibrary/Prism/blob/29c0356899dfc3a8dd2467d685424fd08946abd6/src/Prism.Core/Commands/DelegateCommand.cs#L109

https://github.com/PrismLibrary/Prism/blob/29c0356899dfc3a8dd2467d685424fd08946abd6/src/Prism.Core/Commands/DelegateCommandBase.cs#L95

https://github.com/PrismLibrary/Prism/blob/29c0356899dfc3a8dd2467d685424fd08946abd6/src/Prism.Core/Commands/PropertyObserver.cs

Usage example

var command = new RelayCommand(Submit, CanSubmit) .ObservesProperty(() => Name) .ObservesProperty(() => Email);

Breaking change?

No

Alternatives

Use Prism's DelegateCommand

Additional context

No response

Help us help you

No, just wanted to propose this

Sergio0694 commented 1 year ago

The MVVM Toolkit already supports this through the source generators. The only difference is you need to annotate the property and not the command, as we specifically adopted a top-down approach, so that it's always possible to know all dependent changes that will be triggered when any specific component is updated (rather than going the other way around).

That is, this Prism example:

public class ArticleViewModel : BindableBase
{
    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set { SetProperty(ref _isEnabled, value); }
    }

    public DelegateCommand SubmitCommand { get; private set; }

    public ArticleViewModel()
    {
        SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesProperty(() => IsEnabled);
    }

    void Submit()
    {
        //implement logic
    }

    bool CanSubmit()
    {
        return IsEnabled;
    }
}

Would look like this with the MVVM Toolkit:

public partial class ArticleViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SubmitCommand))]
    private bool _isEnabled;

    [RelayCommand(CanExecute = nameof(IsEnabled))]
    void Submit()
    {
        //implement logic
    }
}

That's much more compact, and it's also very efficient and trim/AOT-friendly, unlike Prism's implementation 🙂

bdovaz commented 1 year ago

@Sergio0694 OK, so the problem is a lack of documentation because at least on these pages it doesn't appear (I think):

https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/overview

EDIT: I haven't said anything, it does appear:

https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty#notifying-dependent-commands