DataGrid设为Cell选择模式时,如何在行头显示选中图标?
当把DataGridSelectionUnit设为Cell时,原来基于DataGridRow.IsSelected的绑定会失效——因为此时选中的是单元格,而非整行,DataGridRow的IsSelected属性不会被自动设置为true。要实现该行任意单元格被选中时,行头图标就显示的需求,核心是判断当前行是否存在被选中的单元格,而非依赖单个单元格或行的选中状态。
推荐解决方案:MultiBinding + 多值转换器
这种方式能监听DataGrid的选中单元格集合变化,实时判断当前行是否有选中项,保证图标的显示状态准确。
1. 创建多值转换器
这个转换器会接收当前行和DataGrid的选中单元格集合,判断该行是否存在选中的单元格:
public class RowHasSelectedCellMultiConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { // 校验输入参数 if (values.Length != 2 || values[0] is not DataGridRow row || values[1] is not IList<DataGridCellInfo> selectedCells) { return Visibility.Collapsed; } // 判断当前行是否有单元格被选中 return selectedCells.Any(cellInfo => cellInfo.Item == row.Item) ? Visibility.Visible : Visibility.Collapsed; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { // 不需要反向转换,抛出异常即可 throw new NotImplementedException("反向转换未实现"); } }
2. 替换原有绑定为MultiBinding
不再绑定单个单元格的IsSelected,而是同时绑定当前DataGridRow和DataGrid的SelectedCells集合:
// 创建多值绑定 var multiBinding = new MultiBinding(); multiBinding.Converter = new RowHasSelectedCellMultiConverter(); // 绑定1:当前DataGridRow multiBinding.Bindings.Add(new Binding() { Path = new PropertyPath("."), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGridRow), 1) }); // 绑定2:DataGrid的SelectedCells集合 multiBinding.Bindings.Add(new Binding() { Path = new PropertyPath("SelectedCells"), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGrid), 1) }); // 给图标元素设置绑定 elemFactory.SetBinding(VisibilityProperty, multiBinding);
为什么原有绑定不生效?
你之前绑定的是单个DataGridCell的IsSelected属性,这意味着只有当前绑定的这个单元格被选中时,图标才会显示。但如果该行其他单元格被选中,这个绑定的单元格未选中,图标就会隐藏,不符合“任意单元格选中即显示”的需求。
备选方案:单值转换器(需额外处理通知)
如果不想用多值绑定,也可以用单值转换器,通过视觉树找到当前行所属的DataGrid,再检查选中单元格集合:
public class RowHasSelectedCellConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is not DataGridRow row) return Visibility.Collapsed; // 找到当前行所在的DataGrid var dataGrid = FindVisualParent<DataGrid>(row); return dataGrid?.SelectedCells.Any(cellInfo => cellInfo.Item == row.Item) == true ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } // 视觉树查找父元素的工具方法 private T FindVisualParent<T>(DependencyObject child) where T : DependencyObject { while (child != null) { if (child is T parent) return parent; child = VisualTreeHelper.GetParent(child); } return null; } }
绑定代码:
Binding bindingVisibility = new Binding() { Path = new PropertyPath("."), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGridRow), 1 ), UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, Converter = new RowHasSelectedCellConverter() }; elemFactory.SetBinding(VisibilityProperty, bindingVisibility);
⚠️ 注意:这种方式下,当SelectedCells集合变化时,绑定不会自动触发更新(因为绑定源是DataGridRow,它的属性没有变化)。需要额外在DataGrid的SelectedCellsChanged事件中,手动触发绑定更新,或者给DataGridRow添加附加属性来监听选中变化,不如多值绑定优雅。
内容的提问来源于stack exchange,提问作者user1686688

