Reputation: 2071
I have problem with passing CommandParameter to my Command
I have command:
private Command<Boolean> _setAsCompletedCommand;
public Command<Boolean> SetAsCompletedCommand
{
get
{
return _setAsCompletedCommand ?? (_setAsCompletedCommand = new Command<Boolean>(isToComplete =>
{
if (isToComplete)
{
//do something
}
else
{
//do something else
}
}, isToComplete =>
{
if (isToComplete)
{
//check something
}
else
{
//check something else
}
}));
}
}
and I'm trying to pass system:Boolean like this:
<Button
Command="{Binding SetAsCompletedCommand}">
<Button.CommandParameter>
<system:Boolean>
True
</system:Boolean>
</Button.CommandParameter>
</Button>
The problem is that on construction of a View my SetAsCompletedCommand.CanExecute()
is executed with False
parameter.
How is it possible? How can I fix it?
When I click the button CommandParameter
is set properly to True
I'm using Catel framework as a MVVM framework in this project. But I don't think it produces the problem.
Upvotes: 1
Views: 1493
Reputation: 5724
This is very tricky and is caused by the order of binding execution. As soon as the binding updates, WPF runs the CanExecute on your command. At this stage the CommandParameter isn't bound yet and Catel uses the default value of a Boolean (which is false
) to use as the command parameter.
After that, commands are only being re-evaluated when a property changes. For example, there is no reason for Catel to invalidate the commands state unless a property changes.
One solution to fix this is to use CommandManager.InvalidateCommands(true)
in the InitializeAsync
of your view model. This will invalidate the state of all commands on the vm and re-evaluate them (and at this stage, the binding for the command parameter is correct).
You might ask yourself: "Why don't you re-evaluate automatically in the initialize method?".
Well, we did in the past, but we wanted to provide better performance out of the box. This is probably the first time you are using a command parameter that hits this restriction, but I think you created many more commands in the past. It means that you had a performance improvements on all the other commands so far, so I still think it's a good decision. There are sufficient workarounds as well to get back the old behavior where it used the CommandManager
of WPF to re-evaluate commands on nearly every routed event (mouse move, keyboard, etc).
Upvotes: 1
Reputation: 2323
To start, I wouldn't ever create a property this way. You should stick to simple properties and let the command itself do all the work. I would suggest writing a custom command and have it inherit from a command base class like this one:
using System;
using System.Windows.Input;
namespace MyCommands
{
/// <summary>
/// Base class for all Commands.
/// </summary>
public abstract class CommandBase : ICommand
{
/// <summary>
/// Defines the method that determines whether the command can execute in its current
/// state.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data
/// to be passed, this object can be set to null.</param>
/// <returns>
/// true if this command can be executed; otherwise, false.
/// </returns>
public virtual bool CanExecute(object parameter)
{
return true;
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data
/// to be passed, this object can be set to null.</param>
public virtual void Execute(object parameter) {}
/// <summary>
/// Occurs when changes occur that affect whether or not the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
For there, you can specify the conditions that allow your command to execute and what it should do when it is executed by simply overriding CanExecute and Execute respectively.
Upvotes: 0