DataTrigger in CellTemplate binding to HeaderTemplate; can it work?
The goal here would be to check all grid checkboxes if the header checkbox changes:
<Window.Resources>
<Style TargetType="CheckBox" x:Key="InnerBox">
<Setter Property="HorizontalAlignment" Value="Center" />
<Style.Triggers>
<DataTrigger Value="True"
Binding="{Binding IsChecked,
ElementName=HeaderCheckbox}">
<Setter Property="IsChecked" Value="True" />
</DataTrigger>
<DataTrigger Value="False"
Binding="{Binding IsChecked,
ElementName=HeaderCheckbox}">
<Setter Property="IsChecked" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<DataGrid>
<DataGrid.Columns>
<!-- col1 --开发者_如何学编程>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<!-- header check -->
<CheckBox Name="HeaderCheckbox" />
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!-- body check -->
<CheckBox Style="{StaticResource InnerBox}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- col2 -->
<DataGridTextColumn Binding="{Binding}" Header="Text" />
</DataGrid.Columns>
<!-- sample data -->
<sys:String>1</sys:String>
<sys:String>2</sys:String>
<sys:String>3</sys:String>
</DataGrid>
Looks like:
For some reason, the trigger does not fire.
Any ideas?
ElementName
binding inside a DataTemplate
can't reach an element outside of the template as you noticed. This is because it can be instantiated many times and has its own namescope so any ElementName
binding you create inside a DataTemplate
will look inside the template for another element with that name.
Looking at it with Snoop we can also see that a RelativeSource
binding can't be used directly since they are in different parts of the Visual Tree
The only thing that I can think of to get around this is to bind both of the CheckBoxes to a common ancestor, e.g. the parent DataGrid
and use an attached property or the Tag
property. Example
<Style TargetType="CheckBox" x:Key="InnerBox">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="IsChecked" Value="False" />
<Style.Triggers>
<DataTrigger Value="True"
Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=Tag}">
<Setter Property="IsChecked" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
and
<DataTemplate>
<!-- header check -->
<CheckBox Name="HeaderCheckbox"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=Tag,
Mode=OneWayToSource}"/>
</DataTemplate>
I don't think regular DataBinding to the HeaderCheckBox
is possible because the CheckBox
exists as part of a Template
, and it is in a different branch of the VisualTree
than the DataGridItems
Usually I make it the reverse: When the header CheckBox
gets checked, check all the row CheckBoxes
. My main reason for this is because the CheckBoxes are usually there so users can check/uncheck them, and if they're bound to the header CheckBox checked state, then the user can't alter them.
For implementing that, I usually hook into the Click
or Checked
event of the Header CheckBox.
If the row CheckBox.IsChecked
state is bound to something in a ViewModel, I'll hook the event to a Command
in my ViewModel, and set the data item that the CheckBox.IsChecked
is bound to to true
/false
depending on the header CheckBox state (usually passed in as a CommandParameter
)
If the CheckBox.IsChecked
state is not bound to anything, you can use regular code-behind to loop through your DataGrid.Items
, use the ItemContainerGenerator
to get the ItemContainer for each item, find the CheckBox
, and then set it's check state.
精彩评论