Binding to a command in a datagrid

2019-02-01 22:30发布

问题:

I am using the M-V-VM pattern in a WPF app. I am binding a ViewModel to a ContentControl and using a data template defined in the window resources to render the view (a UserControl) for that ViewModel.

In the ViewModel, I have a collection of items. I'm binding that collection to the data grid provided in the WPF toolkit. Also in the view model, I have a RemoveItem command defined which takes an argument for the item ID to remove.

How would I bind to that command in the data grid? The grid's data context is that collection, so something like:

<Button Command="{Binding Path=RemoveCommand}" CommandParameter="{Binding Path=id}">X</Button>

doesn't work - it can't find the command. I think I need to do RelativeSource binding, but what would that look like? Would the Ancestor type be UserControl, or the ContentControl? Where is my ViewModel residing as the DataContext?

Or am I way off here?

回答1:

Yeah, you just need to get one level up. I'd try a binding with ElementName first and resort to RelativeSource only if necessary. For example, I'd prefer this:

<DataGrid x:Name="_grid">
    ...
        <Button Command="{Binding DataContext.RemoveItem, ElementName=_grid}"/>
    ...
</DataGrid>

That said, the XAML compiler can get its knickers in a knot when it comes to element names and scoping in controls, so you may need to resort to a RelativeSource:

<DataGrid x:Name="_grid">
    ...
  <Button Command="{Binding DataContext.RemoveItem, 
                    RelativeSource={RelativeSource FindAncestor, 
                                    AncestorType={x:Type DataGrid}}
                   }"/>
    ...
</DataGrid>

You only need to search up until the data context will be your view model. You could search for a UserControl if you wanted to - not sure it really matters. Both are pretty fragile bindings, which is why I prefer the ElementName approach.



回答2:

I like to define the viewmodel in datacontext of the control with name ViewModel. The binding is easier to write by using ElementName

...
<UserControl.DataContext>
    <local:UserControlViewModel x:Name="ViewModel"/>
</UserControl.DataContext>
...

...
<DataGridTemplateColumn  Width="30">
    <DataGridTemplateColumn.CellTemplate>
         <DataTemplate>
             <Button Command="{Binding RemoveCommand, ElementName=ViewModel}" 
                     CommandParameter="{Binding}">Remove</Button>
         </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...

Note that in this case Command Parameter is whole data object of the row. Sometime better than

CommandParameter="{Binding Id}"  

because you don't have to find data again.