Search code examples
c#wpfdatagrid

Reset row numbers after deleting an item from datagrid


Suppose I have a dataGrid as shown below: (at-runtime)

enter image description here

After deleting row no.4:

enter image description here

After adding two more rows to the datagrid:

enter image description here

Notice that in the first picture the row numbering is good. When I delete an item from the Datagrid, that row number is missing after deletion as shown in image 2. In image 3 you can see that I have aded two new rows but the row number 11 is repeated twice.

Here I have got numbering using LoadingRow event as follows :

private void maindg_LoadingRow_1(object sender, DataGridRowEventArgs e)
{
    e.Row.Header = ((e.Row.GetIndex()) + 1).ToString(); 
}

After getting the problems as shown in image2 and image3 I understood that I should manually renumber the dataGridRowHeaders. So, I tried the below code in PreviewKeyDown event of the DaaGrid :

if (e.Key == Key.Delete)
{
    DataGridRow dgr = (DataGridRow)(maindg.ItemContainerGenerator.ContainerFromIndex(maindg.SelectedIndex));

    if (!(dgr.IsEditing))
    {
        foreach (var item in maindg.Items)
        {
            dgr.Header = ((dgr.GetIndex()) + 1).ToString();
        }
    }
}

When I run the program again I could not see any changes in the output. So I thought that the DataGrid is renumbered just before deletion of the row, so I moved that code to PreviewKeyUp. There I got IndexOutOfRangeException as Selected index is reset to -1.

What can I do to get the correct numbering?

Solution with ShowRowNumbers Property so that developers can turn on/off the Row Numbers :

public class ExtendedDataGrid : DataGrid
{

    public static bool GetShowRowNumbers(DependencyObject obj)
    {
        return (bool)obj.GetValue(ShowRowNumbersProperty);
    }

    public static void SetShowRowNumbers(DependencyObject obj, bool value)
    {
        obj.SetValue(ShowRowNumbersProperty, value);
    }
    public static readonly DependencyProperty ShowRowNumbersProperty =
        DependencyProperty.RegisterAttached("ShowRowNumbers", typeof(bool), typeof(ExtendedDataGrid), new UIPropertyMetadata(false));

    public ExtendedDataGrid()
    {

        LoadingRow += (sender, args) =>
            {
                if (GetShowRowNumbers(this))
                    RefreshRowNumber(args.Row);
            };
    }

    protected override void OnExecutedDelete(ExecutedRoutedEventArgs e)
    {
        base.OnExecutedDelete(e);

        if (GetShowRowNumbers(this))
            RefreshRowNumbers();
    }

    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);

        if (GetShowRowNumbers(this))
            RefreshRowNumbers();
    }

    private void RefreshRowNumbers()
    {
        for (int i = 0; i < Items.Count; i++)
            RefreshRowNumber((DataGridRow)ItemContainerGenerator.ContainerFromIndex(i));
    }

    private void RefreshRowNumber(DataGridRow row)
    {   
        if(row != null)
            row.Header = ((row.GetIndex()) + 1).ToString();
    }
}

In XAML:

<DataGrid ......
          ShowRowNumbers="True".....>

Solution

  • It seems like a more precise approach would be to handle OnExecutedDeleteOnItemsChanged rather than trying to sync up with the delete key-press. Maybe you could subclass DataGrid and refresh the rows this way:

    public class ExtendedDataGrid : DataGrid
    {
        public ExtendedDataGrid()
        {
            LoadingRow += (sender, args) => RefreshRowNumber(args.Row);
        }
    
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
            RefreshRowNumbers();
        }
        private void RefreshRowNumbers()
        {
            for (int i = 0; i < Items.Count; i++)
                RefreshRowNumber((DataGridRow)ItemContainerGenerator.ContainerFromIndex(i));
        }
    
        private void RefreshRowNumber(DataGridRow row)
        {
            if (row != null)
                row.Header = ((row.GetIndex()) + 1).ToString();
        }
    }