0

The case is that I try to disable a button in the window form when it was clicked and after some time (some seconds) it should be enabled again.

But this didn't work. After a click on the button the command set the enabled to false and after some seconds the command set it back to true (I tested it, the order is right and it set it to true again) but the button is still not enabled on the window form.

For that case I use a RelayCommmand. The RelayCommand is a standard class you find on Internet and will be shown in the end. To organise the command I wrote a class called Testclass:

class Testclass
{
    private bool _testValueCanExecute;
    public bool TestValueCanExecute
    {
        get { return _testValueCanExecute; }
        set
        {
            _testValueCanExecute = value;
            OnPropertyChanged();
        }
    }

    public ICommand TestValueCommand { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    public Testclass()
    {
        TestValueCommand = new RelayCommand(TestMethod, param => _testValueCanExecute);
        TestValueCanExecute = true;
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private async void TestMethod(object obj)
    {
        TestValueCanExecute = false;
        await Task.Delay(3000);
        TestValueCanExecute = true;
    }
}

In the XAML File I added a button as followed:

<Button x:Name="TestButton" Command="{Binding TestValueCommand}" Content="Test Button" HorizontalAlignment="Left" Margin="149,96,0,0" VerticalAlignment="Top" Width="75"/>

The MainWindow code looks as followed:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new Testclass();
    }
}

So the RelayCommand use the TestMethod method set the command enable variable to false, wait 3 seconds and set them back to true. But as I wrote above the button on the window form still not enabled.

It would be nice to understand what happens here and how I can solve this.

Update: I use the following Code for the RelayCommand:

public class RelayCommand : ICommand
{
    private Action<object> execute;
    private Func<object, bool> canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }
}
mm8
  • 163,881
  • 10
  • 57
  • 88
Mirar
  • 171
  • 13
  • I may need more caffine, but this code seems confusing - such as your setter OnPropertyChanged(); doesnt list which property you're changing so to me it would seem its right nothing would change – BugFinder Jan 16 '18 at 10:54
  • 1
    @BugFinder the `[CallerMemberName]` attribute makes sure to send the name behind the scenes. – default Jan 16 '18 at 11:06
  • 1
    thanks @Default I just dont see how it knows its supposed to enable/disable.. – BugFinder Jan 16 '18 at 11:07
  • @BugFinder oh. Sorry, I guess I misunderstood. In that regard I agree with you, but I believe that is a misunderstanding from the OP, because he never does trigger enable/disable of the command. It _shouldn't_ (depending on the implementation of RelayCommand) have anything to do with the OPC method though. – default Jan 16 '18 at 11:12
  • Your implementation doesn't have any RaiseCanExecuteChanged() method. See my edited answer. – mm8 Jan 16 '18 at 12:56

2 Answers2

1

The RelayCommand is a standard class you find on Internet ...

There is no such thing as a "standard class you find on Internet". In fact there are several different implementations of the RelayCommand available "on the Internet".

A good implementation should contain a method for raising the CanExecuteChanged event. MvvmLight's implementation has a RaiseCanExecuteChanged() method that does this. You need to call this one to "refresh" the status of the command:

private async void TestMethod(object obj)
{
    RelayCommand cmd = TestValueCommand as RelayCommand;
    TestValueCanExecute = false;
    cmd.RaiseCanExecuteChanged();
    await Task.Delay(3000);
    TestValueCanExecute = true;
    cmd.RaiseCanExecuteChanged();
}

The event is not raised automatically when you set the TestValueCanExecute property and raise the PropertyChanged event for the view model.

Edit: Your implementation doesn't have any RaiseCanExecuteChanged() method. Add one to your RelayCommand class and call it as per above:

public void RaiseCanExecuteChanged()
{
    CommandManager.InvalidateRequerySuggested();
}
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thank you for your answer. To use a framework is one possible answer, but at the moment I try to get into C# and for that I want understand what happens and to learn how it works. For that it would be nice to see how a simple `RaiseCanExecuteChanged()` can be written by my own. And expectedly I hope to understand how it works and why the code from me don't work. – Mirar Jan 17 '18 at 12:17
0

I strongly recommend using existing frameworks, instead of inveting the wheel once again. Take a look at ReactiveUI ReactiveCommand

In your case, it would do all the work by itself:

TestValueCommand = ReactiveCommand.CreateFromTask(async () => await Task.Delay(500));

You bind that command to a button in xaml and the button is disabled until command is done.

You can easily add another condition for disabling the command, and then, binding will disable button

Krzysztof Skowronek
  • 2,796
  • 1
  • 13
  • 29