开发者

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:

DataTrigger in CellTemplate binding to HeaderTemplate; can it work?

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

DataTrigger in CellTemplate binding to HeaderTemplate; can it work?

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜