Skip to content

Fix issue 15093: Removing multiple rows while selecting the last new-item row in DataGridView leads to exception - #15117

Open
SimonZhao888 wants to merge 3 commits into
dotnet:mainfrom
SimonZhao888:Fix_Issue_15093
Open

SimonZhao888 wants to merge 3 commits into
dotnet:mainfrom
SimonZhao888:Fix_Issue_15093

Conversation

@SimonZhao888

@SimonZhao888 SimonZhao888 commented Sep 16, 2026

Copy link
Copy Markdown
Member

Fixes #15093

Root Cause

When deleting a selected row that includes the final "new item" row (the asterisk row), the bound data source may trigger an additional ItemAdded event. At this point, the number of items in the data source exceeds the number of bound rows in DataGridView.Rows, causing the row collection and the CurrencyManager state to fall out of sync. If the asterisk row is clicked again, the existing code treats this state as invalid and throws an InvalidOperationException.

Proposed changes

  • In ProcessDeleteKey, strengthen the handling of the "selection includes a new row" scenario: only invoke DeleteRow(NewRowIndex) to clean up the pending new-row transaction when a valid AddNew transaction exists and the index is valid.
  • Add boundary checks in DataGridViewDataConnection.GetError(int rowIndex) and DataGridViewDataConnection.GetError(int boundColumnIndex, int columnIndex, int rowIndex):
    • Return string.Empty immediately if CurrencyManager is null or rowIndex >= CurrencyManager.Count to prevent out-of-bounds access.
  • Preserve the semantics of existing exception-handling branches; avoid replacing the core logic with a crude refresh mechanism that would introduce "blank ghost rows."

Customer Impact

  • The default DataGridView error dialog no longer appears when users perform multiple batch deletions to clear the grid and then click "Add New Row" to re-enter data.
  • Complex interactions involving row deletion are now more stable, reducing exceptions and state inconsistencies caused by residual transaction data.
  • The data entry workflow is smoother, preventing interruptions caused by out-of-bounds exceptions.

Regression?

  • No

Risk

  • Mini

Screenshots

Before

Recording.2026-09-16.095825.mp4

After

Recording.2026-09-16.152747.mp4

Test methodology

  • Manual

Test environment(s)

  • 11.0.100-rc.1.26420.103
Microsoft Reviewers: Open in CodeFlow

…item row in DataGridView leads to exception

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A moderate issue and a regression-test gap remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Fixes DataGridView synchronization failures when deleting rows involving the final new-item row.

Changes:

  • Rebuilds bound rows for out-of-sync ItemAdded events.
  • Restores new-item row synchronization.
File summaries
File Summary Findings
src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.DataConnection.cs Handles row resynchronization after affected ItemAdded events. Moderate (1 vote): Preserve or recompute the initiating row/current position across refresh. Nit (3 votes): Add a regression test for the reported sequence.
Review details

Suppressed comments (1)

src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.DataConnection.cs:607

  • Rebuilding here changes NewRowIndex while OnRowEnter is still processing the cell that initiated AddNew. RefreshRows clears and repopulates the rows, then PushAllowUserToAddRows appends the new-item row, so the original rowIndex (the old asterisk row) now points at the phantom item. When OnRowEnter resumes at DataGridView.Methods.cs:17773, DataConnection.OnRowEnter can move the currency position back to that old index, leaving focus/editing on the phantom row instead of the newly added item. Preserve or recompute the initiating row/current position across this refresh.
                                _owner.RefreshRows(scrollIntoView: !_owner.InSortOperation);
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@codecov

codecov Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 37.24166%. Comparing base (60135a0) to head (f83d7a9).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@              Coverage Diff              @@
##                main      #15117   +/-   ##
=============================================
  Coverage   37.24166%   37.24166%           
=============================================
  Files            246         246           
  Lines           9774        9774           
  Branches        1029        1029           
=============================================
  Hits            3640        3640           
  Misses          5970        5970           
  Partials         164         164           
Flag Coverage Δ
Debug 37.24166% <ø> (ø)
production 39.36526% <ø> (ø)
test 20.64923% <ø> (ø)
unit 39.36526% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dotnet-policy-service dotnet-policy-service Bot added the draft draft PR label Sep 16, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Moderate issues remain in exception handling and regression-test coverage.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

src/System.Windows.Forms/System/Windows/Forms/Controls/DataGridView/DataGridView.Methods.cs:20008

  • The new test never enters the * row, so it leaves IsInAddNewTransaction false and skips the new branch at lines 20006-20014. It would pass against the base implementation and does not reproduce the hidden pending item, the subsequent move away, or the second *-row activation that triggers #15093. Add the reported sequence and assert that the second activation does not throw and that the list and bound-row counts remain synchronized.
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

@SimonZhao888
SimonZhao888 marked this pull request as ready for review September 16, 2026 08:23
@SimonZhao888
SimonZhao888 requested a review from a team as a code owner September 16, 2026 08:23
@dotnet-policy-service dotnet-policy-service Bot removed the draft draft PR label Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Removing multiple rows while selecting the last new-item row in DataGridView leads to exception

2 participants