You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:58:56