dotnet icon indicating copy to clipboard operation
dotnet copied to clipboard

[Feature] Being able to subscribe actions when RelayCommand is executed

Open antoniovalentini opened this issue 2 years ago • 0 comments

Overview

Context

I'm using Avalonia UI for a simple project where the MainWindow opens a dialog window. When the dialog closes, I want to get back the dialog result, which in my case is the dialog window view model. To be able to return an object when the dialog closes, I need a way to tell the OK/CANCEL buttons in the dialog to execute the Avalonia window's Close(object dialogResult) method.

To achieve that, I need to put inside the dialog view model an action like public Action<DialogWindowViewModel>? Close { get; set; }, and call it when the related command gets triggered like this:

public partial class DialogWindowViewModel : ObservableObject
{
    [RelayCommand]
    private void Ok()
    {
        Result = "OK";
        Close?.Invoke(this);
    }
}

In order for it to work, I need to "subscribe" to the Close action inside the view model from the dialog window code-behind:

public partial class DialogWindow : Window
{
    public DialogWindow()
    {
        InitializeComponent();
        Activated += (_, _) => ((DialogWindowViewModel)DataContext!).Close = Close;
    }
}

This is the only way I found to implement it, without involving third-party frameworks like ReactiveUI, but I feel like it's leaking some View logic inside the ViewModel.

API breakdown

public sealed class RelayCommand : IRelayCommand, IObservable<Object>
{
    // implements the Subscribe method
}

Then we can use the public static IDisposable Subscribe<T>(this IObservable<T> source, Action<T> onNext) extension method to add the action we want to be invoked when the command is executed.

Usage example

public partial class DialogWindow : Window
{
    public DialogWindow()
    {
        InitializeComponent();
        Activated += (_, _) => ((DialogWindowViewModel)DataContext!).OkCommand.Subscribe(Close);
    }
}

Breaking change?

I'm not sure

Alternatives

Use the ReactiveCommand from ReactiveUI instead of the CommunityTollkit's RelayCommand, and subscribe from dialog's code-behind:

public partial class DialogWindow : ReactiveWindow<DialogWindowViewModel>
{
    public DialogWindow()
    {
        InitializeComponent();
        this.WhenActivated(d => d(ViewModel!.OkCommand.Subscribe(Close)));
    }
}

Additional context

No response

Help us help you

Yes, but only if others can assist

antoniovalentini avatar Aug 20 '23 16:08 antoniovalentini