CommunityToolkit / CommunityToolkit/dotnet
[Feature] Being able to subscribe actions when RelayCommand is executed
- Dominant language
- C#
- Stars
- 3.8k
- Forks
- 400
- PR merge metrics
- No merged PRs in 30d
Description
### 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)](https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.Controls/Window.cs#L449C42-L449C42) method.
To achieve that, I need to put inside the dialog view model an action like `public Action? Close { get; set; }`, and call it when the related command gets triggered like this:
```csharp
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:
```csharp
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](https://www.reactiveui.net/), but I feel like it's leaking some View logic inside the ViewModel.
### API breakdown
```csharp
public sealed class RelayCommand : IRelayCommand, IObservable
{
// implements the Subscribe method
}
```
Then we can use the `public static IDisposable Subscribe(this IObservable source, Action onNext)` extension method to add the action we want to be invoked when the command is executed.
### Usage example
```csharp
public partial class DialogWindow : Window
{
public DialogWindow()
{
InitializeComponent();
Activated += (_, _) => ((DialogWindowViewModel)DataContext!).OkCommand.Subscribe(Close);
}
}
```
### Breaking change?
I'm not sure
### Alternatives
Use the [ReactiveCommand](https://www.reactiveui.net/docs/handbook/commands/) from ReactiveUI instead of the CommunityTollkit's `RelayCommand`, and subscribe from dialog's code-behind:
```csharp
public partial class DialogWindow : ReactiveWindow
{
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
Contributor guide
Assessment
This issue has not been assessed yet.