在 WPF 中获取 CheckBox 单元格的布尔值

Getting the boolean value of a CheckBox cell in WPF

我有一个 DataGrid,在一行的每个第一个单元格中都有一个 Checkbox。如果选中该单元格中的 Checkbox,我想遍历我的 DataGrid 行并获取这些行。

这是我检查 DataGrid:

的代码

根据 Ilan 的回答编辑:

Private Sub ApproveBtn_Clicked(sender As Object, e As RoutedEventArgs)


    For i As Integer = 0 To TimeSheetAppDGrid.Items.Count - 1

        TimeSheetAppDGrid.SelectedItem = TimeSheetAppDGrid.Items(i)
        Dim row As Row = DirectCast(TimeSheetAppDGrid.SelectedItems(0), Row)

        Dim mycheckbox = TryCast(TimeSheetAppDGrid.Columns(0).GetCellContent(TimeSheetAppDGrid.Items(i)), ContentPresenter)
        If mycheckbox Is Nothing Then
            Return
        End If
        Dim checkBox = GetAllVisualChildren(mycheckbox).OfType(Of CheckBox)().FirstOrDefault(Function(box) box.Name = "chkSelectAll")
        If checkBox Is Nothing Then
            Return
        End If

        Dim isChecked = checkBox.Checked

        If isChecked = True Then

            MsgBox(row.Mark)

        End If

    Next


End Sub

下面的代码似乎没有被 VB.NET 接受,导致编译错误“Value of type 'FrameworkElement' cannot be converted to 'CheckBox'.” 在网上搜索后,几乎所有的答案都包含这段代码,但是在 C# 中,所以我转换了它:

Dim mycheckbox As CheckBox = TryCast(TimeSheetAppDGrid.Columns(0).GetCellContent(TimeSheetAppDGrid.SelectedItems(0)), CheckBox)

这个 link 是我获得代码的地方。下面是来自 C# 的原始代码行:

CheckBox mycheckbox = myGrid.Columns[5].GetCellContent(myGrid.Items[i]) as CheckBox;

这是我的 DataGrid xaml:

<DataGrid Name ="TimeSheetAppDGrid" Margin="16,200,48,0"  CanUserSortColumns="False" CanUserReorderColumns="False" CanUserAddRows="False" AutoGenerateColumns="False"  SelectionUnit="FullRow"
                  materialDesign:DataGridAssist.CellPadding="13 8 8 8" materialDesign:DataGridAssist.ColumnHeaderPadding="8"  IsReadOnly="True" SelectionMode="Single"  >

    <DataGrid.Resources>
        <Style TargetType="{x:Type DataGridRow}"  BasedOn="{StaticResource MaterialDesignDataGridRow}">
            <EventSetter Event="MouseDoubleClick" Handler="Row_DoubleClick"/>
        </Style>
    </DataGrid.Resources>

    <DataGrid.Columns>

        <DataGridTemplateColumn>
            <DataGridTemplateColumn.Header>
                <CheckBox Name="AllChkBox" />
            </DataGridTemplateColumn.Header>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox Name="chkSelectAll" Margin="3.5 0 0 0"
              IsChecked="{Binding IsChecked, ElementName=AllChkBox,
                                  Mode=OneWay}"/>
                </DataTemplate> 
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <DataGridTextColumn Binding="{Binding Path=Mark}" Header="Mark" Width="120" MaxWidth="120"
         EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnEditingStyle}"/>
        <materialDesign:MaterialDataGridTextColumn Binding="{Binding Path=ProjectCode}" Header="Project Code" Width="130" MaxWidth="130"
         EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnPopupEditingStyle}"/>
        <materialDesign:MaterialDataGridTextColumn Binding="{Binding Path=Project}" Header="Project" Width="250" MaxWidth="250"
         EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnPopupEditingStyle}"/>
        <materialDesign:MaterialDataGridTextColumn Binding="{Binding Path=PM}" Header="Project Manager" Width="230" MaxWidth="230"
         EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnPopupEditingStyle}"/>
        <materialDesign:MaterialDataGridTextColumn Binding="{Binding Path=Activity}" Header="Activity" Width="600" MaxWidth="600"
         EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnPopupEditingStyle}"/>
        <materialDesign:MaterialDataGridTextColumn Binding="{Binding Path=Today}" Header="Date" Width="200" MaxWidth="200"
         EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnPopupEditingStyle}"/>
        <materialDesign:MaterialDataGridTextColumn Binding="{Binding Path=Regular}" Header="Regular" Width="130" MaxWidth="130"
         EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnPopupEditingStyle}"/>
        <materialDesign:MaterialDataGridTextColumn Binding="{Binding Path=Overtime}" Header="Overtime" Width="130" MaxWidth="130"                                            
         EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnPopupEditingStyle}">
            <DataGridTextColumn.HeaderStyle>
                <Style TargetType="{x:Type DataGridColumnHeader}" BasedOn="{StaticResource MaterialDesignDataGridColumnHeader}">
                    <Setter Property="HorizontalAlignment" Value="Left" />
                </Style>
            </DataGridTextColumn.HeaderStyle>
            <DataGridTextColumn.ElementStyle>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="HorizontalAlignment" Value="Left" />
                </Style>
            </DataGridTextColumn.ElementStyle>
        </materialDesign:MaterialDataGridTextColumn>
    </DataGrid.Columns>

    <DataGrid.CellStyle>
        <Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource MaterialDesignDataGridCell}">
            <Setter Property="BorderThickness" Value="0"/>
        </Style>
    </DataGrid.CellStyle>
</DataGrid>

有什么建议吗?

我认为您采用的解决方案存在一些概念错误:

CheckBox mycheckbox = myGrid.Columns[5].GetCellContent(myGrid.Items[i]) as CheckBox;

我已经检查过了,当我们使用 DataGridTemplateColumn.CellTemplate 时,就像您的情况一样,接受的值 (mycheckbox) 是 ContentPresenter,因为单元格后面有 DataTemplate(因为您正在使用数据模板)。这就是代码实际上不起作用的原因。

我建议您的解决方案是从 ContentPresenter 获取复选框对象(代码在 C# 中,您必须将其转换为 VB),因为它是 ContentPresenter 的视觉子对象(您可以在 Snoop 中找到它)。

更新这里是GetAllVisualChildren代码:

public static class VisualTreeHelperExtensions
{
    public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
    {
        while (true)
        {
            //get parent item
            DependencyObject parentObject = VisualTreeHelper.GetParent(child);

            //we've reached the end of the tree
            if (parentObject == null) return null;

            //check if the parent matches the type we're looking for
            T parent = parentObject as T;
            if (parent != null)
                return parent;
            child = parentObject;
        }
    }

    public static List<DependencyObject> GetAllVisualChildren(this DependencyObject parent)
    {
        var resultedList = new List<DependencyObject>();
        var visualQueue = new Queue<DependencyObject>();
        visualQueue.Enqueue(parent);

        do
        {
            var depObj = visualQueue.Dequeue();
            var childrenCount = VisualTreeHelper.GetChildrenCount(depObj);

            for (int i = 0; i < childrenCount; i++)
            {
                var v = VisualTreeHelper.GetChild(depObj, i);
                visualQueue.Enqueue(v);
            }

            resultedList.Add(depObj);
        } while (visualQueue.Count > 0);

        resultedList.RemoveAt(0);
        return resultedList;
    }

}

做接下来的事情以将 VisualTreeHelperExtensions class 导入到 VB

  1. 创建 C# class 库作为解决方案的一部分。
  2. 将 VisualTreeHelperExtensions class 添加到新创建的项目中。
  3. 编译您的 C# class 库。
  4. 将新创建(和编译)的 class 库作为参考 (dll) 添加到您的 vb.net 项目中。

更新 #3 - 更改了 vb 代码

Dim mycheckbox = TryCast(TimeSheetAppDGrid.Columns(0).GetCellContent(TimeSheetAppDGrid.Items(0)), ContentPresenter)
    If mycheckbox Is Nothing Then
        Return
    End If
    Dim checkBox = mycheckbox.GetAllVisualChildren().OfType(Of CheckBox)().FirstOrDefault(Function(box) box.Name = "chkSelectAll")
    If checkBox Is Nothing Then
        Return
    End If

    Dim isChecked = checkBox.IsChecked

    If isChecked = True Then

        MsgBox("Checked")

    End If

VB.net 复选框示例

  1. CheckBox Control WPF In VB.NET.

  2. CheckBox Class in .Net Framework(no any restriction to vb)

因为 .net CheckBox 有它的 IsChecked 布尔状态表示器。

此致。

编辑

好的,这里有一种更精确的方法来实现您的需要,而无需搜索可视化树以查找复选框(同样在 c# 中,但希望翻译起来不难)。 首先,我创建了一个 Student class 来绑定到实现 INotifyPropertyChanged :

    public class Student:INotifyPropertyChanged
    {
        private bool _isChecked;

        public bool IsChecked
        {
            get { return _isChecked; }
            set { _isChecked = value;
                OnPropertyChanged("IsChecked");
            }
        }
        public string Mark { get; set; }
        public string ProjectCode { get; set; }
        public string Project { get; set; }
        public string PM { get; set; }
        public string Activity { get; set; }
        public DateTime Today { get; set; }
        public string Regular { get; set; }
        public string Overtime { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

然后我用测试数据在我的数据网格中添加一行:

            TimeSheetAppDGrid.ItemsSource = new List<Student>()
            {
                new Student { ProjectCode="Project1",Mark = "A",Today = DateTime.Today}
            };

在数据模板的 XAML 中,我已将绑定更改为 而不是 绑定到 header 复选框,但绑定到学生 IsChecked 属性 并且我在 header 单元格中为 AllChkBox 复选框添加了一个点击处理程序:

  <DataGridTemplateColumn>
                    <DataGridTemplateColumn.Header>
                        <CheckBox Click="AllChkBox_OnClick" Name="AllChkBox" />
                    </DataGridTemplateColumn.Header>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox  Name="chkSelectAll" Margin="3.5 0 0 0"
              IsChecked="{Binding Path=IsChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

对于 AllChkBox 单击处理程序 (AllChkBox_OnClick),我遍历每个数据源(即学生)并将 IsChecked 属性 切换为 AllChkBox 检查值:

        private void AllChkBox_OnClick(object sender, RoutedEventArgs e)
        {

            foreach (var s in TimeSheetAppDGrid.ItemsSource)
            {
                Student st = s as Student;

                st.IsChecked = (bool) AllChkBox.IsChecked;
            }

        }

现在可以询问每个项目源(即学生)的 IsChecked 属性(绑定到相应的复选框单元格),而无需四处寻找复选框(请原谅双关语)即

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    foreach (var source in TimeSheetAppDGrid.ItemsSource)
    {
        Student st = source as Student;

        if (st.IsChecked)
            MessageBox.Show("Is Checked");
    }
}

您必须查看使用 Snoop 生成的可视化树,以便您的转换正常工作。

最简单的方法是根据 CheckBoxChecked/Unchecked 事件不断更新 Dictionary<int, DataGridRow>

抱歉,代码是用 C# 编写的。

XAML :

<CheckBox Name="AllChkBox" Checked="AllChkBox_Checked" Unchecked="AllChkBox_Unchecked" />

<CheckBox Name="chkSelectAll" Margin="3.5 0 0 0" 
          Checked="chkSelectAll_Checked" Unchecked="chkSelectAll_Unchecked" 
          IsChecked="{Binding IsChecked, ElementName=AllChkBox, Mode=OneWay}"/>

代码:

    Dictionary<int, DataGridRow> rows = new Dictionary<int, DataGridRow>();

    private void chkSelectAll_Checked(object sender, RoutedEventArgs e)
    {
        var cur_item = DGrid.CurrentItem;
        int cur_index = DGrid.Items.IndexOf(cur_item);

        if (cur_index == -1) // header checkbox is checked
            return;

        DataGridRow row = (DataGridRow)DGrid.ItemContainerGenerator.ContainerFromItem(cur_item);
        rows.Add(DGrid.Items.IndexOf(cur_item), row);            
    }

    private void chkSelectAll_Unchecked(object sender, RoutedEventArgs e)
    {
        var cur_item = DGrid.CurrentItem;
        rows.Remove(DGrid.Items.IndexOf(cur_item));
    }

    private void AllChkBox_Checked(object sender, RoutedEventArgs e)
    {
        foreach (var item in DGrid.Items)
        {
            DataGridRow row = (DataGridRow)DGrid.ItemContainerGenerator.ContainerFromItem(item);
            rows.Add(DGrid.Items.IndexOf(item), row);
        }
    }

    private void AllChkBox_Unchecked(object sender, RoutedEventArgs e)
    {
        rows.Clear();
    }