-1

I have a DataGrid with a column of checkboxes and I have a checkbox in the DataGrid header that, when checked, checks all the checkboxes. Based on this answer, I have a command bound to the "checked" event and another one that binds to the "unchecked" event.

All the relevant files are below (simplified, of course)

My XAML:

<DataGridTemplateColumn Width="40">
    <DataGridTemplateColumn.Header>
        <CheckBox>
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Checked">
                    <i:InvokeCommandAction Command="{Binding CheckAllRowsCommand}"/>
                </i:EventTrigger>
                <i:EventTrigger EventName="Unchecked">
                    <i:InvokeCommandAction Command="{Binding UncheckAllRowsCommand}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </CheckBox>
    </DataGridTemplateColumn.Header>

    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>

</DataGridTemplateColumn>

My xaml.cs

public partial class MyTableView: UserControl
{
    public MyTableView()
    {
        InitializeComponent();
        DataContext = new MyTableViewModel();
    }
}

MyTableViewModel.cs

public class MyTableViewModel: BaseViewModel
{
    public MyTableViewModel() : base()
    {
        CheckAllRowsCommand= new CheckAllRowsCommand(this);
        UncheckAllRowsCommand = new UncheckAllRowsCommand(this);
    }

    public ICommand CheckAllRowsCommand{ get; }
    public ICommand UncheckAllRowsCommand{ get; }
}

CheckAllRowsCommand

public class CheckAllRowsCommand: BaseCommand
{
    public CheckAllRowsCommand(MyTableViewModel parent) : base(parent)
    {
    }

    public override bool CanExecute(object parameter)
    {
        return true;
    }

    public override void Execute(object parameter)
    {
       // Set the Selected property of each data row
    }
}

When running this, I get the following error:

System.Windows.Data Error: 40 : BindingExpression path error: 'CheckAllRowsCommand' property not found on 'object' ''CheckBox' (Name='')'. BindingExpression:Path=CheckAllRowsCommand; DataItem='CheckBox' (Name=''); target element is 'InvokeCommandAction' (HashCode=47015983); target property is 'Command' (type 'ICommand')

Any help would be greatly appreciated.

noblerare
  • 10,277
  • 23
  • 78
  • 140

3 Answers3

0

CheckAllRowsCommand is a property in a ViewModel, not in CheckBox. Try to bind to viewModel via DataGrid (it should have inherited the DataContext):

<DataGrid Name="NameOfDataGrid" ...>

<i:InvokeCommandAction Command="{Binding DataContext.CheckAllRowsCommand, ElementName=NameOfDataGrid}"/>
ASh
  • 34,632
  • 9
  • 60
  • 82
0

This is a more verbose solution than ASh has posted but is more flexible, in my opinion.

<CheckBox DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}">
...
</CheckBox>

The problem is that the binding in the template seems to be using the CheckBox as it's data context when you would think it would use the data context of the containing control. WPF can be funky sometimes, an any time I see an error like this (ie BindingExpression path error: 'blah' property not found on 'object' ''blah') I always assume the data context isn't being inferred correctly.

The workaround I'm proposing is to forcibly set the data context on the CheckBox using a relative source. Relative sources can be used to declaratively say "use the first parent of this type" for example. This binding is saying "set my DataContext property to the DataContext of the first parent above me of type DataGrid". Logically the first parent of that checkbox of type DataGrid is going to be the one you intended. Then your inner command bindings don't need to change and everything should work as expected.

Will Custode
  • 4,576
  • 3
  • 26
  • 51
0

You could bind the DataContext of the CheckBox to the DataContext of the parent UserControl where the command properties are defined using a {RelativeSource}:

<CheckBox DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Checked">
            <i:InvokeCommandAction Command="{Binding CheckAllRowsCommand}"/>
        </i:EventTrigger>
        <i:EventTrigger EventName="Unchecked">
            <i:InvokeCommandAction Command="{Binding UncheckAllRowsCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</CheckBox>
mm8
  • 163,881
  • 10
  • 57
  • 88