WPF DataGrid:如何禁用特定行选择/焦点并让上下键跳过该行?
解决DataGrid禁用指定行选择/焦点并让方向键跳过的方案
我之前也碰到过完全一样的需求,只给子元素设置Focusable=false确实只能阻止焦点进入,但没法让方向键直接跳过该行——得从阻止行选中/聚焦和拦截键盘导航两个维度一起处理,给你分享个亲测可行的方案:
一、先阻止目标行被选中和获得焦点
首先我们需要给要禁用的行做个标记(比如用Tag属性,或者绑定ViewModel里的布尔属性更符合MVVM),然后通过事件拦截来阻止选中和聚焦:
1. XAML中设置行样式和事件
<DataGrid x:Name="MyDataGrid" PreviewKeyDown="MyDataGrid_PreviewKeyDown"> <DataGrid.RowStyle> <Style TargetType="{x:Type DataGridRow}"> <!-- 绑定Focusable到你的禁用标记,或者直接根据Tag判断 --> <Setter Property="Focusable" Value="{Binding IsRowEnabled}"/> <!-- 拦截鼠标点击,阻止选中 --> <EventSetter Event="PreviewMouseDown" Handler="DataGridRow_PreviewMouseDown"/> <!-- 拦截键盘焦点,阻止焦点进入该行 --> <EventSetter Event="PreviewGotKeyboardFocus" Handler="DataGridRow_PreviewGotKeyboardFocus"/> </Style> </DataGrid.RowStyle> <!-- 你的列定义... --> </DataGrid>
2. 后台事件处理代码
private void DataGridRow_PreviewMouseDown(object sender, MouseButtonEventArgs e) { var targetRow = sender as DataGridRow; // 这里判断是否是要禁用的行,比如检查Tag或者ViewModel属性 if (targetRow != null && targetRow.Tag?.ToString() == "DisabledRow") { e.Handled = true; // 阻止事件传递,避免行被选中 } } private void DataGridRow_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { var targetRow = sender as DataGridRow; if (targetRow != null && targetRow.Tag?.ToString() == "DisabledRow") { e.Handled = true; // 阻止焦点进入该行 } }
二、处理上下方向键,直接跳过禁用行
接下来是核心:拦截DataGrid的键盘事件,当按下上下键时,自动跳过禁用行,定位到下一个可选中的行:
private void MyDataGrid_PreviewKeyDown(object sender, KeyEventArgs e) { var dataGrid = sender as DataGrid; if (dataGrid == null) return; // 只处理上下方向键 if (e.Key != Key.Down && e.Key != Key.Up) return; var currentSelectedRow = dataGrid.ItemContainerGenerator.ContainerFromItem(dataGrid.SelectedItem) as DataGridRow; if (currentSelectedRow == null) return; int currentIndex = dataGrid.Items.IndexOf(currentSelectedRow.DataContext); int targetIndex = e.Key == Key.Down ? currentIndex + 1 : currentIndex - 1; // 循环查找下一个可选中的行,跳过禁用行 while (targetIndex >= 0 && targetIndex < dataGrid.Items.Count) { var targetRow = dataGrid.ItemContainerGenerator.ContainerFromIndex(targetIndex) as DataGridRow; // 检查目标行是否可用(不是禁用行) if (targetRow != null && targetRow.Tag?.ToString() != "DisabledRow") { // 选中并聚焦目标行 dataGrid.SelectedItem = dataGrid.Items[targetIndex]; dataGrid.ScrollIntoView(dataGrid.SelectedItem); Keyboard.Focus(targetRow); e.Handled = true; // 阻止默认的导航行为 break; } // 继续查找下一个/上一个 targetIndex += e.Key == Key.Down ? 1 : -1; } }
额外提示(MVVM场景优化)
如果你的项目用MVVM,建议不要用Tag,而是给数据模型加一个IsRowDisabled布尔属性,然后在XAML里绑定判断:
<!-- 行样式里的判断 --> <Style.Triggers> <DataTrigger Binding="{Binding IsRowDisabled}" Value="True"> <Setter Property="Focusable" Value="False"/> </DataTrigger> </Style.Triggers>
后台事件里也改成检查(targetRow.DataContext as YourModel)?.IsRowDisabled == true,这样更优雅。
另外如果你的DataGrid开启了虚拟化(默认是开启的),ItemContainerGenerator.ContainerFromIndex可能返回null,这时候可以先调用dataGrid.ScrollIntoView(dataGrid.Items[targetIndex])强制生成行容器,再获取行对象。
内容的提问来源于stack exchange,提问作者daVinci
相关产品推荐
相关产品推荐

