Skip to content

When DataGridView has a lot of rows, selecting row near bottom of DataGridView is very slow #15171

Description

@hjin1113-web

.NET version

10.0.12

Did it work in .NET Framework?

Yes

Did it work in any of the earlier releases of .NET Core or .NET 5+?

No response

Issue description

Very easy to reproduce. Using following codes. Scroll down to the bottom. Select any row. It will take seconds to select a row.

        private void Form1_Load(object sender, EventArgs e)
        {
            DataGridView Tester = new DataGridView();
            Tester.Columns.Add("trest", "trstr");
            for (int i = 0; i < 30000; i++)
            {
                Tester.Rows.Add($"Row {i:00000}");
            }

            Tester.Visible = true;
            Tester.Dock = DockStyle.Fill;
            this.Controls.Add(Tester);
       }

The bug is in DataGridViewRowCollection. GetVisibleIndex repeats Count (30001) and DisplayIndexToRowIndex also repeats Count (30001).

    internal int GetVisibleIndex(DataGridViewRow row)
    {
        for (int i = 0; i < Count; i++)
        {
            int index = DisplayIndexToRowIndex(i);
            if (index != -1 && _items[index] == row)
            {
                return i;
            }
        }

        return -1;
    }

    internal int DisplayIndexToRowIndex(int visibleRowIndex)
    {
        Debug.Assert(visibleRowIndex < GetRowCount(DataGridViewElementStates.Visible));

        // go row by row
        // the alternative would be to do a binary search using DataGridViewRowCollection::GetRowCount(...)
        // but that method also iterates thru each row so we would not gain much
        int indexOfCurrentVisibleRow = -1;
        for (int i = 0; i < Count; i++)
        {
            if ((GetRowState(i) & DataGridViewElementStates.Visible) == DataGridViewElementStates.Visible)
            {
                indexOfCurrentVisibleRow++;
            }

            if (indexOfCurrentVisibleRow == visibleRowIndex)
            {
                return i;
            }
        }

        Debug.Assert(false, "we should have found the row already");
        return -1;
    }

GetVisibleIndex is called by DataGridViewCellAccessibleObject:Name
Following codes bypass the performance issue.

        private void Form1_Load(object sender, EventArgs e)
        {
            DataGridView Tester = new DataGridView();
            Tester.Columns.Add("trest", "trstr");
            for (int i = 0; i < 30000; i++)
            {
                DataGridViewRow row = new DataGridViewRow();
                NoAccessCell cell = new NoAccessCell();
                cell.Value = $"Row {i:00000}";
                row.Cells.Add(cell);
                Tester.Rows.Add(row);
            }

            Tester.Visible = true;
            Tester.Dock = DockStyle.Fill;
            this.Controls.Add(Tester);
        }
    class NoAccessCell : DataGridViewTextBoxCell
    {
        protected override AccessibleObject CreateAccessibilityInstance()
        {
            return new NoAccessibilityObject();
        }
    }
    class NoAccessibilityObject : AccessibleObject
    {
        public override string Name
        {
            get => null;
            set { }
        }

        public override AccessibleRole Role => AccessibleRole.None;

        public override AccessibleObject GetChild(int index) => null;
        public override int GetChildCount() => 0;

        public override Rectangle Bounds => Rectangle.Empty;

        public override void DoDefaultAction() { }
    }

Steps to reproduce

Very easy to reproduce. Using following codes. Scroll down to the bottom. Select any row. It will take seconds to select a row.

        private void Form1_Load(object sender, EventArgs e)
        {
            DataGridView Tester = new DataGridView();
            Tester.Columns.Add("trest", "trstr");
            for (int i = 0; i < 30000; i++)
            {
                Tester.Rows.Add($"Row {i:00000}");
            }

            Tester.Visible = true;
            Tester.Dock = DockStyle.Fill;
            this.Controls.Add(Tester);
       }

Activity

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

Metadata

Metadata

Assignees

Labels

tenet-accessibilityMAS violation, UIA issue; problems with accessibility standards

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions