Skip to content

Conversation

@LeafShi1
Copy link
Member

@LeafShi1 LeafShi1 commented Jan 20, 2026

Fixes #14204

Root Cause

The foreach loop over DataGridViewRowCollection uses an enumerator that calls this[index], which unshares (clones) each shared row. For 700k rows, this causes massive memory allocation and CPU usage.

Proposed changes

  • DataGridView.Methods.cs:
    • Added ReleaseRowUiaProviders() helper method
    • Modified ReleaseUiaProvider() to use HashSet deduplication instead of foreach
  • DataGridViewColumnCollection.cs:
    • Modified RemoveAtInternal() to use HashSet deduplication
  • The implementation of DataGridViewRowAccessibleObject.Navigate(AccessibleNavigation.Previous/Up) has been changed from navigating through accessibility child indices (AccessibilityObject.GetChild(...)) to direct navigation based on the DataGridView row API:
    • Use Rows.GetPreviousRow(currentIndex, DataGridViewElementStates.Visible) to get the previous row index;
    • Return Rows[index].AccessibilityObject, ensuring that the target row's AccessibilityObject is created only when needed (Lazy).

Customer Impact

  • Applications with large DataGridView (700k+ rows) freeze for several seconds when closing forms or removing columns

Regression?

  • Yes (The regression was introduced in this commit)

Risk

  • Minimal

Screenshots

Before

Closing a form with 700k rows causes 5-10+ seconds of UI freeze and high memory spike due to row cloning.

After

Form closes near-instantly with minimal memory overhead.

Test methodology

  • Manual test

Test environment(s)

  • .net 11.0.0-alpha.1.26057.107
Microsoft Reviewers: Open in CodeFlow

@LeafShi1 LeafShi1 requested a review from a team as a code owner January 20, 2026 10:21
@LeafShi1 LeafShi1 marked this pull request as draft January 20, 2026 10:21
@LeafShi1
Copy link
Member Author

LeafShi1 commented Jan 20, 2026

This fix has been tested and verified.

@codecov
Copy link

codecov bot commented Jan 21, 2026

Codecov Report

❌ Patch coverage is 38.70968% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.18534%. Comparing base (15506ad) to head (894a006).
⚠️ Report is 19 commits behind head on main.

Additional details and impacted files
@@                 Coverage Diff                 @@
##                main      #14224         +/-   ##
===================================================
+ Coverage   77.15242%   77.18534%   +0.03291%     
===================================================
  Files           3279        3279                 
  Lines         645333      645151        -182     
  Branches       47720       47732         +12     
===================================================
+ Hits          497890      497962         +72     
+ Misses        143757      143509        -248     
+ Partials        3686        3680          -6     
Flag Coverage Δ
Debug 77.18534% <38.70968%> (+0.03291%) ⬆️
integration 19.04136% <0.00000%> (+0.05712%) ⬆️
production 52.06744% <38.70968%> (+0.05198%) ⬆️
test 97.40479% <ø> (-0.00080%) ⬇️
unit 49.51047% <38.70968%> (+0.06872%) ⬆️

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.

@LeafShi1 LeafShi1 marked this pull request as ready for review January 22, 2026 01:48
@dotnet-policy-service dotnet-policy-service bot removed the draft draft PR label Jan 22, 2026
@LeafShi1 LeafShi1 requested a review from Copilot January 22, 2026 08:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a critical performance issue in DataGridView where applications with large datasets (700k+ rows) experience severe UI freezes when closing forms or removing columns. The root cause was that iterating over the DataGridViewRowCollection using foreach would unshare (clone) each shared row, causing massive memory allocation and CPU usage.

Changes:

  • Replaced inefficient foreach loops that trigger row cloning with HashSet-based deduplication using direct SharedList access
  • Added a helper method ReleaseRowUiaProviders() to centralize row UIA provider cleanup logic
  • Applied the same optimization pattern to both form disposal and column removal scenarios

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
DataGridView.Methods.cs Added ReleaseRowUiaProviders() helper and modified ReleaseUiaProvider() to use HashSet deduplication when iterating over rows, preventing costly row cloning
DataGridViewColumnCollection.cs Modified RemoveAtInternal() to use HashSet deduplication when releasing UIA providers for cells in the removed column

SimonZhao888
SimonZhao888 previously approved these changes Jan 23, 2026
@SimonZhao888 SimonZhao888 dismissed their stale review January 23, 2026 09:15

Please fix the failure test cases firstly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataGridView: Excessive CPU+memory usage when closing form

2 participants