DataTrigger в привязке CellTemplate к HeaderTemplate; это может работать?

Цель здесь состоит в том, чтобы проверить все флажки сетки, если флажок заголовка изменится:

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

Похоже:

Экран

Почему-то триггер не срабатывает.

Любые идеи?


person Jerry Nixon    schedule 30.08.2011    source источник


Ответы (2)


Как вы заметили, привязка ElementName внутри DataTemplate не может достичь элемента за пределами шаблона. Это связано с тем, что он может быть создан много раз и имеет собственную область имен, поэтому любая привязка ElementName, которую вы создаете внутри DataTemplate, будет искать внутри шаблона другой элемент с таким именем.

Глядя на это с помощью Snoop, мы также видим, что привязку RelativeSource нельзя использовать напрямую, поскольку они находятся в разных частях визуального дерева.

введите здесь описание изображения

Единственное, что я могу придумать, чтобы обойти это, - это привязать оба флажка к общему предку, например. родитель DataGrid и использовать присоединенное свойство или свойство Tag. Пример

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

и

<DataTemplate>
    <!-- header check -->
    <CheckBox Name="HeaderCheckbox"
              IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                  Path=Tag,
                                  Mode=OneWayToSource}"/>
</DataTemplate>
person Fredrik Hedblad    schedule 30.08.2011
comment
Умно, и если используется тег, вы можете добавить прикрепленное свойство. - person Jerry Nixon; 31.08.2011

Я не думаю, что регулярная привязка данных к HeaderCheckBox возможна, потому что CheckBox существует как часть Template и находится в другой ветви VisualTree, чем DataGridItems

Обычно я делаю наоборот: когда проверяется заголовок CheckBox, проверяем всю строку CheckBoxes. Моя основная причина этого заключается в том, что CheckBoxes обычно существуют, поэтому пользователи могут устанавливать/снимать их, и если они привязаны к состоянию CheckBox заголовка, то пользователь не может их изменить.

Для реализации этого я обычно подключаюсь к событию Click или Checked CheckBox заголовка.

Если состояние строки CheckBox.IsChecked связано с чем-то в ViewModel, я привязываю событие к Command в моей ViewModel и устанавливаю элемент данных, к которому привязан CheckBox.IsChecked, на true/false в зависимости от состояния CheckBox заголовка (обычно передается в качестве CommandParameter)

Если состояние CheckBox.IsChecked ни к чему не привязано, вы можете использовать обычный код программной части, чтобы перебрать DataGrid.Items, использовать ItemContainerGenerator для получения ItemContainer для каждого элемента, найти CheckBox, а затем установить его состояние проверки.

person Rachel    schedule 30.08.2011
comment
Извините, ваш ответ не имеет для меня смысла. Вопрос о DataTriggers в привязке CellTemplate к HeaderTemplate. Я не использую команды или ViewModel в вопросе. Думаю, мне действительно просто интересно, можно ли это сделать. Я понимаю, что использование других вещей, кроме триггеров, может сделать то же самое. Извините, я не был более ясен в своем вопросе. - person Jerry Nixon; 30.08.2011
comment
@Jerry Я уверен, что это возможно, но для этого потребуются весьма необычные обходные пути, такие как конвертер, который ищет в визуальном дереве флажок заголовка. Я бы порекомендовал вам переоценить то, чего вы пытаетесь достичь, прежде чем идти по этому пути, и проверить альтернативы. - person Rachel; 30.08.2011
comment
Я не думаю, что обычная привязка данных к HeaderCheckBox возможна, потому что CheckBox существует в шаблоне и находится в другой ветви VisualTree, чем DataGridItems. - person Rachel; 30.08.2011