I have the following grid:
<DataGrid
x:Name="CandiesDataGrid"
ItemsSource="{Binding Candies}"
SelectedItem="{Binding SelectedCandy}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding CandySelectedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn KeyboardNavigation.IsTabStop="False" IsReadOnly="True" Width="100" Header="{l:LocText Candy_Prop1}" Binding="{Binding CandyInfo.Name}"/>
<DataGridTemplateColumn >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="IsConfirmed" Grid.Column="0"
Style="{StaticResource CandyCheckBox}"
IsChecked="{Binding IsConfirmed, Mode=TwoWay}"
Margin="-75 0 0 0"
Command="{Binding IsConfirmedCommand}">
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
My property uses the OnPropertyChanged
. Not only it does not change the value of IsConfirmed
but also does not executes the ICommand
IsConfirmedCommand
.
I searched on the internet and it seems DataGridTemplateColumn
loses the ItemSource
of the datagrid
.
I did try to put RelativeSource
in after the mode=TwoWay
on my checkbox but it does not work.
Is there any way to have access to the ItemSource in my TemplateColumn?
EDIT:
//Properties
public ObservableCollection<Candy> Candies{ get; } = new ObservableCollection<Candy>();
public Candy SelectedCandy { get { return _selectedCandy; } set { SetProperty(ref _selectedCandy, value); } } //SetProperty acts as OnPropertyChanged
private Candy _selectedCandy;
//Constructor:
public CandyClass()
{
IsConfirmedCommand = new DelegateCommand(IsConfirmedCommand_Execute);
}
//Method
private void IsConfirmedCommand_Execute()
{
//Doing something
}
Inside your CellTemplate, the DataContext is the DataGrid row, whatever that may be (Candy
in this case). So by default, that Candy
instance will be the Source
property of any Binding in that DataTemplate. That's where the binding will look for the property named in the Path (IsConfirmed
and IsConfirmedCommand
, in this case).
That's what you want: You've got more than one row in the grid, and the row is what you care about in a cell, usually. That or the field: But very often a cell template will want to look at more than one field, so they give you the whole row.
But in this case, you want to go back up and grab something off the parent viewmodel. Viewmodels have no natural parent/child hierarchy, though you could give them one if you wanted: Candy could have a Parent
property that had reference to the viewmodel that owns the Candies
collection. If you did, you could bind like this:
Command="{Binding Parent.IsConfirmed}"
But that's not common practice. I don't know if it's a particularly great idea or not.
One reason we don't need to do that is we can tell the binding to use a different source instead. UI elements do have a natural parent/child hierarchy, and bindings can navigate it. If you’re doing things right, your parent viewmodel will be the DataContext of something up there somewhere.
{Binding Path=DataContext.IsConfirmed,
RelativeSource={RelativeSource AncestorType=DataGrid}}
"Walk the UI tree upwards until you find a DataGrid. That's your source. Now, once you have a source, find the source object's DataContext property, if any. If it's got a DataContext, take the value of DataContext and look on that object for some property called IsConfirmed."
DataGrid has a DataContext property. Since your binding to Candies
worked, we know that DataContext must be your class that has a Candies
property. You assure me that class has IsConfirmed
as well.
Hence:
<DataTemplate>
<CheckBox
Style="{StaticResource CandyCheckBox}"
IsChecked="{Binding DataContext.IsConfirmed,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
Margin="-75 0 0 0"
Command="{Binding DataContext.IsConfirmedCommand,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
/>
</DataTemplate>