DataGridView
Row Indexing
C# Programming
User Interface
Windows Forms

Index of Currently Selected Row in DataGridView

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Windows Forms, the DataGridView control provides several ways to get the index of the currently selected row. The most common approaches are CurrentCell.RowIndex (returns the row of the active cell), SelectedRows[0].Index (returns the first selected row when full-row selection is enabled), and CurrentRow.Index (returns the row containing the current cell). The right property depends on your selection mode and whether you need to handle multi-row selection.

Using CurrentCell.RowIndex

csharp
1// Get the row index of the currently active cell
2if (dataGridView1.CurrentCell != null)
3{
4    int rowIndex = dataGridView1.CurrentCell.RowIndex;
5    Console.WriteLine($"Current row index: {rowIndex}");
6}

CurrentCell always points to the cell that has focus, regardless of the selection mode. This works even when SelectionMode is set to CellSelect.

Using SelectedRows

csharp
1// Get the index of the first selected row (requires FullRowSelect)
2if (dataGridView1.SelectedRows.Count > 0)
3{
4    int rowIndex = dataGridView1.SelectedRows[0].Index;
5    Console.WriteLine($"Selected row index: {rowIndex}");
6
7    // Access cell values from the selected row
8    string name = dataGridView1.SelectedRows[0].Cells["Name"].Value?.ToString();
9    Console.WriteLine($"Name: {name}");
10}

SelectedRows is populated only when SelectionMode is FullRowSelect or RowHeaderSelect. With CellSelect, the collection is empty even when cells are selected.

Using CurrentRow

csharp
1// Get the row containing the current cell
2if (dataGridView1.CurrentRow != null)
3{
4    int rowIndex = dataGridView1.CurrentRow.Index;
5    Console.WriteLine($"Current row index: {rowIndex}");
6
7    // Check if the row is a new row (for adding)
8    bool isNewRow = dataGridView1.CurrentRow.IsNewRow;
9}

CurrentRow returns the DataGridViewRow that contains CurrentCell. It is null only when the grid has no rows.

Handling Multiple Selected Rows

csharp
1// Enable multi-select
2dataGridView1.MultiSelect = true;
3dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
4
5// Get all selected row indices
6if (dataGridView1.SelectedRows.Count > 0)
7{
8    foreach (DataGridViewRow row in dataGridView1.SelectedRows)
9    {
10        Console.WriteLine($"Row {row.Index}: {row.Cells["Name"].Value}");
11    }
12
13    // Get indices as a list
14    List<int> indices = dataGridView1.SelectedRows
15        .Cast<DataGridViewRow>()
16        .Select(r => r.Index)
17        .OrderBy(i => i)
18        .ToList();
19
20    Console.WriteLine($"Selected indices: {string.Join(", ", indices)}");
21}

Note that SelectedRows returns rows in the order they were selected (last selected first), not in display order. Sort explicitly if order matters.

Event-Based Row Selection

csharp
1public Form1()
2{
3    InitializeComponent();
4    dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
5    dataGridView1.SelectionChanged += DataGridView1_SelectionChanged;
6    dataGridView1.CellClick += DataGridView1_CellClick;
7}
8
9private void DataGridView1_SelectionChanged(object sender, EventArgs e)
10{
11    if (dataGridView1.CurrentRow != null)
12    {
13        int rowIndex = dataGridView1.CurrentRow.Index;
14        labelStatus.Text = $"Row {rowIndex} selected";
15    }
16}
17
18private void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
19{
20    if (e.RowIndex >= 0)  // Ignore header clicks
21    {
22        string value = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value?.ToString();
23        Console.WriteLine($"Clicked row {e.RowIndex}, column {e.ColumnIndex}: {value}");
24    }
25}

Getting Row Data from the Underlying DataSource

csharp
1// When bound to a DataTable
2if (dataGridView1.CurrentRow != null)
3{
4    DataRowView rowView = dataGridView1.CurrentRow.DataBoundItem as DataRowView;
5    if (rowView != null)
6    {
7        DataRow dataRow = rowView.Row;
8        int id = (int)dataRow["Id"];
9        string name = (string)dataRow["Name"];
10        Console.WriteLine($"ID: {id}, Name: {name}");
11    }
12}
13
14// When bound to a List<T>
15if (dataGridView1.CurrentRow != null)
16{
17    var item = dataGridView1.CurrentRow.DataBoundItem as Employee;
18    if (item != null)
19    {
20        Console.WriteLine($"Employee: {item.Name}, Dept: {item.Department}");
21    }
22}

Comparison of Approaches

PropertyReturnsSelection ModeNull When
CurrentCell.RowIndexActive cell's rowAnyNo current cell
SelectedRows[0].IndexFirst selected rowFullRowSelect / RowHeaderSelectNo rows selected
CurrentRow.IndexRow of current cellAnyNo rows in grid
SelectedCells[0].RowIndexFirst selected cell's rowAnyNo cells selected

Common Pitfalls

  • Accessing SelectedRows with CellSelect mode: SelectedRows is empty when SelectionMode is CellSelect. Switch to FullRowSelect or use CurrentCell.RowIndex instead.
  • Not checking for null before accessing properties: CurrentCell, CurrentRow, and SelectedRows[0] can be null or out of bounds when the grid is empty or nothing is selected. Always check != null or .Count > 0 before accessing.
  • Assuming SelectedRows order matches display order: SelectedRows returns rows in reverse selection order (most recently selected first). Sort by .Index if you need display order.
  • Using SelectedRows after deleting rows: After removing a row, the SelectedRows collection updates but indices shift. Re-query the selection after any modification to the grid's data source.
  • Clicking column headers triggering row selection: The CellClick event fires with e.RowIndex == -1 when a column header is clicked. Always guard with if (e.RowIndex >= 0) to avoid ArgumentOutOfRangeException.

Summary

  • Use CurrentCell.RowIndex for the active cell's row — works with any selection mode
  • Use SelectedRows[0].Index for full-row selection — requires FullRowSelect mode
  • Use CurrentRow.Index for the row containing the current cell
  • Always null-check before accessing selection properties
  • Handle SelectionChanged event for real-time row tracking
  • Access the underlying data with CurrentRow.DataBoundItem for typed access to bound objects

Course illustration
Course illustration