I ran into a problem that the Command CanExecute is not invoked when the CommandSource's CommandParameter is updated.

For example, this spike application displays a window with a title and two buttons.

CommandManagerNotDetermineCommandParameterChange Window

this.CommandBindings.Add(new CommandBinding(Commands.OldTitle, OldTitleExecuted, OldTitleCanExecute));

this.CommandBindings.Add(new CommandBinding(Commands.NewTitle, NewTitleExecuted, NewTitleCanExecute));

The "OldTitle" button should only be enabled if the window's title is "Window New".

void OldTitleCanExecute(object sender, CanExecuteRoutedEventArgs e)

{

string title = e.Parameter as string;

e.CanExecute = !string.IsNullOrEmpty(title) && 0 == string.Compare(title, Constants.TtileNew);

}

The "OldTitle" will change the window's title to "Window Old".

void OldTitleExecuted(object sender, ExecutedRoutedEventArgs e)

{

ChangeTitleAsync(Constants.TitleOld);

}

The "NewTitle" button should only be enabled if the window's titile is "Window Old".

The "NewTitle" will change the window's title to "Window New".

Each of this buttons has its CommandProperty set to a static RoutedUICommand and its CommandParameterProperty set to the window's title.

<Button

Command="{x:Static sun:Commands.OldTitle}"

CommandParameter="{Binding ElementName=window, Path=Title}"

Content="{Binding Source={x:Static sun:Commands.OldTitle}, Path=Text}" />

<Button

Command="{x:Static sun:Commands.NewTitle}"

CommandParameter="{Binding ElementName=window, Path=Title}"

Content="{Binding Source={x:Static sun:Commands.NewTitle}, Path=Text}" />

The RoutedUICommand's Execute code will fire up a background worker to sleep a short while, then return the new title (or old) to be set to the Windows's title.

private void ChangeTitleAsync(string title)

{

BackgroundWorker bw = new BackgroundWorker();

bw.DoWork += new DoWorkEventHandler(ChangeTitle);

bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ChangeTitleCompleted);

bw.RunWorkerAsync(title);

}

void ChangeTitleCompleted(object sender, RunWorkerCompletedEventArgs e)

{

BackgroundWorker bw = sender as BackgroundWorker;

if (bw != null)

{

bw.Dispose();

bw = null;

}

string title = e.Result as string;

if (!string.IsNullOrEmpty(title))

{

this.Title = title;

}

//CommandManager.InvalidateRequerySuggested();

}

void ChangeTitle(object sender, DoWorkEventArgs e)

{

System.Threading.Thread.Sleep(1000);

e.Result = e.Argument;

}

Since the windows' title is a dependency property, and it is referenced in the CommandParameter via DataBinding, so as soon as the window's title is set, the CommandParameter is updated. (You can prove this by throwing in a TracingConverter in the DataBinding).

However, the update of CommandParameter does not cause the Command's CanExecute being re-evaluted.

One solution is to force a CommandManager to raise the RequerySuggested event via the InvalidateRequerySuggested static method. And in the examle above, one place to call it reactively is in the end of the background worker completed handler since we just touched the window's Title.

void ChangeTitleCompleted(object sender, RunWorkerCompletedEventArgs e)

{

...

CommandManager.InvalidateRequerySuggested();

}

In summary, as MSDN has pointed out:

In situations where the CommandManager does not sufficiently determine a change in conditions that cause a command to not be able to execute, InvalidateRequerySuggested can be called to force the CommandManager to raise the RequerySuggested event.