Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my ICommand.CanExecute(object) parameter null when I set CommandParameter to some Binding, but non-null when I set it to some static value?

Tags:

c#

wpf

icommand

I'm learning ICommands in WPF and I ran into a problem with some simple code. I have a Button with a Command. If I set the command parameter to a static value like this, CommandParameter="100", the value of the parameter argument in CanExecute is 100, however when I set the value of the command parameter via binding like this CommandParameter="{Binding}", the value of the parameter argument in CanExecute is null.

Here's my ICommand:

internal class MyCommand : ICommand
{
    public bool CanExecute(object parameter) //parameter is null
    {
        var datacontext = parameter as MyDataContext;
        if (datacontext == null)
            return false;

        return datacontext.IsChecked == true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        throw new NotImplementedException();
    }
}

Here's the XAML code. Notice that I'm setting the CommandParameter before setting the Command. I got that from here. Again, if I change the CommandParameter to somwthing like CommandParameter="100", the code acts as I would expect (i.e., the parameter is 100, not null).

<StackPanel Orientation="Vertical">
    <StackPanel.Resources>
        <cmd:MyCommand x:Key="kCmd" />
    </StackPanel.Resources>

    <CheckBox Content="Check this to enable button" IsChecked="{Binding IsChecked}" />
    <Button Content="Click" CommandParameter="{Binding}" 
            Command="{StaticResource kCmd}" />
</StackPanel>

Here's my MainWindow code-behind. Here, I'm setting the DataContext before calling InitializeComponent(). While debugging, I found that InitializeComponent() triggers a call to the ICommand's CanExecute(object).

public MainWindow()
{
    this.DataContext = new MyDataContext();
    InitializeComponent();
}

My MyDataContext class is pretty simple, so I left it out.

like image 923
user2023861 Avatar asked Sep 29 '14 14:09

user2023861


2 Answers

It is also a solution to force reevaluation of CanExecute from the on Loaded of a FrameworkElement event by raising the CanExecuteChanged event of the command. This approach can especially be used, when you are working with DataTemplates, and you are experiencing this problem.

Example:

 <DataTemplate x:Key="MyTemplate">
            <Grid Loaded="HandleLoaded">
 ...

And code behind:

 void HandleLoaded(object sender, RoutedEventArgs e)
 {
     var viewModel = this.DataContext as ViewModel;
     if (viewModel != null)
     {
         viewModel.DoItCommand.RaiseCanExecuteChanged();
     }
 }

Another possible solution, which may works, is to define the binding to the command itself as IsAsync=True. But this will result in some flickering. So it is maybe not the best choice.

Example: {Binding DoItCommand, IsAsync=True}

like image 118
Christoph Meißner Avatar answered Nov 09 '22 05:11

Christoph Meißner


Try raising the CanExecuteChanged-event of the MyCommand class after InitializeComponent() has finished. Probably, the CanExecute(object) of MyCommand is called to initialize the state of the button when rendering the first time, while the bindings have not all yet been initialized.

like image 1
Wim.van.Gool Avatar answered Nov 09 '22 06:11

Wim.van.Gool