DataTrigger in CellTemplate binding to HeaderTemplate; can it work?

人走茶凉 提交于 2019-12-12 10:03:45

问题


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?


回答1:


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>



回答2:


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.



来源:https://stackoverflow.com/questions/7245915/datatrigger-in-celltemplate-binding-to-headertemplate-can-it-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!