按下删除键时DataGrid失去焦点

时间:2012-01-24 17:20:28

标签: wpf wpfdatagrid

我正在做MVVM,其中DataGrid绑定到ObservableCollection,其中DeleteItemCommand连接到DataGrid.InputBindings,如下所示:

  <DataGrid.InputBindings>
      <KeyBinding Key="Delete" Command="{Binding DeleteItemCommand}" />
  </DataGrid.InputBindings>

当用户点击删除键但网格失去焦点时,将删除项目和行。您必须单击或选项卡到网格,以便它重新获得焦点,然后点击删除删除另一行(非常令人讨厌)。我尝试设置DataGrid.CanUserDeleteRows =“False”,但它没有任何区别。

我用ListView替换了DataGrid,ListView保留了焦点。

这是DataGrid的错误还是我做错了?和平与爱,和平与爱!

4 个答案:

答案 0 :(得分:3)

我通过使用WPF DataGrid的内置功能解决了这个问题。如果底层集合是可编辑的(如果集合专用于此目的没有问题,则可以添加中间集合......),网格会在默认情况下处理删除项目。我避免任何键绑定,只是设置这样的网格:
<DataGrid ItemsSource="{Binding InvoiceItems}" IsReadOnly="False" CanUserDeleteRows="True" CanUserAddRows="False">
ItemsSource集合的类型为BidningCollection&lt;&gt;

在我的ViewModel(我的DataContext)中,我为CollectionChanged事件添加了一个处理程序:
InvoiceItems.CollectionChanged += InvoiceItemsCollectionChanged;

并按照以下方式实施:

private void InvoiceItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action != NotifyCollectionChangedAction.Remove)
            return;
        foreach (var oldItem in e.OldItems)
        {
            //do any other processing necessary
        }
    }

那是因为你可能至少有两种方法可以从你的底层集合中删除一个项目(带有Del键的键盘,一些按钮),也可以在删除一个项目时处理其他一些事情。

答案 1 :(得分:0)

我前段时间碰到了这个问题。不知怎的,这个事件从未被提出过。 尝试 this approach

长话短说,活动PreviewKeyDown会让你到达目的地。

以MVVM友好的方式:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="PreviewKeyDown">
      <i:InvokeCommandAction Command="{Binding DeleteItemCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

答案 2 :(得分:0)

你有没有看过这个答案?

How to bind delete action (in WPF Datagrid) to a command or property in view model

您可能需要:

  1. 确保CanUserDeleteRows =“False”
  2. 确保密钥实际上绑定到datacontext中的指定命令,如下所示:

    <DataGrid.InputBindings>
        <KeyBinding Key="Delete" Command="{Binding DataContext.DeleteEntry, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"/>
    </DataGrid.InputBindings>
    
  3. 以前我在第2项失败并编写了Command =“{Binding DeleteEntry}”,而实际上我应该通过RelativeSource绑定到DataContext.DeleteEntry。

答案 3 :(得分:0)

我不得不以不同于jl.'s answer的方式解决问题,因为它无法进行{em> 之前的任何处理(例如检查访问权限),因为Command可以。尽管可能不够强大,但它确实可以满足您的要求。保持原始代码不变,只需钩住以下SelectionChanged,甚至更好地使用附加属性即可。
因为首先删除一个项目会产生索引为-1的SelectionChanged,所以很容易可靠地猜测何时发生删除并设置一个标志。在第一次使用-1调用之后,又发生了另一个具有最近邻居索引的调用,此时,如果设置了该标志,则可以将当前单元格聚焦为安全的

private int LastItemCount = 0;
private bool ShouldFocusOnSelection = false;
private void FocusOnDeleteDG_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (sender is DataGrid dg)
    {
        if (IsRemovalEvent(dg, e))
        {
            ShouldFocusOnSelection = true;
        }
        else if (ShouldFocusOnSelection)
        {
            dg.FocusCurrentCell();
            ShouldFocusOnSelection = false;
        }
        LastItemCount = dg.Items.Count;
    }
}

其中IsRemovalEvent检查选择事件是否是由于物品去除而产生的:

private static bool IsRemovalEvent(DataGrid dg, SelectionChangedEventArgs e)
{
    return e.RemovedItems.Count > 0
        && e.AddedItems.Count == 0
        && dg.SelectedIndex == -1
        && dg.Items.Count > 0
        && LastItemCount > dg.Items.Count;
}

FocusCurrentCell / GetChildren是您可能already have的辅助方法:

public static void FocusCurrentCell(this DataGrid dataGrid)
{
    var rowIndex = dataGrid.SelectedIndex != -1 ? dataGrid.SelectedIndex : (dataGrid.Items.Count > 0 ? 0 : -1);
    if (!(dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) is DataGridRow row))
    {
        return;
    }

    if (dataGrid.CurrentColumn?.DisplayIndex != null)
    {
        // traverse VisualTree using VisualTreeHelper.GetChild()
        var cell = row.GetChildren<DataGridCell>()
                      .Skip(dataGrid.CurrentColumn.DisplayIndex).FirstOrDefault();
        Keyboard.Focus(cell);
    }
}