The trick is to use Windows.Input.CommandManager.InvalidateRequerySuggested()
Here's an overly complicated example. Start a new WPF App project using C# and call it EvaluateCommand.
The main window will have two buttons and a TextBlock. Each button is linked to a command that is only executable when the TextBlock has a specific content. Each button launches a background task that changes the content of the TextBlock. We need that task to cause the commands' CanExecute methods to be re-evaluated when the task is done.
Here is the XAML
<Window x:Class="EvaluateCommand.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:EvaluateCommand"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="Evaluate
Command" SizeToContent="WidthAndHeight">
<Window.Resources>
<RoutedCommand x:Key="FailCommand"/>
<RoutedCommand x:Key="PassCommand"/>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource FailCommand}" CanExecute="Fail_CanExecute" Executed="Fail_Executed"/>
<CommandBinding Command="{StaticResource PassCommand}" CanExecute="Pass_CanExecute" Executed="Pass_Executed"/>
</Window.CommandBindings>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="Make
it Fail" Command="{StaticResource FailCommand}" Width="100" Margin="20"/>
<TextBlock Grid.Column="1" Text="{Binding Status}" Width="100" Margin="20"/>
<Button Grid.Column="2" Content="Make
it Pass" Command="{StaticResource PassCommand}" Width="100" Margin="20"/>
</Grid>
</Window>
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Runtime.CompilerServices;
namespace EvaluateCommand
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _Status
= "PASS";
public string Status
{
get { return _Status;
}
set { SetProperty(ref _Status, value); }
}
public MainWindow()
{
InitializeComponent();
}
private void
Fail_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = (Status == "PASS");
}
private void
Fail_Executed(object sender,
ExecutedRoutedEventArgs e)
{
SetStatusAsync("FAIL");
}
private void
Pass_CanExecute(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = (Status == "FAIL");
}
private void
Pass_Executed(object sender,
ExecutedRoutedEventArgs e)
{
SetStatusAsync("PASS");
}
private void
SetStatusAsync(String NewStatus)
{
Status = "Processing...";
BackgroundWorker BW = new BackgroundWorker();
BW.DoWork += SetStatus;
BW.RunWorkerCompleted +=
EvaluateCommands;
BW.RunWorkerAsync(NewStatus);
BW.Dispose();
}
private void
SetStatus(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(1000);
Dispatcher.Invoke(new Action(() => Status =
e.Argument.ToString()));
}
private void
EvaluateCommands(object sender,
RunWorkerCompletedEventArgs e)
{
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
}
public event
PropertyChangedEventHandler PropertyChanged;
public bool
SetProperty<T>(ref T storage, T value,
[CallerMemberName] string
propertyName = null)
{
if (Object.Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
private void
OnPropertyChanged(String PropertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
}