CellTemplate 中的 DataTrigger 绑定到 HeaderTemplate;可以吗?

发布于 2024-12-02 06:48:43 字数 2024 浏览 0 评论 0原文

这里的目标是在标题复选框发生变化时检查所有网格复选框:

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

看起来像:

Screen

由于某种原因,触发器不着火。

有什么想法吗?

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:

Screen

For some reason, the trigger does not fire.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

还给你自由 2024-12-09 06:48:43

正如您所注意到的,DataTemplate 内的 ElementName 绑定无法到达模板外部的元素。这是因为它可以多次实例化,并且具有自己的名称范围,因此您在 DataTemplate 内创建的任何 ElementName 绑定都将在模板内查找具有该名称的另一个元素。

使用 Snoop 查看它,我们还可以看到 RelativeSource 绑定不能直接使用,因为它们位于可视化树的不同部分

在此处输入图像描述

我能想到的解决此问题的唯一方法是将两个复选框绑定到一个共同的祖先,例如父 DataGrid 和使用附加属性或标签属性。示例

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

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

enter image description here

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>
黑白记忆 2024-12-09 06:48:43

我认为常规数据绑定到 HeaderCheckBox 是不可能的,因为 CheckBox 作为 Template 的一部分存在,并且位于不同的分支中VisualTree 的值比 DataGridItems 的值

通常相反:当标题 CheckBox 被选中时,检查所有行 CheckBoxes< /代码>。我这样做的主要原因是因为复选框通常在那里,因此用户可以选中/取消选中它们,如果它们绑定到标题复选框选中状态,则用户无法更改它们。

为了实现这一点,我通常挂接到 Header CheckBox 的 ClickChecked 事件。

如果行 CheckBox.IsChecked 状态绑定到 ViewModel 中的某些内容,我会将事件挂钩到 ViewModel 中的 Command ,并设置 < code>CheckBox.IsChecked 绑定到 true/false,具体取决于标头 CheckBox 状态(通常作为 CommandParameter 传入) )

如果CheckBox.IsChecked 状态不绑定到任何东西,您可以使用常规代码隐藏来循环 DataGrid.Items,使用 ItemContainerGenerator 来获取每个项目的 ItemContainer,找到 CheckBox,然后设置其选中状态。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文