0

In reactive UI, BindCommand can bind some control to view model's property or method, e.g. method in ViewModel that will be executed when some button in XAML was clicked.

https://www.reactiveui.net/docs/handbook/commands/binding-commands

How do I disable or enable a set of buttons when some of them was clicked?

According to docs, BindCommand should have 3rd argument that can accept some function, but can't find an example.

XAML

<Button Content="Start" x:Name="StartButton" />
<Button Content="Stop" x:Name="StopButton" IsEnabled="False" />
<Button Content="Pause" x:Name="PauseButton" IsEnabled="False" />

XAML.cs

// How to enable Stop and Pause when Start was clicked?

this.BindCommand(ViewModel, vm => vm.Stop, view => view.StopButton).DisposeWith(container);
this.BindCommand(ViewModel, vm => vm.Start, view => view.StartButton).DisposeWith(container);
this.BindCommand(ViewModel, vm => vm.Pause, view => view.PauseButton).DisposeWith(container);

// In plain WPF I could do modify controls inside OnClick handler

private void OnStartClick(object sender, RoutedEventArgs e)
{
  // How can I do this in Reactive UI?

  StopButton.IsEnabled = true;
  PauseButton.IsEnabled = true;
}

View Model

public DashboardViewModel(IScreen screen)
{
  HostScreen = screen;

  // Or how to get access to controls in these event handlers?

  Stop = ReactiveCommand.Create(() => {});
  Start = ReactiveCommand.Create(() => {});
  Pause = ReactiveCommand.Create(() => {});
}
Anonymous
  • 1,823
  • 2
  • 35
  • 74
  • 2
    Try https://www.reactiveui.net/docs/handbook/commands/#controlling-executability – Glenn Watson Aug 15 '20 at 11:54
  • @GlennWatson Well, this is an example of how to prevent executing of some event handler on the `ViewModel` level. My question is more about how to visually update UI, disable, change colors, maybe even hide some elements when button `click` happens. In plain WPF, I would do all these things right in the `OnClick` event handler. Would be good to know how to manipulate UI controls using Reactive UI. – Anonymous Aug 15 '20 at 12:22
  • 2
    I'd probably add a few reactive IsEnabled properties in the view model to bind to each button. And just subscribe to the commands within the view model to pipe the values into the properties. – Colt Bauman Aug 15 '20 at 16:27
  • 2
    @anonymous yeah the only way you can do it from the view model is either properties or passing in a `IObservable` into your ViewModel. – Glenn Watson Aug 16 '20 at 04:47
  • 1
    You normally don't execute event handlers in view model levels in MVVM. That's against MVVM. You must use commands instead of events, and commands have "CanExecute" which enables and disables the View. – Bizhan Aug 16 '20 at 11:17

2 Answers2

2

ReactiveCommand.Create accepts an IObservable<bool> that determines whether the value of CanExecute:

Start = ReactiveCommand.Create(() => { });
Stop = ReactiveCommand.Create(() => { }, Start.Select(_ => true));
Pause = ReactiveCommand.Create(() => { }, Start.Select(_ => true));
mm8
  • 163,881
  • 10
  • 57
  • 88
  • @Anonymous: Does this answer your question? – mm8 Aug 18 '20 at 14:44
  • The example is useful, but it doesn't seem to work for some cases... The first disadvantage is that I need to enable `Stop` and disable `Start` when `Start` is clicked. When `Stop` is clicked, it should disable `Stop` and enable `Start`. Given your example, `Stop` will be undefined inside `Start` handler. – Anonymous Aug 19 '20 at 15:24
  • The second issue, it's still a bit unclear, how to modify styles of the button using this handler... – Anonymous Aug 19 '20 at 15:25
  • @Anonymous: I just answered how to translate your current `OnStartClick` implementation. Obviously, there is a lot more you need to do in your actual app to get this to work as expected. – mm8 Aug 20 '20 at 14:15
1

Considering that 3 persons, including me, voted for creating relevant properties in View Model and binding them in XAML, I did this first.

View Model

public ReactiveCommand<Unit, Unit> StopCommand { get; protected set; }
public ReactiveCommand<Unit, Unit> StartCommand { get; protected set; }

public bool StopState { get => _stopState; set => this.RaiseAndSetIfChanged(ref _stopState, value); }
public bool StartState { get => _startState; set => this.RaiseAndSetIfChanged(ref _startState, value); }

StopCommand = ReactiveCommand.Create(() =>
{
  StopState = false;
  StartState = true;
});

StartCommand = ReactiveCommand.Create(() =>
{
  StopState = true;
  StartState = false;
});

XAML

<Button Content="Start" IsEnabled="{Binding Path=StartState}" x:Name="StartButton" />
<Button Content="Stop" IsEnabled="{Binding Path=StopState}" x:Name="StopButton" />

That seemed like the most MVVM approach, even though not exactly a Reactive UI approach. Then, I found this answer that seems to be way more elegant and doesn't require hardcoded bindings between XAML and View Model.

What are the distinctions between the various WhenAny methods in Reactive UI

Using WhenAnyObservable I can subscribe to selected command and modify XAML from the code-behind, without creating a bunch of unnecessary properties in the View Model

this
  .BindCommand(ViewModel, vm => vm.StartCommand, view => view.StartButton)
  .WhenAnyObservable(o => o.ViewModel.StartCommand)
  .Subscribe(o =>
  {
    StartButton.IsEnabled = false;
    StopButton.IsEnabled = true;
  })
  .DisposeWith(container);

Done.

Anonymous
  • 1,823
  • 2
  • 35
  • 74