4

I'm doing MVVM where a DataGrid is bound to an ObservableCollection with a DeleteItemCommand hooked up to the DataGrid.InputBindings as follows:

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

The item and row are removed when the user hits the delete key but the grid looses focus. You have to click or tab to the grid so it regains focus before hitting Delete to remove another row (pretty freaking annoying). I tried setting the DataGrid.CanUserDeleteRows="False" but it doesn't make any difference.

I replaced the DataGrid with a ListView and the ListView retains focus.

Is this a bug with the DataGrid or am I doing something wrong? Peace and love, peace and love!

H.B.
  • 166,899
  • 29
  • 327
  • 400
KornMuffin
  • 2,887
  • 3
  • 32
  • 48

4 Answers4

4

I solved this by using the built in functionality of WPF DataGrid. The grid handles removing items by default if the underlying collection is editable (if the collection is dedicated to this purpose that's no problem, otherwise an intermediate collection can be added...). I avoided any key bindings and just set up the grid like this:
<DataGrid ItemsSource="{Binding InvoiceItems}" IsReadOnly="False" CanUserDeleteRows="True" CanUserAddRows="False">
The ItemsSource collection is of type BidningCollection<>

In my ViewModel (my DataContext) I add a handler for CollectionChanged event:
InvoiceItems.CollectionChanged += InvoiceItemsCollectionChanged;

And implement it like this:

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

That's because you will probably be having at least two ways of removing an item from you underlying collection (keyboard with Del key, some button) and maybe some other things to take care of when an item is deleted.

jl.
  • 752
  • 6
  • 19
0

Did you check this answer yet?

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

You probably need to:

  1. make sure CanUserDeleteRows="False"
  2. make sure the key is actually bound to the specified command in your datacontext, like this:

    <DataGrid.InputBindings>
        <KeyBinding Key="Delete" Command="{Binding DataContext.DeleteEntry, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"/>
    </DataGrid.InputBindings>
    

Previously I fail at item #2 and wrote Command="{Binding DeleteEntry}", while in fact I should bind to DataContext.DeleteEntry by RelativeSource.

Community
  • 1
  • 1
RainCast
  • 4,134
  • 7
  • 33
  • 47
  • The answer you linked is more about deleting a row vs. keeping focus on the grid (which was my issue). I'd hit the delete key, the row would disappear but then my DataGrid would no longer have focus. I ended up trying a bunch of different things and ended up using this answer (http://stackoverflow.com/a/17127664/512365) to get the focus on the grid so I move between rows using the arrow keys w/o having the click the grid with the mouse after each delete. – KornMuffin Mar 10 '15 at 13:14
0

I had to solve the issue in a different way than the jl.'s answer, because it could not do any processing before (e.g. checking access) deletion happen as a Command could. While perhaps not as robust, it does exactly what you asked about. Keeping your original code unchanged, just hook the following SelectionChanged, or even better use attached property.
Because deleting an item first produces SelectionChanged with index -1 it is easy enough to reliably guess when deletion happen and set a flag. After the first invocation with -1 another one with nearest neighbor index happens, at this point if the flag was set, it is safe to focus the current cell:

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;
    }
}

where IsRemovalEvent checks if the selection event was produced by item removal:

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;
}

and the FocusCurrentCell/GetChildren are helper methods you probably 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);
    }
}
wondra
  • 3,271
  • 3
  • 29
  • 48
0

I bumped on that some time ago. Somehow this event is never raised. Try this approach.

Long story short, event PreviewKeyDown will get you where you want.

And in MVVM-friendly manner:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="PreviewKeyDown">
      <i:InvokeCommandAction Command="{Binding DeleteItemCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>
Community
  • 1
  • 1
narcos
  • 41
  • 3
  • Thanks narcos for the response. I greatly appreciate you taking your time to answer. I tried this and the item gets deleted but this issue with the DataGrid loosing focus still rears it's ugly head. Also, when I press any key while in the grid the DeleteItemCommand fires. – KornMuffin Mar 06 '12 at 15:50
  • You can get the actual key pressed by examining the event arguments as described [here](http://sonyarouje.com/tag/caliburn-micro/). – jl. May 03 '12 at 16:43