0

I have the following code in my WPF Application that I use to enable or disable buttons on my UI

XAML

<Button x:Name="FirstButton" Command="{x:Static local:MyClass.CommandOne}"/>
<Button x:Name="FirstButton" Command="{x:Static local:MyClass.CommandTwo}"/>

Code Behind

CommandBindings.Add(new CommandBinding(CommandOne, CommandOne_Executed, CommandOne_CanExecute));
CommandBindings.Add(new CommandBinding(CommandTwo, CommandTwo_Executed, CommandTwo_CanExecute));

public static readonly RoutedUICommand CommandOne = new RoutedUICommand();
public static readonly RoutedUICommand CommandTwo = new RoutedUICommand();

private void CommandOne_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    if (e != null)
        e.CanExecute = (_currentValue > 1);
}

private void CommandTwo_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    if (e != null)
        e.CanExecute = (_currentValue > 100);
}

Worker Code

private async Task DoSomeWork(int value)
{
    await Task.Run(() =>
    {
        // Do some work on value
        _currentValue = value
    }
}

What I find is that when the values of _currentValue change due to some processing sometimes the CommandOne_CanExecute and CommandTwo_CanExecute functions do not get called. If I then e.g. move the UI they will then be called. How can I ensure that these are called everytime.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Harry Boy
  • 4,159
  • 17
  • 71
  • 122
  • Can you post _currentValue full property code ? – Krishna Apr 19 '17 at 08:41
  • Is there any reason why you're using these types of commands? Usually they go into your `ViewModel`? I use my own `ICommand` implementations which allows me to handle when to call `Can Execute Changed`. Here is a [link to such implementation](http://stackoverflow.com/a/22286816/2029607) – XAMlMAX Apr 19 '17 at 08:41
  • 1
    A similar thing has occured to me. Try calling `CommandManager.InvalidateRequerySuggested()` in your code-behind after executing a command. http://stackoverflow.com/questions/2331622/weird-problem-where-button-does-not-get-re-enabled-unless-the-mouse-is-clicked –  Apr 19 '17 at 08:45
  • @MickyD bare in mind that call `CommandManager.InvalidateRequerySuggested()` is an expensive method as it will refresh everything on the screen. – XAMlMAX Apr 19 '17 at 08:58
  • @XAMlMAX Agreed. However in some cases there was no alternative –  Apr 19 '17 at 09:03

2 Answers2

3

You could call the CommandManager.InvalidateRequerySuggested() method to invalidate all commands.

What you should do however is to implement your own command - it is simply a class that implements the ICommand interface - and raise the CanExecuteChanged event of the command whenever you want the CanExecute method to get called.

Or you could use any of the implementations of the ICommand that are included in any of the MVVM frameworks out there. You could for example take a look at how the RelayCommand class is implemented in MvvmLight: https://github.com/paulcbetts/mvvmlight/blob/master/GalaSoft.MvvmLight/GalaSoft.MvvmLight%20(NET35)/Command/RelayCommand.cs.

It has a RaiseCanExecuteChanged() method that you can call to raise the CanExecuteChanged event of the command. This will cause the CanExecute method to get invoked and the command to be invalidated.

The built-in RoutedUICommand class has no such method I am afraid.

mm8
  • 163,881
  • 10
  • 57
  • 88
0

You need to create a property for CurrentValue instead of a variable and raisecanexecutechanged event Try below code

    private int _currentValue = false;
    public bool CurrentValue
    {
        get { return _currentValue; }
        set 
          { 
              if(_currentValue != value)
                CommandManager.InvalidateRequerySuggested();
              SetProperty(ref _currentValue, value); 

          }
    }

CommandBindings.Add(new CommandBinding(CommandOne, CommandOne_Executed, CommandOne_CanExecute));
CommandBindings.Add(new CommandBinding(CommandTwo, CommandTwo_Executed, CommandTwo_CanExecute));

public static readonly RoutedUICommand CommandOne = new RoutedUICommand();
public static readonly RoutedUICommand CommandTwo = new RoutedUICommand();

private void CommandOne_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    if (e != null)
        e.CanExecute = (CurrentValue > 1);
}

private void CommandTwo_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    if (e != null)
        e.CanExecute = (CurrentValue > 100);
}

private async Task DoSomeWork(int value)
{
    await Task.Run(() =>
    {
        // Do some work on value
        CurrentValue = value
    }
}
Krishna
  • 1,945
  • 1
  • 13
  • 24
  • CommandOne.RaiseCanExecuteChanged(); gives the error Error CS1061 'RoutedUICommand' does not contain a definition for 'RaiseCanExecuteChanged' and no extension method 'RaiseCanExecuteChanged' accepting a first argument of type 'RoutedUICommand' could be found (are you missing a using directive or an assembly reference?) – Harry Boy Apr 19 '17 at 09:18
  • 1
    @HarryBoy my mistake RoutedUICommand do not has a built-in RaiseCanExecuteChanged I think you need to use InvalidateRequerySuggested – Krishna Apr 19 '17 at 09:27