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?
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.
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.