// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Forms.Automation;
using System.Windows.Forms.Layout;
using System.Windows.Forms.Primitives;
using System.Windows.Forms.VisualStyles;
using Microsoft.Win32;
using Windows.Win32.UI.Accessibility;

namespace System.Windows.Forms;

public partial class DataGridView
{
    protected virtual void AccessibilityNotifyCurrentCellChanged(Point cellAddress)
    {
        if (cellAddress.X < 0 || cellAddress.X >= Columns.Count)
        {
            throw new ArgumentOutOfRangeException(nameof(cellAddress));
        }

        if (cellAddress.Y < 0 || cellAddress.Y >= Rows.Count)
        {
            throw new ArgumentOutOfRangeException(nameof(cellAddress));
        }

        int visibleRowIndex = Rows.GetRowCount(DataGridViewElementStates.Visible, 0, cellAddress.Y);
        int visibleColumnIndex = Columns.ColumnIndexToActualDisplayIndex(cellAddress.X, DataGridViewElementStates.Visible);

        int topHeaderRowIncrement = ColumnHeadersVisible ? 1 : 0;
        int rowHeaderIncrement = RowHeadersVisible ? 1 : 0;

        int objectID = visibleRowIndex + topHeaderRowIncrement  // + 1 because the top header row acc obj is at index 0
                                       + 1;                     // + 1 because objectID's need to be positive and non-zero

        int childID = visibleColumnIndex + rowHeaderIncrement;  // + 1 because the column header cell is at index 0 in top header row acc obj
                                                                //     same thing for the row header cell in the data grid view row acc obj

        if (ContainsFocus)
        {
            AccessibilityNotifyClients(AccessibleEvents.Focus, objectID, childID);

            DataGridViewCell? currentCell = CurrentCell;
            if (currentCell is not null && currentCell.IsParentAccessibilityObjectCreated)
            {
                currentCell.AccessibilityObject.SetFocus();
            }
        }

        AccessibilityNotifyClients(AccessibleEvents.Selection, objectID, childID);
    }

    internal void ActivateToolTip(
        bool activate,
        string toolTipText,
        int columnIndex,
        int rowIndex)
    {
        ToolTipPrivate = toolTipText;
        _ptToolTipCell = new Point(columnIndex, rowIndex);
        _toolTipControl.Activate(activate);
    }

    internal void AddNewRow(bool createdByEditing)
    {
        Debug.Assert(NewRowIndex == -1);

        Rows.AddInternal(newRow: true, values: null);
        NewRowIndex = Rows.Count - 1;
        _dataGridViewState1[State1_NewRowCreatedByEditing] = createdByEditing;

        if (createdByEditing)
        {
            DataGridViewRowEventArgs dgvRowEvent = new(Rows[NewRowIndex]);
            OnUserAddedRow(dgvRowEvent);

            if (IsAccessibilityObjectCreated)
            {
                AccessibilityObject.InternalRaiseAutomationNotification(
                    AutomationNotificationKind.ItemAdded,
                    AutomationNotificationProcessing.ImportantMostRecent,
                    string.Format(
                        SR.DataGridView_RowAddedNotification,
                        NewRowIndex + (LocalAppContextSwitches.DataGridViewUIAStartRowCountAtZero ? 0 : 1)));
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public virtual DataGridViewAdvancedBorderStyle AdjustColumnHeaderBorderStyle(
        DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStyleInput,
        DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder,
        bool isFirstDisplayedColumn,
        bool isLastVisibleColumn)
    {
        if (ApplyVisualStylesToHeaderCells)
        {
            switch (dataGridViewAdvancedBorderStyleInput.All)
            {
                case DataGridViewAdvancedCellBorderStyle.OutsetPartial:
                case DataGridViewAdvancedCellBorderStyle.OutsetDouble:
                    if (RightToLeftInternal)
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None;
                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.None
                                : DataGridViewAdvancedCellBorderStyle.Outset
                            : DataGridViewAdvancedCellBorderStyle.None;
                    }
                    else
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.None
                                : DataGridViewAdvancedCellBorderStyle.OutsetDouble
                            : DataGridViewAdvancedCellBorderStyle.None;

                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None;
                    }

                    dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble;
                    dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset;
                    return dataGridViewAdvancedBorderStylePlaceholder;

                case DataGridViewAdvancedCellBorderStyle.InsetDouble:
                    if (RightToLeftInternal)
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None;
                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.None
                                : DataGridViewAdvancedCellBorderStyle.Inset
                            : DataGridViewAdvancedCellBorderStyle.None;
                    }
                    else
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.None
                                : DataGridViewAdvancedCellBorderStyle.InsetDouble
                            : DataGridViewAdvancedCellBorderStyle.None;

                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None;
                    }

                    dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble;
                    dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset;
                    return dataGridViewAdvancedBorderStylePlaceholder;

                case DataGridViewAdvancedCellBorderStyle.Single:
                case DataGridViewAdvancedCellBorderStyle.Outset:
                case DataGridViewAdvancedCellBorderStyle.Inset:
                    if (!isFirstDisplayedColumn || RowHeadersVisible)
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None;
                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None;
                        dataGridViewAdvancedBorderStylePlaceholder.TopInternal = dataGridViewAdvancedBorderStyleInput.All;
                        dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = dataGridViewAdvancedBorderStyleInput.All;
                        return dataGridViewAdvancedBorderStylePlaceholder;
                    }
                    else
                    {
                        // isFirstDisplayedColumn == true && this.RowHeadersVisible == false
                        if (RightToLeftInternal)
                        {
                            dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None;
                            dataGridViewAdvancedBorderStylePlaceholder.RightInternal = dataGridViewAdvancedBorderStyleInput.All;
                        }
                        else
                        {
                            dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = dataGridViewAdvancedBorderStyleInput.All;
                            dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None;
                        }

                        dataGridViewAdvancedBorderStylePlaceholder.TopInternal = dataGridViewAdvancedBorderStyleInput.All;
                        dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = dataGridViewAdvancedBorderStyleInput.All;
                        return dataGridViewAdvancedBorderStylePlaceholder;
                    }
            }
        }
        else
        {
            switch (dataGridViewAdvancedBorderStyleInput.All)
            {
                case DataGridViewAdvancedCellBorderStyle.OutsetPartial:
                    if (RightToLeftInternal)
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = isLastVisibleColumn
                            ? DataGridViewAdvancedCellBorderStyle.Outset
                            : DataGridViewAdvancedCellBorderStyle.OutsetPartial;
                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.Outset
                                : DataGridViewAdvancedCellBorderStyle.OutsetDouble
                            : DataGridViewAdvancedCellBorderStyle.OutsetPartial;
                    }
                    else
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.Outset
                                : DataGridViewAdvancedCellBorderStyle.OutsetDouble
                            : DataGridViewAdvancedCellBorderStyle.OutsetPartial;

                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = isLastVisibleColumn
                            ? DataGridViewAdvancedCellBorderStyle.Outset
                            : DataGridViewAdvancedCellBorderStyle.OutsetPartial;
                    }

                    dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble;
                    dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset;
                    return dataGridViewAdvancedBorderStylePlaceholder;

                case DataGridViewAdvancedCellBorderStyle.OutsetDouble:
                    if (RightToLeftInternal)
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Outset;
                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.Outset
                                : DataGridViewAdvancedCellBorderStyle.OutsetDouble
                            : DataGridViewAdvancedCellBorderStyle.Outset;
                    }
                    else
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.Outset
                                : DataGridViewAdvancedCellBorderStyle.OutsetDouble
                            : DataGridViewAdvancedCellBorderStyle.Outset;

                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Outset;
                    }

                    dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.OutsetDouble;
                    dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Outset;
                    return dataGridViewAdvancedBorderStylePlaceholder;

                case DataGridViewAdvancedCellBorderStyle.InsetDouble:
                    if (RightToLeftInternal)
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Inset;
                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.Inset
                                : DataGridViewAdvancedCellBorderStyle.InsetDouble
                            : DataGridViewAdvancedCellBorderStyle.Inset;
                    }
                    else
                    {
                        dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = isFirstDisplayedColumn
                            ? RowHeadersVisible
                                ? DataGridViewAdvancedCellBorderStyle.Inset
                                : DataGridViewAdvancedCellBorderStyle.InsetDouble
                            : DataGridViewAdvancedCellBorderStyle.Inset;

                        dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Inset;
                    }

                    dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.InsetDouble;
                    dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Inset;
                    return dataGridViewAdvancedBorderStylePlaceholder;

                case DataGridViewAdvancedCellBorderStyle.Single:
                    if (!isFirstDisplayedColumn || RowHeadersVisible)
                    {
                        if (RightToLeftInternal)
                        {
                            dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.Single;
                            dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.None;
                        }
                        else
                        {
                            dataGridViewAdvancedBorderStylePlaceholder.LeftInternal = DataGridViewAdvancedCellBorderStyle.None;
                            dataGridViewAdvancedBorderStylePlaceholder.RightInternal = DataGridViewAdvancedCellBorderStyle.Single;
                        }

                        dataGridViewAdvancedBorderStylePlaceholder.TopInternal = DataGridViewAdvancedCellBorderStyle.Single;
                        dataGridViewAdvancedBorderStylePlaceholder.BottomInternal = DataGridViewAdvancedCellBorderStyle.Single;
                        return dataGridViewAdvancedBorderStylePlaceholder;
                    }

                    break;
            }
        }

        return dataGridViewAdvancedBorderStyleInput;
    }

    private bool AdjustExpandingColumn(DataGridViewColumn dataGridViewColumn, int rowIndex)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(rowIndex > -1);
        Debug.Assert(rowIndex < Rows.Count);

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return false;
        }

        bool ret = false; // No autosizing occurs by default.
        try
        {
            _noAutoSizeCount++;
            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
            int preferredThickness = dataGridViewRow.Cells[dataGridViewColumn.Index].GetPreferredWidth(
                rowIndex,
                dataGridViewRow.GetHeight(rowIndex));

            if (preferredThickness > DataGridViewBand.MaxBandThickness)
            {
                preferredThickness = DataGridViewBand.MaxBandThickness;
            }

            if (dataGridViewColumn.Width < preferredThickness)
            {
                // Column needs to be expanded
                dataGridViewColumn.ThicknessInternal = preferredThickness;
                ret = true;
            }
        }
        finally
        {
            Debug.Assert(_noAutoSizeCount > 0);
            _noAutoSizeCount--;
        }

        return ret;
    }

    private bool AdjustExpandingColumns(DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter, int rowIndex)
    {
        Debug.Assert(autoSizeColumnCriteriaFilter != DataGridViewAutoSizeColumnCriteriaInternal.None);
        Debug.Assert((autoSizeColumnCriteriaFilter & DataGridViewAutoSizeColumnCriteriaInternal.Fill) == 0);

        bool ret = false; // No column auto size by default
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        while (dataGridViewColumn is not null)
        {
            DataGridViewAutoSizeColumnCriteriaInternal inheritedAutoSizeColumnCriteria =
                (DataGridViewAutoSizeColumnCriteriaInternal)dataGridViewColumn.InheritedAutoSizeMode;
            DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered =
                inheritedAutoSizeColumnCriteria & autoSizeColumnCriteriaFilter;
            if (autoSizeColumnCriteriaFiltered != 0)
            {
                ret |= AdjustExpandingColumn(dataGridViewColumn, rowIndex);
            }

            dataGridViewColumn = Columns.GetNextColumn(
                dataGridViewColumn,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
        }

        return ret;
    }

    private int AdjustExpandingRow(int rowIndex, int columnIndex, bool fixedWidth)
    {
        Debug.Assert(columnIndex >= -1 && columnIndex < Columns.Count);
        Debug.Assert(rowIndex >= 0 && rowIndex < Rows.Count);
        Debug.Assert(_autoSizeRowsMode is DataGridViewAutoSizeRowsMode.DisplayedHeaders
            or DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders
            or DataGridViewAutoSizeRowsMode.DisplayedCells);

        int width = 0;
        DataGridViewCell dataGridViewCell;
        if (columnIndex > -1
            && ((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode).HasFlag(DataGridViewAutoSizeRowsModeInternal.AllColumns))
        {
            dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
            if (fixedWidth)
            {
                width = Columns[columnIndex].Thickness;
            }
        }
        else
        {
            Debug.Assert(columnIndex == -1);
            Debug.Assert((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0);
            dataGridViewCell = Rows.SharedRow(rowIndex).HeaderCell;
            if (fixedWidth)
            {
                Debug.Assert(RowHeadersWidth > 0);
                Debug.Assert(RowHeadersVisible);
                width = RowHeadersWidth;
            }
        }

        int preferredThickness = fixedWidth
            ? dataGridViewCell.GetPreferredHeight(rowIndex, width)
            : dataGridViewCell.GetPreferredSize(rowIndex).Height;

        Rows.SharedRow(rowIndex).GetHeightInfo(rowIndex, out int height, out int minimumHeight);
        if (preferredThickness < height)
        {
            preferredThickness = height;
        }

        Debug.Assert(preferredThickness >= minimumHeight);
        if (preferredThickness > DataGridViewBand.MaxBandThickness)
        {
            preferredThickness = DataGridViewBand.MaxBandThickness;
        }

        if (height != preferredThickness)
        {
            Debug.Assert(_autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None);
            Rows[rowIndex].Thickness = preferredThickness;   // un-sharing the resized row
        }

        return preferredThickness;
    }

    private void AdjustExpandingRows(int columnIndex, bool fixedWidth)
    {
        if ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0
            && ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) == 0 || !RowHeadersVisible))
        {
            return;
        }

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return;
        }

        // Very expensive processing - the developer should avoid this scenario.
        // Switch to batch operation
        _inBulkPaintCount++;
        try
        {
            if ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllRows) != 0)
            {
                _inBulkLayoutCount++;
                try
                {
                    for (int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                        rowIndex != -1;
                        rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible))
                    {
                        int width = 0;
                        DataGridViewCell dataGridViewCell;
                        if (columnIndex > -1 && (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0)
                        {
                            dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
                            if (fixedWidth)
                            {
                                width = Columns[columnIndex].Thickness;
                            }
                        }
                        else
                        {
                            Debug.Assert((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0);
                            dataGridViewCell = Rows.SharedRow(rowIndex).HeaderCell;
                            if (fixedWidth)
                            {
                                Debug.Assert(RowHeadersWidth > 0);
                                Debug.Assert(RowHeadersVisible);
                                width = RowHeadersWidth;
                            }
                        }

                        int preferredHeight = fixedWidth
                            ? dataGridViewCell.GetPreferredHeight(rowIndex, width)
                            : dataGridViewCell.GetPreferredSize(rowIndex).Height;

                        if (Rows.SharedRow(rowIndex).Height < preferredHeight)
                        {
                            Rows[rowIndex].Height = preferredHeight;  // un-sharing the row to be resized
                        }
                    }
                }
                finally
                {
                    ExitBulkLayout(invalidInAdjustFillingColumns: false);
                }
            }
            else
            {
                Debug.Assert((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0);
                int displayHeight = _layout.Data.Height;
                int cy = 0;

                int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                while (rowIndex != -1 && cy < displayHeight)
                {
                    cy += AdjustExpandingRow(rowIndex, columnIndex, fixedWidth);
                    rowIndex = Rows.GetNextRow(rowIndex,
                        DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                }

                if (cy < displayHeight)
                {
                    rowIndex = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                    while (rowIndex != -1 && cy < displayHeight)
                    {
                        cy += AdjustExpandingRow(rowIndex, columnIndex, fixedWidth);
                        rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                    }
                }
            }
        }
        finally
        {
            ExitBulkPaint(-1, -1);
        }
    }

    internal void AdjustFillingColumn(DataGridViewColumn dataGridViewColumn, int width)
    {
        if (InAdjustFillingColumns)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAlterAutoFillColumnParameter);
        }

        _dataGridViewOper[OperationInAdjustFillingColumn] = true;

        try
        {
            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
            Debug.Assert(dataGridViewColumn.Visible);
            Debug.Assert(!dataGridViewColumn.Frozen);
            Debug.Assert(dataGridViewColumn.MinimumWidth <= width);
            Debug.Assert(dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill);
            Debug.Assert(!_layout._dirty);

            if (Columns.GetColumnsWidth(DataGridViewElementStates.Visible) > _layout.Data.Width)
            {
                // Columns are scrolling - this means that all columns have reached their minimum width.
                // Do not affect their width or fill weight
                Debug.Assert(dataGridViewColumn.MinimumWidth == dataGridViewColumn.Width);
                return;
            }

            int availableWidth = _layout.Data.Width;  // Width available for auto-filled columns

            // Check if the column is the first or last visible scrolling column
            if (DesignMode
                || dataGridViewColumn == Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen)
                || dataGridViewColumn == Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen))
            {
                // Changing the width is equivalent to adjusting the FillWeight when:
                //  - the column is not scrolling and is the first non-frozen visible column
                //  - the column is not scrolling and is the last non-frozen visible column

                float weightSum = 0;        // Weights of all auto filled visible columns.
                int widthSum = 0;           // Sum of widths of visible auto filled columns.
                int imposedWidthSum = 0;    // Minimum width required for all other columns.
                bool otherFillingColumnExists = false;
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumnTmp.Visible)
                    {
                        if (dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                        {
                            Debug.Assert(!dataGridViewColumnTmp.Frozen);
                            widthSum += dataGridViewColumnTmp.Width;
                            if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index)
                            {
                                imposedWidthSum += dataGridViewColumnTmp.MinimumWidth;
                                otherFillingColumnExists = true;
                            }

                            weightSum += dataGridViewColumnTmp.FillWeight;
                        }
                        else
                        {
                            imposedWidthSum += dataGridViewColumnTmp.Width;
                            availableWidth -= dataGridViewColumnTmp.Width;
                        }
                    }
                }

                if (!otherFillingColumnExists)
                {
                    // The resized column is the only one that is filling. This is a no-op operation.
                    // Neither the width nor the fill weight can change
                    return;
                }

                int maximumPossibleWidth = _layout.Data.Width - imposedWidthSum;
                if (width > maximumPossibleWidth)
                {
                    width = maximumPossibleWidth;
                }

                // Determine fill weight equivalent to 'width'
                float oldWeight = dataGridViewColumn.FillWeight;
                float newWeight = (float)(width * weightSum) / widthSum;
                bool desiredWidthTooSmall = false;
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index
                        && dataGridViewColumnTmp.Visible
                        && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                    {
                        dataGridViewColumnTmp.FillWeightInternal = (weightSum - newWeight) * dataGridViewColumnTmp.FillWeight / (weightSum - oldWeight);

                        if (dataGridViewColumnTmp.FillWeight < (dataGridViewColumnTmp.MinimumWidth * weightSum) / widthSum)
                        {
                            desiredWidthTooSmall = true;
                            dataGridViewColumnTmp.DesiredFillWidth = -1;
                        }
                        else
                        {
                            dataGridViewColumnTmp.DesiredFillWidth = 0;
                        }
                    }
                }

                dataGridViewColumn.FillWeightInternal = newWeight;

                if (desiredWidthTooSmall)
                {
                    // At least one column hits its minimum width
                    // Adjust UsedFillWeight values are adjusted FillWeight values
                    float usedWeightSumNoneMinimal = weightSum;
                    float weightSumNoneMinimal = weightSum;
                    float usedFillWeights = 0F;
                    foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                    {
                        if (dataGridViewColumnTmp.Visible
                            && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                        {
                            if (dataGridViewColumnTmp.Index == dataGridViewColumn.Index)
                            {
                                dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight;
                                usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight;
                                weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight;
                                usedFillWeights += dataGridViewColumnTmp.UsedFillWeight;
                            }
                            else if (dataGridViewColumnTmp.DesiredFillWidth == -1)
                            {
                                dataGridViewColumnTmp.UsedFillWeight = weightSum * dataGridViewColumnTmp.MinimumWidth / widthSum;
                                usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight;
                                weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight;
                                usedFillWeights += dataGridViewColumnTmp.UsedFillWeight;
                            }
                        }
                    }

                    foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                    {
                        if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index
                            && dataGridViewColumnTmp.Visible
                            && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill
                            && dataGridViewColumnTmp.DesiredFillWidth != -1)
                        {
                            dataGridViewColumnTmp.UsedFillWeight = Math.Max(
                                dataGridViewColumnTmp.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal,
                                weightSum * dataGridViewColumnTmp.MinimumWidth / widthSum);
                            usedFillWeights += dataGridViewColumnTmp.UsedFillWeight;
                        }
                    }

                    dataGridViewColumn.UsedFillWeight += weightSum - usedFillWeights;
                }
                else
                {
                    // No column hits its minimum width
                    // Each UsedFillWeight simply equals the FillWeight
                    foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                    {
                        if (dataGridViewColumnTmp.Visible
                            && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                        {
                            dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight;
                        }
                    }
                }
#if DEBUG
                float weightSumDbg = 0F;
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumnTmp.Visible
                        && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                    {
                        weightSumDbg += dataGridViewColumnTmp.UsedFillWeight;
                    }
                }

                Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F);
#endif
            }
            else
            {
                // This column is not the first nor the last visible non-frozen column
                // Changing its width only affects the width and weight of the columns displayed after it

                // First figure out the maximum possible width
                int imposedWidthSum = 0;    // Minimum width required for all other columns
                float weightSum = 0;        // Weights of all auto filled visible columns.
                float oldWeightSum = 0F;    // Fill weights of the columns displayed after this resized column
                bool otherFillingColumnExists = false;
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumnTmp.Visible)
                    {
                        if (dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                        {
                            Debug.Assert(!dataGridViewColumnTmp.Frozen);
                            if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index)
                            {
                                if (Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index))
                                {
                                    imposedWidthSum += dataGridViewColumnTmp.MinimumWidth;   // Column is allowed to shrink down to its minimum
                                    oldWeightSum += dataGridViewColumnTmp.FillWeight;
                                }
                                else
                                {
                                    // Column is displayed before 'dataGridViewColumn', it is not allowed to shrink at all
                                    imposedWidthSum += dataGridViewColumnTmp.Width;
                                }

                                otherFillingColumnExists = true;
                            }

                            weightSum += dataGridViewColumnTmp.FillWeight;
                        }
                        else
                        {
                            imposedWidthSum += dataGridViewColumnTmp.Width;
                            availableWidth -= dataGridViewColumnTmp.Width;
                        }
                    }
                }

                if (!otherFillingColumnExists)
                {
                    // The resized column is the only one that is filling. This is a no-op operation.
                    // Neither the width nor the fill weight can change
                    return;
                }

                int maximumPossibleWidth = _layout.Data.Width - imposedWidthSum;
                if (width > maximumPossibleWidth)
                {
                    width = maximumPossibleWidth;
                }

                // Then figure out the target fill weights
                float oldWeight = dataGridViewColumn.FillWeight;
                float newWeight = weightSum * width / availableWidth;
                float newWeightSum = oldWeightSum + oldWeight - newWeight;
                Debug.Assert(newWeightSum > 0);
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumnTmp.Index != dataGridViewColumn.Index
                        && dataGridViewColumnTmp.Visible
                        && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill
                        && Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index))
                    {
                        dataGridViewColumnTmp.FillWeightInternal = dataGridViewColumnTmp.FillWeight * newWeightSum / oldWeightSum;
                    }
                }

                dataGridViewColumn.FillWeightInternal = newWeight;

                bool desiredWidthTooSmall = false;
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumnTmp.Visible
                        && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                    {
                        if (dataGridViewColumnTmp.FillWeight < (dataGridViewColumnTmp.MinimumWidth * weightSum) / availableWidth)
                        {
                            desiredWidthTooSmall = true;
                            dataGridViewColumnTmp.DesiredFillWidth = -1;
                        }
                        else
                        {
                            dataGridViewColumnTmp.DesiredFillWidth = 0;
                        }
                    }
                }

                if (desiredWidthTooSmall)
                {
                    // At least one column hits its minimum width
                    // Adjust UsedFillWeight values are adjusted FillWeight values
                    float usedWeightSumNoneMinimal = weightSum;
                    float weightSumNoneMinimal = weightSum;
                    float usedFillWeights = 0F;
                    foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                    {
                        if (dataGridViewColumnTmp.Visible
                            && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                        {
                            if (dataGridViewColumnTmp.Index == dataGridViewColumn.Index
                                || Columns.DisplayInOrder(dataGridViewColumnTmp.Index, dataGridViewColumn.Index))
                            {
                                dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.Index == dataGridViewColumn.Index
                                    ? dataGridViewColumnTmp.FillWeight
                                    : weightSum * dataGridViewColumnTmp.Width / availableWidth;

                                usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight;
                                weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight;
                                usedFillWeights += dataGridViewColumnTmp.UsedFillWeight;
                            }
                            else if (dataGridViewColumnTmp.DesiredFillWidth == -1)
                            {
                                dataGridViewColumnTmp.UsedFillWeight = weightSum * dataGridViewColumnTmp.MinimumWidth / availableWidth;
                                usedWeightSumNoneMinimal -= dataGridViewColumnTmp.UsedFillWeight;
                                weightSumNoneMinimal -= dataGridViewColumnTmp.FillWeight;
                                usedFillWeights += dataGridViewColumnTmp.UsedFillWeight;
                            }
                        }
                    }

                    foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                    {
                        if (Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index)
                            && dataGridViewColumnTmp.Visible
                            && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill
                            && dataGridViewColumnTmp.DesiredFillWidth != -1)
                        {
                            dataGridViewColumnTmp.UsedFillWeight = Math.Max(dataGridViewColumnTmp.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal,
                                                                            weightSum * dataGridViewColumnTmp.MinimumWidth / availableWidth);
                            usedFillWeights += dataGridViewColumnTmp.UsedFillWeight;
                        }
                    }

                    dataGridViewColumn.UsedFillWeight += weightSum - usedFillWeights;
                }
                else
                {
                    // No column hits its minimum width
                    // Each UsedFillWeight simply equals the FillWeight
                    foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                    {
                        if (dataGridViewColumnTmp.Visible
                            && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                        {
                            dataGridViewColumnTmp.UsedFillWeight = dataGridViewColumnTmp.FillWeight;
                        }
                    }
                }
#if DEBUG
                float weightSumDbg = 0F;
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumnTmp.Visible
                        && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                    {
                        weightSumDbg += dataGridViewColumnTmp.UsedFillWeight;
                    }
                }

                Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F);
#endif
            }

            // UsedFillWeight properties are up-to-date
            _dataGridViewState2[State2_UsedFillWeightsDirty] = false;
            _availableWidthForFillColumns = availableWidth;
            // AdjustFillingColumns() will resize columns based on the UsedFillWeight values
            PerformLayoutPrivate(
                useRowShortcut: false,
                computeVisibleRows: true,
                invalidInAdjustFillingColumns: false,
                repositionEditingControl: false);
        }
        finally
        {
            _dataGridViewOper[OperationInAdjustFillingColumn] = false;
        }
    }

    private bool AdjustFillingColumns()
    {
        if (_dataGridViewOper[OperationInAdjustFillingColumns])
        {
            // No need to auto fill columns while we're already doing it.
            return false;
        }

        _dataGridViewOper[OperationInAdjustFillingColumns] = true;

        bool columnsAdjusted = false;
        try
        {
            // Determine free space for filling columns.
            int numVisibleFillColumns = 0;  // number of visible columns that are auto filled.
            int imposedWidthSum = 0;        // total width of columns that don't have a flexible width.
            int requiredWidthSum = 0;       // total of the minimum widths of the visible auto filled columns.
            float weightSum = 0F;           // total of the weights of the visible auto filled columns.
            List<DataGridViewColumn>? autoFillColumns = null;
            foreach (DataGridViewColumn dataGridViewColumn in Columns)
            {
                if (dataGridViewColumn.Visible)
                {
                    if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                    {
                        Debug.Assert(!dataGridViewColumn.Frozen);
                        numVisibleFillColumns++;
                        requiredWidthSum += dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth;
                        weightSum += dataGridViewColumn.FillWeight;
                        autoFillColumns ??= new(Columns.Count);

                        autoFillColumns.Add(dataGridViewColumn);
                    }
                    else
                    {
                        imposedWidthSum += dataGridViewColumn.Width;
                    }
                }
            }

            if (numVisibleFillColumns > 0)
            {
                // Assuming no vertical scrollbar has been accounted for yet
                Debug.Assert(_layout.Data.Width == _layout.Inside.Width - _layout.RowHeaders.Width - (SingleVerticalBorderAdded ? 1 : 0));
                int availableWidth = _layout.Data.Width - imposedWidthSum;
                if (_scrollBars is ScrollBars.Both or ScrollBars.Vertical)
                {
                    int totalVisibleRowCount = Rows.GetRowCount(DataGridViewElementStates.Visible);
                    int totalVisibleHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible);
                    int totalVisibleFrozenHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);

                    // Assuming there is no horizontal scrollbar, is a vertical scrollbar required?
                    ComputeVisibleRows(); // Make sure this.DisplayedBandsInfo.FirstDisplayedScrollingRow and other row count info variables have been set

                    if (DisplayedBandsInfo.NumTotallyDisplayedFrozenRows == Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)
                        && DisplayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)
                        && (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight))
                        && _layout.Data.Height > totalVisibleFrozenHeight
                        && SystemInformation.VerticalScrollBarWidth <= _layout.Data.Width)
                    {
                        // Vertical scrollbar is required, even when there is not horizontal one.
                        availableWidth -= SystemInformation.VerticalScrollBarWidth;
                    }
                }

                int columnEntry;

                if (availableWidth <= requiredWidthSum)
                {
                    // All auto filled columns need to take their minimum width. If (availableWidth < requiredWidthSum)
                    // a horizontal scrollbar appears.
                    availableWidth = 0;
                    for (columnEntry = 0; columnEntry < autoFillColumns!.Count; columnEntry++)
                    {
                        DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                        int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth;
                        if (dataGridViewColumn.Thickness != minimumWidth)
                        {
                            columnsAdjusted = true;
                            dataGridViewColumn.ThicknessInternal = minimumWidth;
                        }

                        availableWidth += dataGridViewColumn.Thickness;
                    }

                    // if (this.dataGridViewState2[State2_UsedFillWeightsDirty])
                    {
                        for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                        {
                            // Make sure the UsedFillWeight correspond to the actual column width
                            DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                            dataGridViewColumn.UsedFillWeight = dataGridViewColumn.Width * weightSum / availableWidth;
                        }

                        _dataGridViewState2[State2_UsedFillWeightsDirty] = false;
                        _availableWidthForFillColumns = availableWidth;
                    }

                    return columnsAdjusted;
                }

                // Auto filling columns can share some real estate.

                int usedWidth = 0;

                // Update the UsedFillWeight value if dirty
                if (_dataGridViewState2[State2_UsedFillWeightsDirty])
                {
                    // Assign desired widths
                    Debug.Assert(weightSum > 0);
                    bool desiredWidthTooSmall = false;
                    for (columnEntry = 0; columnEntry < autoFillColumns!.Count; columnEntry++)
                    {
                        DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                        if (columnEntry == autoFillColumns.Count - 1)
                        {
                            dataGridViewColumn.DesiredFillWidth = availableWidth - usedWidth;
                        }
                        else
                        {
                            float desiredFillWidth = (dataGridViewColumn.FillWeight / weightSum) * availableWidth;
                            dataGridViewColumn.DesiredFillWidth = (int)Math.Round(desiredFillWidth, MidpointRounding.AwayFromZero);
                            usedWidth += dataGridViewColumn.DesiredFillWidth;
                        }

                        int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth;
                        if (dataGridViewColumn.DesiredFillWidth < minimumWidth)
                        {
                            desiredWidthTooSmall = true;
                            dataGridViewColumn.DesiredFillWidth = -1;
                        }
                    }

                    if (desiredWidthTooSmall)
                    {
                        // At least one column hits its minimum width
                        // Adjust UsedFillWeight values are adjusted FillWeight values
                        float usedWeightSumNoneMinimal = weightSum;
                        float weightSumNoneMinimal = weightSum;
                        for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                        {
                            DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                            if (dataGridViewColumn.DesiredFillWidth == -1)
                            {
                                int minimumWidth = dataGridViewColumn.DesiredMinimumWidth > 0 ? dataGridViewColumn.DesiredMinimumWidth : dataGridViewColumn.MinimumWidth;
                                dataGridViewColumn.UsedFillWeight = weightSum * minimumWidth / availableWidth;
                                usedWeightSumNoneMinimal -= dataGridViewColumn.UsedFillWeight;
                                weightSumNoneMinimal -= dataGridViewColumn.FillWeight;
                            }
                        }

                        for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                        {
                            DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                            if (dataGridViewColumn.DesiredFillWidth != -1)
                            {
                                dataGridViewColumn.UsedFillWeight = dataGridViewColumn.FillWeight * usedWeightSumNoneMinimal / weightSumNoneMinimal;
                            }
                        }
                    }
                    else
                    {
                        // No column hits its minimum width
                        // Each UsedFillWeight simply equals the FillWeight
                        for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                        {
                            DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                            dataGridViewColumn.UsedFillWeight = dataGridViewColumn.FillWeight;
                        }
                    }

                    _dataGridViewState2[State2_UsedFillWeightsDirty] = false;
                    _availableWidthForFillColumns = availableWidth;
                }
                else if (availableWidth != _availableWidthForFillColumns)
                {
                    // The available width for auto-filled columns has changed - UsedFillWeight values need to be adjusted.
                    if (availableWidth > _availableWidthForFillColumns)
                    {
                        // Available width increased
                        int widthGain = availableWidth - _availableWidthForFillColumns;
                        // Allocate additional width according to UsedFillWeight and FillWeight values
                        for (columnEntry = 0; columnEntry < autoFillColumns!.Count; columnEntry++)
                        {
                            DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                            dataGridViewColumn.DesiredFillWidth = dataGridViewColumn.Width;
                        }

                        float[] floatDesiredWidths = new float[autoFillColumns.Count];
                        for (int gain = 0; gain < widthGain; gain++)
                        {
                            float fillWeightRatioSum = 0F;
                            bool minimumColumnExists = false;
                            for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                            {
                                DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                                fillWeightRatioSum += dataGridViewColumn.FillWeight / dataGridViewColumn.UsedFillWeight;
                                if (dataGridViewColumn.DesiredFillWidth <= dataGridViewColumn.MinimumWidth)
                                {
                                    minimumColumnExists = true;
                                }
                            }

                            for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                            {
                                DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                                if (gain == 0)
                                {
                                    floatDesiredWidths[columnEntry] = _availableWidthForFillColumns * dataGridViewColumn.UsedFillWeight / weightSum;
                                }

                                if (minimumColumnExists)
                                {
                                    floatDesiredWidths[columnEntry] += dataGridViewColumn.FillWeight / dataGridViewColumn.UsedFillWeight / fillWeightRatioSum;
                                }
                                else
                                {
                                    floatDesiredWidths[columnEntry] += dataGridViewColumn.FillWeight / weightSum;
                                }
                            }
                        }

                        for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                        {
                            DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                            dataGridViewColumn.UsedFillWeight = weightSum / availableWidth * floatDesiredWidths[columnEntry];
                        }
                    }
                    else
                    {
                        // availableWidth < this.availableWidthForFillColumns - Available width decreased
                        int totalWidthLoss = _availableWidthForFillColumns - availableWidth;
                        int cumulatedWidthLoss = 0;
                        for (columnEntry = 0; columnEntry < autoFillColumns!.Count; columnEntry++)
                        {
                            DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                            dataGridViewColumn.DesiredFillWidth = dataGridViewColumn.Width;
                        }

                        // the width loss is accounted for in steps of 10%
                        do
                        {
                            int stepDownAvailableWidthForFillColumns = _availableWidthForFillColumns - cumulatedWidthLoss;
                            int widthLoss = Math.Min(stepDownAvailableWidthForFillColumns - availableWidth, Math.Max(1, (int)(stepDownAvailableWidthForFillColumns * 0.1F)));
                            cumulatedWidthLoss += widthLoss;
                            bool changeDone;
                            do
                            {
                                changeDone = false;
                                // Determine which column deserves to shrink the most
                                float biggestWeightDeficiency = 0F, fillWeightRatioSum = 0F, fillWeightRatio;
                                DataGridViewColumn? mostDeservingDataGridViewColumn = null;
                                for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                                {
                                    DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                                    if (dataGridViewColumn.DesiredFillWidth > dataGridViewColumn.MinimumWidth)
                                    {
                                        fillWeightRatio = dataGridViewColumn.UsedFillWeight / dataGridViewColumn.FillWeight;
                                        fillWeightRatioSum += fillWeightRatio;
                                        if (fillWeightRatio > biggestWeightDeficiency)
                                        {
                                            mostDeservingDataGridViewColumn = dataGridViewColumn;
                                            biggestWeightDeficiency = fillWeightRatio;
                                        }
                                    }
                                }

                                if (mostDeservingDataGridViewColumn is not null)
                                {
                                    float floatDesiredWidth = (stepDownAvailableWidthForFillColumns * mostDeservingDataGridViewColumn.UsedFillWeight / weightSum) - widthLoss * mostDeservingDataGridViewColumn.UsedFillWeight / mostDeservingDataGridViewColumn.FillWeight / fillWeightRatioSum;
                                    if (floatDesiredWidth < mostDeservingDataGridViewColumn.MinimumWidth)
                                    {
                                        floatDesiredWidth = mostDeservingDataGridViewColumn.MinimumWidth;
                                    }

                                    int oldDesiredWidth = mostDeservingDataGridViewColumn.DesiredFillWidth;
                                    mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Min(oldDesiredWidth, (int)Math.Round(floatDesiredWidth, MidpointRounding.AwayFromZero));
                                    changeDone = (oldDesiredWidth != mostDeservingDataGridViewColumn.DesiredFillWidth);
                                    if (!changeDone && widthLoss == 1 && oldDesiredWidth > mostDeservingDataGridViewColumn.MinimumWidth)
                                    {
                                        mostDeservingDataGridViewColumn.DesiredFillWidth--;
                                        changeDone = true;
                                    }

                                    Debug.Assert(oldDesiredWidth >= mostDeservingDataGridViewColumn.DesiredFillWidth);
                                    widthLoss -= oldDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth;
                                    if (changeDone)
                                    {
                                        stepDownAvailableWidthForFillColumns -= oldDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth;
                                        for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                                        {
                                            DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                                            dataGridViewColumn.UsedFillWeight = weightSum / stepDownAvailableWidthForFillColumns * dataGridViewColumn.DesiredFillWidth;
                                        }
                                    }

                                    Debug.Assert(widthLoss >= 0);
                                }
                            }
                            while (changeDone && widthLoss > 0);
                        }
                        while (cumulatedWidthLoss < totalWidthLoss);
                    }

                    _availableWidthForFillColumns = availableWidth;
                }

#if DEBUG
                float weightSumDbg = 0F;
                for (columnEntry = 0; columnEntry < autoFillColumns!.Count; columnEntry++)
                {
                    DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                    weightSumDbg += dataGridViewColumn.UsedFillWeight;
                }

                Debug.Assert(Math.Abs(weightSum - weightSumDbg) < 1.0F);
#endif

                // Finally update the columns' width according the UsedFillWeight values.
                try
                {
                    _dataGridViewState2[State2_AllowHorizontalScrollbar] = false;
                    usedWidth = 0;
                    float carryover = 0F;
                    while (autoFillColumns!.Count > 0)
                    {
                        DataGridViewColumn? mostDeservingDataGridViewColumn = null;
                        if (autoFillColumns.Count == 1)
                        {
                            mostDeservingDataGridViewColumn = autoFillColumns[0];
                            mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Max(availableWidth - usedWidth, mostDeservingDataGridViewColumn.MinimumWidth);
                            autoFillColumns.Clear();
                        }
                        else
                        {
                            float biggestWeightDiscrepancy = 0F, weightDiscrepancy;
                            for (columnEntry = 0; columnEntry < autoFillColumns.Count; columnEntry++)
                            {
                                DataGridViewColumn dataGridViewColumn = autoFillColumns[columnEntry];
                                weightDiscrepancy = Math.Abs(dataGridViewColumn.UsedFillWeight - dataGridViewColumn.FillWeight) / dataGridViewColumn.FillWeight;
                                if (weightDiscrepancy > biggestWeightDiscrepancy || mostDeservingDataGridViewColumn is null)
                                {
                                    mostDeservingDataGridViewColumn = dataGridViewColumn;
                                    biggestWeightDiscrepancy = weightDiscrepancy;
                                }
                            }

                            float floatDesiredWidth = (mostDeservingDataGridViewColumn!.UsedFillWeight * availableWidth / weightSum) + carryover;
                            mostDeservingDataGridViewColumn.DesiredFillWidth = Math.Max(mostDeservingDataGridViewColumn.MinimumWidth, (int)Math.Round(floatDesiredWidth, MidpointRounding.AwayFromZero));
                            carryover = floatDesiredWidth - mostDeservingDataGridViewColumn.DesiredFillWidth;
                            usedWidth += mostDeservingDataGridViewColumn.DesiredFillWidth;
                            autoFillColumns.Remove(mostDeservingDataGridViewColumn);
                        }

                        if (mostDeservingDataGridViewColumn.DesiredFillWidth != mostDeservingDataGridViewColumn.Thickness)
                        {
                            columnsAdjusted = true;
                            mostDeservingDataGridViewColumn.ThicknessInternal = mostDeservingDataGridViewColumn.DesiredFillWidth;
                        }
                    }
                }
                finally
                {
                    _dataGridViewState2[State2_AllowHorizontalScrollbar] = true;
                }
            }
#if DEBUG
            if (!_dataGridViewState2[State2_UsedFillWeightsDirty])
            {
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumnTmp.Visible
                        && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                    {
                        Debug.Assert(Math.Abs(dataGridViewColumnTmp.UsedFillWeight * _availableWidthForFillColumns - weightSum * dataGridViewColumnTmp.Width) / weightSum / dataGridViewColumnTmp.Width <= 1.25F / dataGridViewColumnTmp.Width);
                    }
                }
            }

            bool nonMinColumnExists = false;
            int widthSum = 0;
            foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
            {
                if (dataGridViewColumnTmp.Visible
                    && dataGridViewColumnTmp.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                {
                    widthSum += dataGridViewColumnTmp.Width;
                    if (dataGridViewColumnTmp.Width > dataGridViewColumnTmp.MinimumWidth)
                    {
                        nonMinColumnExists = true;
                    }
                }
            }

            if (nonMinColumnExists)
            {
                Debug.Assert(widthSum == _availableWidthForFillColumns);
            }
#endif
        }
        finally
        {
            _dataGridViewOper[OperationInAdjustFillingColumns] = false;
        }

        return columnsAdjusted;
    }

    private void AdjustShrinkingRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode, bool fixedWidth, bool internalAutosizing)
    {
        if ((((DataGridViewAutoSizeRowsModeInternal)autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0
            || ((((DataGridViewAutoSizeRowsModeInternal)autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0 && RowHeadersVisible))
        {
            // Switch to batch operation
            _inBulkPaintCount++;
            try
            {
                if ((((DataGridViewAutoSizeRowsModeInternal)autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllRows) != 0)
                {
                    // Very expensive processing - the developer should avoid this scenario.
                    _inBulkLayoutCount++;
                    try
                    {
                        for (int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                            rowIndex != -1;
                            rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible))
                        {
                            AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing);
                        }
                    }
                    finally
                    {
                        ExitBulkLayout(invalidInAdjustFillingColumns: false);
                    }
                }
                else
                {
                    Debug.Assert((((DataGridViewAutoSizeRowsModeInternal)autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0);
                    int displayHeight = _layout.Data.Height;
                    int cy = 0;

                    int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                    while (rowIndex != -1 && cy < displayHeight)
                    {
                        AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing);
                        cy += Rows.SharedRow(rowIndex).GetHeight(rowIndex);
                        rowIndex = Rows.GetNextRow(rowIndex,
                            DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                    }

                    if (cy < displayHeight)
                    {
                        int cyFrozen = cy;
                        int oldFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                        rowIndex = oldFirstVisibleScrollingRow;
                        while (rowIndex != -1
                            && cy < displayHeight
                            && oldFirstVisibleScrollingRow == DisplayedBandsInfo.FirstDisplayedScrollingRow)
                        {
                            AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing);
                            cy += Rows.SharedRow(rowIndex).GetHeight(rowIndex);
                            rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                        }

                        do
                        {
                            oldFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                            if (cy < displayHeight)
                            {
                                int rowAboveFirstVisibleScrollingRow = Rows.GetPreviousRow(DisplayedBandsInfo.FirstDisplayedScrollingRow, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                                if (rowAboveFirstVisibleScrollingRow != -1)
                                {
                                    AutoResizeRowInternal(rowAboveFirstVisibleScrollingRow, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing);
                                }
                            }

                            cy = cyFrozen;
                            rowIndex = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                            while (rowIndex != -1 && cy < displayHeight)
                            {
                                AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(autoSizeRowsMode), fixedWidth, internalAutosizing);
                                cy += Rows.SharedRow(rowIndex).GetHeight(rowIndex);
                                rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                            }
                        }
                        while (oldFirstVisibleScrollingRow != DisplayedBandsInfo.FirstDisplayedScrollingRow);
                    }
                }
            }
            finally
            {
                ExitBulkPaint(-1, -1);
            }
        }
    }

    public bool AreAllCellsSelected(bool includeInvisibleCells)
    {
        if (Columns.Count == 0 && Rows.Count == 0)
        {
            return true;
        }

        if (!includeInvisibleCells
            && (Rows.GetFirstRow(DataGridViewElementStates.Visible) == -1
                || Columns.GetFirstColumn(DataGridViewElementStates.Visible) is null))
        {
            return true;
        }

        DataGridViewRow? dataGridViewRow;
        bool allCellsSelected;
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.CellSelect:
                {
                    allCellsSelected = _individualSelectedCells.Count == Columns.Count * Rows.Count;
                    if (allCellsSelected || includeInvisibleCells)
                    {
                        return allCellsSelected;
                    }
                    else
                    {
                        for (int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                             rowIndex != -1;
                             rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible))
                        {
                            dataGridViewRow = Rows[rowIndex]; // un-shares this row

                            DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                            while (dataGridViewColumn is not null)
                            {
                                if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected)
                                {
                                    return false;
                                }

                                dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                                    DataGridViewElementStates.Visible,
                                    DataGridViewElementStates.None);
                            }
                        }

                        return true;
                    }
                }

            case DataGridViewSelectionMode.FullColumnSelect:
            case DataGridViewSelectionMode.ColumnHeaderSelect:
                {
                    allCellsSelected = _selectedBandIndexes.Count * Rows.Count + _individualSelectedCells.Count == Columns.Count * Rows.Count;
                    if (allCellsSelected || includeInvisibleCells)
                    {
                        return allCellsSelected;
                    }
                    else
                    {
                        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                        while (dataGridViewColumn is not null)
                        {
                            if (!_selectedBandIndexes.Contains(dataGridViewColumn.Index))
                            {
                                for (int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                                    rowIndex != -1;
                                    rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible))
                                {
                                    dataGridViewRow = Rows[rowIndex]; // un-shares this row
                                    if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected)
                                    {
                                        return false;
                                    }
                                }
                            }

                            dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                                DataGridViewElementStates.Visible,
                                DataGridViewElementStates.None);
                        }

                        return true;
                    }
                }

            case DataGridViewSelectionMode.FullRowSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
                {
                    allCellsSelected = _selectedBandIndexes.Count * Columns.Count + _individualSelectedCells.Count == Columns.Count * Rows.Count;
                    if (allCellsSelected || includeInvisibleCells)
                    {
                        return allCellsSelected;
                    }
                    else
                    {
                        for (int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                            rowIndex != -1;
                            rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible))
                        {
                            Debug.Assert(_selectedBandIndexes.Contains(rowIndex) ==
                                         ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0));
                            if ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0)
                            {
                                dataGridViewRow = Rows[rowIndex]; // un-shares this row
                                DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                                while (dataGridViewColumn is not null)
                                {
                                    if (!dataGridViewRow.Cells[dataGridViewColumn.Index].Selected)
                                    {
                                        return false;
                                    }

                                    dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                                        DataGridViewElementStates.Visible,
                                        DataGridViewElementStates.None);
                                }
                            }
                        }

                        return true;
                    }
                }
        }

        Debug.Fail("DataGridView.AreAllCellsSelected - Shouldn't reach this code");
        return false;
    }

    /// <summary>
    ///  Assigns a new parent control to the DataGridView.
    /// </summary>
    internal override void AssignParent(Control? value)
    {
        if (_toolTipControl.Activated)
        {
            _toolTipControl.Activate(activate: false);
        }

        base.AssignParent(value);
    }

    private void AutoGenerateDataBoundColumns(List<DataGridViewColumn>? boundColumns)
    {
        DataGridViewColumnCollection dataGridViewCols = Columns;
        DataGridViewColumn[] clonedColumns = new DataGridViewColumn[dataGridViewCols.Count];
        int clonedColumnsCount = 0;
        int i;

        // 1. Clone all the columns which are currently bound and will also be bound under the new DataSource/DataMember combination.
        // 2. Delete all the currently bound columns.
        // 3. Sort the cloned columns in order of their DisplayIndex.
        // 4. Add the new data bound columns. Here are the rules:
        //      a. if the cloned collection contains (possible multiple) columns with DataPropertyName == current data field,
        //          add the first cloned column that maps to the data field.
        //      b. other wise add the new bound column.
        // 5. Add the remaining cloned columns in the order of their DisplayIndex.

        // 1. Clone all the currently bound columns.
        //
        // We can't do 1. and 2. in the same loop because we need to save the DisplayIndex.
        for (i = 0; i < dataGridViewCols.Count; i++)
        {
            if (DataSource is not null
                && !string.IsNullOrEmpty(dataGridViewCols[i].DataPropertyName)
                && !dataGridViewCols[i].IsDataBound)
            {
                MapDataGridViewColumnToDataBoundField(dataGridViewCols[i]);
            }

            if (dataGridViewCols[i].IsDataBound)
            {
                // We only clone columns which are data bound w/ the new DataSource/DataMember combination.
                if (DataConnection is not null && DataConnection.BoundColumnIndex(dataGridViewCols[i].DataPropertyName) != -1)
                {
                    clonedColumns[clonedColumnsCount] = (DataGridViewColumn)dataGridViewCols[i].Clone();
                    clonedColumns[clonedColumnsCount].DisplayIndex = dataGridViewCols[i].DisplayIndex;
                    clonedColumnsCount++;
                }
            }
        }

        i = 0;
        // 2. Delete all the currently bound columns.
        while (i < dataGridViewCols.Count)
        {
            if (dataGridViewCols[i].IsDataBound)
            {
                dataGridViewCols.RemoveAtInternal(i, force: true);
            }
            else
            {
                i++;
            }
        }

        // 3. Sort the cloned columns in the order of their DisplayIndex.

        // Sort the cloned columns array by the display index.
        // We need to copy the cloned columns into a possibly smaller array.
        DataGridViewColumn?[] finalClonedColumns;
        if (clonedColumns.Length == clonedColumnsCount)
        {
            finalClonedColumns = clonedColumns;
        }
        else
        {
            finalClonedColumns = new DataGridViewColumn[clonedColumnsCount];
            Array.Copy(clonedColumns, finalClonedColumns, clonedColumnsCount);
        }

        // Sort the array.
        Array.Sort(finalClonedColumns, DataGridViewColumnCollection.ColumnCollectionOrderComparer);

        // 4. Add new columns for the Fields which were not data bound previously ( ie, for fields which do not have a clone ).
        if (boundColumns is not null)
        {
            for (int j = 0; j < boundColumns.Count; j++)
            {
                if (!boundColumns[j].IsBrowsableInternal)
                {
                    continue;
                }

                bool addNewColumn = true;
                // Go thru the list of cloned columns and see if there is another column w/ the same data property name.
                for (int clonedColIndex = 0; clonedColIndex < clonedColumnsCount; clonedColIndex++)
                {
                    var clonedColumn = finalClonedColumns[clonedColIndex];
                    if (clonedColumn is not null
                        && string.Compare(clonedColumn.DataPropertyName,
                            boundColumns[j].DataPropertyName,
                            ignoreCase: true,
                            CultureInfo.InvariantCulture) == 0)
                    {
                        // add the cloned column.
                        dataGridViewCols.Add(clonedColumn);
                        MapDataGridViewColumnToDataBoundField(clonedColumn);
                        Debug.Assert(clonedColumn.IsDataBound);
                        finalClonedColumns[clonedColIndex] = null;
                        addNewColumn = false;
                        break;
                    }
                }

                if (addNewColumn)
                {
                    dataGridViewCols.Add(boundColumns[j]);
                }
            }
        }
#if DEBUG
        else
        {
            // If there are no data bound columns then there are no cloned columns either.
            Debug.Assert(finalClonedColumns.Length == 0);
            Debug.Assert(clonedColumnsCount == 0);
        }
#endif // DEBUG

        // 5. Add remaining cloned columns.
        if (clonedColumnsCount > 0)
        {
            for (int k = 0; k < finalClonedColumns.Length; k++)
            {
                if (finalClonedColumns[k] is DataGridViewColumn dataGridViewColumn)
                {
                    dataGridViewCols.Add(dataGridViewColumn);
                    MapDataGridViewColumnToDataBoundField(dataGridViewColumn);
                    Debug.Assert(dataGridViewColumn.IsDataBound);
                }
            }
        }
    }

    private bool AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter, bool fixedHeight)
    {
        Debug.Assert(autoSizeColumnCriteriaFilter != DataGridViewAutoSizeColumnCriteriaInternal.None);
        Debug.Assert((autoSizeColumnCriteriaFilter & DataGridViewAutoSizeColumnCriteriaInternal.Fill) == 0);

        bool ret = false; // No column auto sizing by default
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        while (dataGridViewColumn is not null)
        {
            DataGridViewAutoSizeColumnCriteriaInternal inheritedAutoSizeColumnCriteria = (DataGridViewAutoSizeColumnCriteriaInternal)dataGridViewColumn.InheritedAutoSizeMode;
            DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = (inheritedAutoSizeColumnCriteria & autoSizeColumnCriteriaFilter);
            if (autoSizeColumnCriteriaFiltered != 0)
            {
                ret |= AutoResizeColumnInternal(dataGridViewColumn.Index, inheritedAutoSizeColumnCriteria, fixedHeight);
            }

            dataGridViewColumn = Columns.GetNextColumn(
                dataGridViewColumn,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
        }

        return ret;
    }

    public void AutoResizeColumn(int columnIndex)
    {
        AutoResizeColumn(columnIndex, DataGridViewAutoSizeColumnMode.AllCells);
    }

    public void AutoResizeColumn(int columnIndex, DataGridViewAutoSizeColumnMode autoSizeColumnMode)
    {
        AutoResizeColumn(columnIndex, autoSizeColumnMode, fixedHeight: true);
    }

    protected void AutoResizeColumn(int columnIndex, DataGridViewAutoSizeColumnMode autoSizeColumnMode, bool fixedHeight)
    {
        if (autoSizeColumnMode is DataGridViewAutoSizeColumnMode.NotSet
            or DataGridViewAutoSizeColumnMode.None
            or DataGridViewAutoSizeColumnMode.Fill)
        {
            throw new ArgumentException(string.Format(SR.DataGridView_NeedColumnAutoSizingCriteria, "autoSizeColumnMode"));
        }

        switch (autoSizeColumnMode)
        {
            case DataGridViewAutoSizeColumnMode.NotSet:
            case DataGridViewAutoSizeColumnMode.None:
            case DataGridViewAutoSizeColumnMode.ColumnHeader:
            case DataGridViewAutoSizeColumnMode.AllCellsExceptHeader:
            case DataGridViewAutoSizeColumnMode.AllCells:
            case DataGridViewAutoSizeColumnMode.DisplayedCellsExceptHeader:
            case DataGridViewAutoSizeColumnMode.DisplayedCells:
            case DataGridViewAutoSizeColumnMode.Fill:
                break;
            default:
                throw new InvalidEnumArgumentException(nameof(autoSizeColumnMode), (int)autoSizeColumnMode, typeof(DataGridViewAutoSizeColumnMode));
        }

        ArgumentOutOfRangeException.ThrowIfNegative(columnIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);

        if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.ColumnHeader && !ColumnHeadersVisible)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAutoSizeInvisibleColumnHeader);
        }

        AutoResizeColumnInternal(columnIndex, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, fixedHeight);
    }

    public void AutoResizeColumnHeadersHeight()
    {
        AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
    }

    public void AutoResizeColumnHeadersHeight(int columnIndex)
    {
        AutoResizeColumnHeadersHeight(columnIndex, fixedRowHeadersWidth: true, fixedColumnWidth: true);
    }

    protected void AutoResizeColumnHeadersHeight(bool fixedRowHeadersWidth, bool fixedColumnsWidth)
    {
        if (!ColumnHeadersVisible)
        {
            return;
        }

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return;
        }

        try
        {
            _noAutoSizeCount++;

            // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle
            // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids,
            // handle creation wipes out and recreates the columns/rows.
            int preferredHeight = 0;
            if (_layout.TopLeftHeader.Width > 0)
            {
                preferredHeight = fixedRowHeadersWidth
                    ? TopLeftHeaderCell.GetPreferredHeight(-1, _layout.TopLeftHeader.Width)
                    : TopLeftHeaderCell.GetPreferredSize(-1).Height;
            }

            int columnsCount = Columns.Count;
            for (int columnIndex = 0; columnIndex < columnsCount; columnIndex++)
            {
                if (Columns[columnIndex].Visible)
                {
                    preferredHeight = fixedColumnsWidth
                        ? Math.Max(preferredHeight, Columns[columnIndex].HeaderCell.GetPreferredHeight(-1, Columns[columnIndex].Thickness))
                        : Math.Max(preferredHeight, Columns[columnIndex].HeaderCell.GetPreferredSize(-1).Height);
                }
            }

            if (preferredHeight < MinimumColumnHeadersHeight)
            {
                preferredHeight = MinimumColumnHeadersHeight;
            }

            if (preferredHeight > MaxHeadersThickness)
            {
                preferredHeight = MaxHeadersThickness;
            }

            if (preferredHeight != ColumnHeadersHeight)
            {
                SetColumnHeadersHeightInternal(preferredHeight, invalidInAdjustFillingColumns: !fixedColumnsWidth);
            }
        }
        finally
        {
            Debug.Assert(_noAutoSizeCount > 0);
            _noAutoSizeCount--;
        }
    }

    protected void AutoResizeColumnHeadersHeight(int columnIndex, bool fixedRowHeadersWidth, bool fixedColumnWidth)
    {
        ArgumentOutOfRangeException.ThrowIfLessThan(columnIndex, -1);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);

        if (!ColumnHeadersVisible)
        {
            return;
        }

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return;
        }

        try
        {
            _noAutoSizeCount++;
            // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle
            // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids,
            // handle creation wipes out and recreates the columns/rows.
            int preferredHeight = 0;
            if (_layout.TopLeftHeader.Width > 0)
            {
                preferredHeight = columnIndex != -1 || fixedRowHeadersWidth
                    ? TopLeftHeaderCell.GetPreferredHeight(-1, _layout.TopLeftHeader.Width)
                    : TopLeftHeaderCell.GetPreferredSize(-1).Height;
            }

            int columnsCount = Columns.Count;
            for (int columnIndexTmp = 0; columnIndexTmp < columnsCount; columnIndexTmp++)
            {
                if (Columns[columnIndexTmp].Visible)
                {
                    preferredHeight = columnIndex != columnIndexTmp || fixedColumnWidth
                        ? Math.Max(preferredHeight, Columns[columnIndexTmp].HeaderCell.GetPreferredHeight(-1, Columns[columnIndexTmp].Thickness))
                        : Math.Max(preferredHeight, Columns[columnIndexTmp].HeaderCell.GetPreferredSize(-1).Height);
                }
            }

            if (preferredHeight < MinimumColumnHeadersHeight)
            {
                preferredHeight = MinimumColumnHeadersHeight;
            }

            if (preferredHeight > MaxHeadersThickness)
            {
                preferredHeight = MaxHeadersThickness;
            }

            if (preferredHeight != ColumnHeadersHeight)
            {
                SetColumnHeadersHeightInternal(preferredHeight, invalidInAdjustFillingColumns: !fixedColumnWidth);
            }
        }
        finally
        {
            Debug.Assert(_noAutoSizeCount > 0);
            _noAutoSizeCount--;
        }
    }

    private bool AutoResizeColumnInternal(int columnIndex, DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal, bool fixedHeight)
    {
        Debug.Assert(autoSizeColumnCriteriaInternal is DataGridViewAutoSizeColumnCriteriaInternal.Header
            or DataGridViewAutoSizeColumnCriteriaInternal.AllRows
            or DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows
            or (DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows)
            or (DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows));
        Debug.Assert(columnIndex >= 0 && columnIndex < Columns.Count);

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return false;
        }

        bool ret = false; // No autosizing occurs by default.
        try
        {
            _noAutoSizeCount++;
            // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle
            // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids,
            // handle creation wipes out and recreates the columns.
            // Note: Even none-resizable column can programmatically be resized.
            DataGridViewColumn dataGridViewColumn = Columns[columnIndex];
            int preferredColumnWidth = dataGridViewColumn.GetPreferredWidth((DataGridViewAutoSizeColumnMode)autoSizeColumnCriteriaInternal, fixedHeight);
            if (preferredColumnWidth < dataGridViewColumn.MinimumThickness)
            {
                preferredColumnWidth = dataGridViewColumn.MinimumThickness;
            }

            if (preferredColumnWidth > DataGridViewBand.MaxBandThickness)
            {
                preferredColumnWidth = DataGridViewBand.MaxBandThickness;
            }

            if (preferredColumnWidth != dataGridViewColumn.Thickness)
            {
                if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                {
                    AdjustFillingColumn(dataGridViewColumn, preferredColumnWidth);
                }
                else
                {
                    Columns[columnIndex].ThicknessInternal = preferredColumnWidth;
                }

                ret = true;
            }
        }
        finally
        {
            Debug.Assert(_noAutoSizeCount > 0);
            _noAutoSizeCount--;
        }

        return ret;
    }

    public void AutoResizeColumns()
    {
        AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
    }

    public void AutoResizeColumns(DataGridViewAutoSizeColumnsMode autoSizeColumnsMode)
    {
        AutoResizeColumns(autoSizeColumnsMode, fixedHeight: true);
    }

    protected void AutoResizeColumns(DataGridViewAutoSizeColumnsMode autoSizeColumnsMode, bool fixedHeight)
    {
        for (int columnIndex = 0; columnIndex < Columns.Count; columnIndex++)
        {
            AutoResizeColumn(columnIndex, (DataGridViewAutoSizeColumnMode)autoSizeColumnsMode, fixedHeight);
        }
    }

    public void AutoResizeRow(int rowIndex)
    {
        AutoResizeRow(rowIndex, DataGridViewAutoSizeRowMode.AllCells);
    }

    public void AutoResizeRow(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode)
    {
        AutoResizeRow(rowIndex, autoSizeRowMode, fixedWidth: true);
    }

    protected void AutoResizeRow(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rowIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        // not using ClientUtils here because it's a flags enum, masking instead.
        if (((DataGridViewAutoSizeRowCriteriaInternal)autoSizeRowMode & InvalidDataGridViewAutoSizeRowCriteriaInternalMask) != 0)
        {
            throw new InvalidEnumArgumentException(nameof(autoSizeRowMode), (int)autoSizeRowMode, typeof(DataGridViewAutoSizeRowMode));
        }

        if (autoSizeRowMode == DataGridViewAutoSizeRowMode.RowHeader && !RowHeadersVisible)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAutoSizeRowInvisibleRowHeader);
        }

        AutoResizeRowInternal(rowIndex, autoSizeRowMode, fixedWidth, internalAutosizing: false);
    }

    // User can override this if there is a quicker way to determine preferred row headers width
    public void AutoResizeRowHeadersWidth(DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode)
    {
        AutoResizeRowHeadersWidth(
            rowHeadersWidthSizeMode,
            fixedColumnHeadersHeight: true,
            fixedRowsHeight: true);
    }

    // User can override this if there is a quicker way to determine preferred row headers width
    protected void AutoResizeRowHeadersWidth(
        DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode,
        bool fixedColumnHeadersHeight,
        bool fixedRowsHeight)
    {
        if (rowHeadersWidthSizeMode is DataGridViewRowHeadersWidthSizeMode.EnableResizing or DataGridViewRowHeadersWidthSizeMode.DisableResizing)
        {
            throw new ArgumentException(string.Format(SR.DataGridView_NeedAutoSizingCriteria, "rowHeadersWidthSizeMode"));
        }

        // custom range checking, not using ClientUtils.
        if (rowHeadersWidthSizeMode is < DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders or > DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader)
        {
            throw new InvalidEnumArgumentException(nameof(rowHeadersWidthSizeMode), (int)rowHeadersWidthSizeMode, typeof(DataGridViewRowHeadersWidthSizeMode));
        }

        if (!RowHeadersVisible)
        {
            return;
        }

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return;
        }

        try
        {
            _noAutoSizeCount++;
            // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle
            // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids,
            // handle creation wipes out and recreates the columns/rows.
            int preferredWidth = 0, rowIndex;
            if (_layout.TopLeftHeader.Width > 0)
            {
                preferredWidth = fixedColumnHeadersHeight
                    ? TopLeftHeaderCell.GetPreferredWidth(-1, _layout.TopLeftHeader.Height)
                    : TopLeftHeaderCell.GetPreferredSize(-1).Width;
            }

            switch (rowHeadersWidthSizeMode)
            {
                case DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader:
                    {
                        rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                        if (rowIndex != -1)
                        {
                            preferredWidth = fixedRowsHeight
                                ? Math.Max(preferredWidth, Rows.SharedRow(rowIndex).HeaderCell.GetPreferredWidth(rowIndex, Rows.SharedRow(rowIndex).GetHeight(rowIndex)))
                                : Math.Max(preferredWidth, Rows.SharedRow(rowIndex).HeaderCell.GetPreferredSize(rowIndex).Width);
                        }

                        break;
                    }

                case DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders:
                    {
                        int displayHeight = _layout.Data.Height, cy = 0;
                        rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                        while (rowIndex != -1 && cy < displayHeight)
                        {
                            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                            int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndex);
                            cy += dataGridViewRowHeight;
                            preferredWidth = fixedRowsHeight
                                ? Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndex, dataGridViewRowHeight))
                                : Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndex).Width);

                            rowIndex = Rows.GetNextRow(
                                rowIndex,
                                DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                        }

                        if (cy < displayHeight)
                        {
                            rowIndex = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                            while (rowIndex != -1 && cy < displayHeight)
                            {
                                DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                                int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndex);
                                cy += dataGridViewRowHeight;
                                preferredWidth = fixedRowsHeight
                                    ? Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndex, dataGridViewRowHeight))
                                    : Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndex).Width);

                                rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                            }
                        }

                        break;
                    }

                case DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders:
                    {
                        for (rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                            rowIndex != -1;
                            rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible))
                        {
                            preferredWidth = fixedRowsHeight
                                ? Math.Max(preferredWidth, Rows.SharedRow(rowIndex).HeaderCell.GetPreferredWidth(rowIndex, Rows.SharedRow(rowIndex).GetHeight(rowIndex)))
                                : Math.Max(preferredWidth, Rows.SharedRow(rowIndex).HeaderCell.GetPreferredSize(rowIndex).Width);
                        }

                        break;
                    }

                default:
                    {
                        Debug.Fail("Unexpected rowHeadersWidthSizeMode value in AutoResizeRowHeadersWidth");
                        break;
                    }
            }

            if (preferredWidth < MinimumRowHeadersWidth)
            {
                preferredWidth = MinimumRowHeadersWidth;
            }

            if (preferredWidth != RowHeadersWidth)
            {
                RowHeadersWidthInternal = preferredWidth;
            }
        }
        finally
        {
            Debug.Assert(_noAutoSizeCount > 0);
            _noAutoSizeCount--;
        }
    }

    public void AutoResizeRowHeadersWidth(int rowIndex, DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode)
    {
        AutoResizeRowHeadersWidth(
            rowIndex,
            rowHeadersWidthSizeMode,
            fixedColumnHeadersHeight: true,
            fixedRowHeight: true);
    }

    protected void AutoResizeRowHeadersWidth(
        int rowIndex,
        DataGridViewRowHeadersWidthSizeMode rowHeadersWidthSizeMode,
        bool fixedColumnHeadersHeight,
        bool fixedRowHeight)
    {
        ArgumentOutOfRangeException.ThrowIfLessThan(rowIndex, -1);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        if (rowHeadersWidthSizeMode is DataGridViewRowHeadersWidthSizeMode.EnableResizing or DataGridViewRowHeadersWidthSizeMode.DisableResizing)
        {
            throw new ArgumentException(string.Format(SR.DataGridView_NeedAutoSizingCriteria, "rowHeadersWidthSizeMode"));
        }

        if (rowHeadersWidthSizeMode is < DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders or > DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader)
        {
            throw new InvalidEnumArgumentException(nameof(rowHeadersWidthSizeMode), (int)rowHeadersWidthSizeMode, typeof(DataGridViewRowHeadersWidthSizeMode));
        }

        if (!RowHeadersVisible)
        {
            return;
        }

        if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader
            && rowIndex != -1
            && rowIndex != Rows.GetFirstRow(DataGridViewElementStates.Visible))
        {
            return;
        }

        if (rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders && rowIndex != -1)
        {
            DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
            bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0;
            if (!rowDisplayed)
            {
                return;
            }
        }

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return;
        }

        try
        {
            _noAutoSizeCount++;
            // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle
            // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids,
            // handle creation wipes out and recreates the columns/rows.
            int preferredWidth = 0, rowIndexTmp;
            if (_layout.TopLeftHeader.Width > 0)
            {
                preferredWidth = rowIndex != -1 || fixedColumnHeadersHeight
                    ? TopLeftHeaderCell.GetPreferredWidth(-1, _layout.TopLeftHeader.Height)
                    : TopLeftHeaderCell.GetPreferredSize(-1).Width;
            }

            switch (rowHeadersWidthSizeMode)
            {
                case DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader:
                    {
                        rowIndexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                        if (rowIndexTmp != -1)
                        {
                            preferredWidth = rowIndex != rowIndexTmp || fixedRowHeight
                                ? Math.Max(preferredWidth, Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredWidth(rowIndexTmp, Rows.SharedRow(rowIndexTmp).GetHeight(rowIndexTmp)))
                                : Math.Max(preferredWidth, Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredSize(rowIndexTmp).Width);
                        }

                        break;
                    }

                case DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders:
                    {
                        int displayHeight = _layout.Data.Height, cy = 0;
                        rowIndexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                        while (rowIndexTmp != -1 && cy < displayHeight)
                        {
                            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndexTmp);
                            int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndexTmp);
                            cy += dataGridViewRowHeight;
                            preferredWidth = rowIndex != rowIndexTmp || fixedRowHeight
                                ? Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndexTmp, dataGridViewRowHeight))
                                : Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndexTmp).Width);

                            rowIndexTmp = Rows.GetNextRow(
                                rowIndexTmp,
                                DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                        }

                        if (cy < displayHeight)
                        {
                            rowIndexTmp = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                            while (rowIndexTmp != -1 && cy < displayHeight)
                            {
                                DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndexTmp);
                                int dataGridViewRowHeight = dataGridViewRow.GetHeight(rowIndexTmp);
                                cy += dataGridViewRowHeight;
                                preferredWidth = rowIndex != rowIndexTmp || fixedRowHeight
                                    ? Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredWidth(rowIndexTmp, dataGridViewRowHeight))
                                    : Math.Max(preferredWidth, dataGridViewRow.HeaderCell.GetPreferredSize(rowIndexTmp).Width);

                                rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible);
                            }
                        }

                        break;
                    }

                case DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders:
                    {
                        for (rowIndexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                            rowIndexTmp != -1;
                            rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible))
                        {
                            preferredWidth = rowIndex != rowIndexTmp || fixedRowHeight
                                ? Math.Max(preferredWidth, Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredWidth(rowIndexTmp, Rows.SharedRow(rowIndexTmp).GetHeight(rowIndexTmp)))
                                : Math.Max(preferredWidth, Rows.SharedRow(rowIndexTmp).HeaderCell.GetPreferredSize(rowIndexTmp).Width);
                        }

                        break;
                    }

                default:
                    {
                        Debug.Fail("Unexpected rowHeadersWidthSizeMode value in AutoResizeRowHeadersWidth");
                        break;
                    }
            }

            if (preferredWidth < MinimumRowHeadersWidth)
            {
                preferredWidth = MinimumRowHeadersWidth;
            }

            if (preferredWidth != RowHeadersWidth)
            {
                RowHeadersWidthInternal = preferredWidth;
            }
        }
        finally
        {
            Debug.Assert(_noAutoSizeCount > 0);
            _noAutoSizeCount--;
        }
    }

    private void AutoResizeRowInternal(int rowIndex, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth, bool internalAutosizing)
    {
        Debug.Assert(rowIndex >= 0 && rowIndex < Rows.Count);
        Debug.Assert(((DataGridViewAutoSizeRowCriteriaInternal)autoSizeRowMode & InvalidDataGridViewAutoSizeRowCriteriaInternalMask) == 0);

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return;
        }

        try
        {
            _noAutoSizeCount++;
            // Use of WindowsFormsUtils.CreateMeasurementGraphics() avoid use of this.Handle
            // IntPtr handle = this.Handle; // Force creation of control's handle because for databound grids,
            // handle creation wipes out and recreates the columns/rows.
            // Note: Even none-resizable row can programmatically be resized.
            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
            dataGridViewRow.GetHeightInfo(rowIndex, out int height, out int minimumHeight);
            int preferredThickness = dataGridViewRow.GetPreferredHeight(rowIndex, autoSizeRowMode, fixedWidth);
            if (preferredThickness < minimumHeight)
            {
                preferredThickness = minimumHeight;
            }

            if (preferredThickness > DataGridViewBand.MaxBandThickness)
            {
                preferredThickness = DataGridViewBand.MaxBandThickness;
            }

            if (height != preferredThickness)
            {
                if (_autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None)
                {
                    if (!OnRowHeightInfoPushed(rowIndex, preferredThickness, minimumHeight))
                    {
                        Rows[rowIndex].ThicknessInternal = preferredThickness;   // un-sharing the resized row
                    }
                }
                else
                {
                    if (internalAutosizing)
                    {
                        Rows[rowIndex].ThicknessInternal = preferredThickness;   // un-sharing the resized row
                    }
                    else
                    {
                        Rows[rowIndex].Thickness = preferredThickness;   // un-sharing the resized row
                    }
                }
            }
        }
        finally
        {
            Debug.Assert(_noAutoSizeCount > 0);
            _noAutoSizeCount--;
        }
    }

    public void AutoResizeRows()
    {
        AutoResizeRows(DataGridViewAutoSizeRowsMode.AllCells);
    }

    public void AutoResizeRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode)
    {
        AutoResizeRows(autoSizeRowsMode, fixedWidth: true);
    }

    protected void AutoResizeRows(DataGridViewAutoSizeRowsMode autoSizeRowsMode, bool fixedWidth)
    {
        switch (autoSizeRowsMode)
        {
            case DataGridViewAutoSizeRowsMode.None:
            case DataGridViewAutoSizeRowsMode.AllHeaders:
            case DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders:
            case DataGridViewAutoSizeRowsMode.AllCells:
            case DataGridViewAutoSizeRowsMode.DisplayedHeaders:
            case DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders:
            case DataGridViewAutoSizeRowsMode.DisplayedCells:
                break;
            default:
                throw new InvalidEnumArgumentException(nameof(autoSizeRowsMode), (int)autoSizeRowsMode, typeof(DataGridViewAutoSizeRowsMode));
        }

        if (autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None)
        {
            throw new ArgumentException(string.Format(SR.DataGridView_NeedAutoSizingCriteria, "autoSizeRowsMode"));
        }

        if ((autoSizeRowsMode == DataGridViewAutoSizeRowsMode.AllHeaders || autoSizeRowsMode == DataGridViewAutoSizeRowsMode.DisplayedHeaders)
            && !RowHeadersVisible)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAutoSizeRowsInvisibleRowHeader);
        }

        AdjustShrinkingRows(autoSizeRowsMode, fixedWidth, internalAutosizing: false);
    }

    protected void AutoResizeRows(int rowIndexStart, int rowsCount, DataGridViewAutoSizeRowMode autoSizeRowMode, bool fixedWidth)
    {
        // not using EnumValidator.Validate here because DataGridViewAutoSizeRowCriteriaInternal is a flags enum.
        if (((DataGridViewAutoSizeRowCriteriaInternal)autoSizeRowMode & InvalidDataGridViewAutoSizeRowCriteriaInternalMask) != 0)
        {
            throw new InvalidEnumArgumentException(nameof(autoSizeRowMode), (int)autoSizeRowMode, typeof(DataGridViewAutoSizeRowMode));
        }

        if (autoSizeRowMode == DataGridViewAutoSizeRowMode.RowHeader && !RowHeadersVisible)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAutoSizeRowsInvisibleRowHeader);
        }

        ArgumentOutOfRangeException.ThrowIfNegative(rowsCount);

        ArgumentOutOfRangeException.ThrowIfNegative(rowIndexStart);

        if (!IsHandleCreated)
        {
            // auto sizing causes handle creation.
            // don't create the handle inside InitializeComponent because that causes problems w/ data binding
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = true;
            return;
        }

        _inBulkPaintCount++;
        _inBulkLayoutCount++;
        try
        {
            int rowIndex = Rows.GetNextRow(rowIndexStart - 1, DataGridViewElementStates.Visible);
            int autoSizedCount = 0;
            while (rowIndex != -1 && autoSizedCount < rowsCount)
            {
                AutoResizeRowInternal(rowIndex, autoSizeRowMode, fixedWidth, internalAutosizing: false);
                autoSizedCount++;
                if (autoSizedCount < rowsCount)
                {
                    rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                }
            }
        }
        finally
        {
            ExitBulkLayout(invalidInAdjustFillingColumns: true);
            ExitBulkPaint(-1, -1);
        }
    }

    private void BeginColumnHeadersResize(int mouseY, int mouseBarOffset)
    {
        if (IsKeyboardOperationActive())
        {
            return;
        }

        Rectangle clip = Rectangle.Union(_layout.ColumnHeaders, _layout.Data);
        if (_layout.TopLeftHeader.Width > 0)
        {
            clip = Rectangle.Union(_layout.TopLeftHeader, clip);
        }

        clip.Y += MinimumColumnHeadersHeight - mouseBarOffset - 1;
        // No need to limit the bottom edge of the cursor clip since maxHeadersThickness is very large.
        CaptureMouse(clip);
        _dataGridViewOper[OperationTrackColHeadersResize] = true;
        _trackRowAnchor = mouseY;
        _mouseBarOffset = mouseBarOffset;
        Debug.Assert(_lastRowSplitBar == -1);
        _currentRowSplitBar = mouseY;
        Invalidate(CalcRowResizeFeedbackRect(_currentRowSplitBar));
    }

    private void BeginColumnRelocation(int mouseX, int index)
    {
        if (IsKeyboardOperationActive())
        {
            return;
        }

        Rectangle cursorClip = _layout.ColumnHeaders;
        int frozenWidth = Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        int scrollingWidth = Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - frozenWidth;
        if (Columns[index].Frozen)
        {
            // A frozen column cannot be relocated into an unfrozen area
            if (RightToLeftInternal)
            {
                cursorClip.X += cursorClip.Width - frozenWidth;
            }

            cursorClip.Width = Math.Min(frozenWidth, _layout.Data.Width);
        }
        else
        {
            // An unfrozen column cannot be relocated into a frozen area
            if (!RightToLeftInternal)
            {
                cursorClip.X += frozenWidth;
            }
            else if (_layout.Data.Width > frozenWidth + scrollingWidth)
            {
                cursorClip.X += _layout.Data.Width - frozenWidth - scrollingWidth;
            }

            cursorClip.Width = Math.Min(scrollingWidth, _layout.Data.Width);
        }

        CaptureMouse(cursorClip);
        _dataGridViewOper[OperationTrackColRelocation] = true;
        _dataGridViewState2[State2_ShowColumnRelocationInsertion] = false;
        _trackColumn = index;
        _trackColumnEdge = -1;

        _mouseBarOffset = GetColumnXFromIndex(index) - mouseX;
        _lastHeaderShadow = mouseX;
        Invalidate(_layout.ColumnHeaders);
    }

    private void BeginColumnResize(int x, int columnIndex)
    {
        _trackColAnchor = x;
        _trackColumn = columnIndex;

        _currentColSplitBar = x;
        Invalidate(CalcColResizeFeedbackRect(_currentColSplitBar));
    }

    private void BeginMouseColumnResize(int mouseX, int mouseBarOffset, int index)
    {
        if (IsKeyboardOperationActive())
        {
            return;
        }

        _dataGridViewOper[OperationTrackColResize] = true;
        _mouseBarOffset = mouseBarOffset;
        _resizeClipRectangle = GetResizeClipRectangle(index);
        CaptureMouse(_resizeClipRectangle);

        BeginColumnResize(mouseX, index);
    }

    private void BeginKeyboardColumnResize(int columnIndex)
    {
        if (IsMouseOperationActive())
        {
            return;
        }

        _dataGridViewOper[OperationTrackKeyboardColResize] = true;
        _mouseBarOffset = 0;
        _resizeClipRectangle = GetResizeClipRectangle(columnIndex);
        _keyboardResizeStep = LogicalToDeviceUnits(RightToLeftInternal ? -1 : 1);
        int x = GetColumnXFromIndex(columnIndex);
        x += RightToLeftInternal ? -Columns[columnIndex].Width : Columns[columnIndex].Width;

        BeginColumnResize(x, columnIndex);
    }

    private Rectangle GetResizeClipRectangle(int columnIndex)
    {
        Rectangle clip = Rectangle.Union(_layout.ColumnHeaders, _layout.Data);
        int leftEdge = GetColumnXFromIndex(columnIndex);
        if (RightToLeftInternal)
        {
            clip.X = _layout.Data.X - _mouseBarOffset - 1;
            clip.Width = leftEdge - Columns[columnIndex].MinimumThickness - _layout.Data.X + 3;
            int overflowWidth = leftEdge - _mouseBarOffset - clip.Left - DataGridViewBand.MaxBandThickness + 1;
            if (overflowWidth > 0)
            {
                clip.X += overflowWidth;
                clip.Width -= overflowWidth;
            }
        }
        else
        {
            clip.X = leftEdge + Columns[columnIndex].MinimumThickness - _mouseBarOffset - 1;
            clip.Width = _layout.Data.Right - leftEdge - 1;
            int overflowWidth = clip.Right + _mouseBarOffset - leftEdge - DataGridViewBand.MaxBandThickness;
            if (overflowWidth > 0)
            {
                clip.Width -= overflowWidth;
            }
        }

        return clip;
    }

    public virtual bool BeginEdit(bool selectAll) => _ptCurrentCell.X == -1
        ? throw new InvalidOperationException(SR.DataGridView_NoCurrentCell)
        : IsCurrentCellInEditMode || BeginEditInternal(selectAll);

    private bool BeginEditInternal(bool selectAll)
    {
        if (_dataGridViewOper[OperationInBeginEdit])
        {
            throw new InvalidOperationException(SR.DataGridView_BeginEditNotReentrant);
        }

        try
        {
            _dataGridViewOper[OperationInBeginEdit] = true;
            Debug.Assert(_ptCurrentCell.X >= 0 && _ptCurrentCell.X < Columns.Count);
            Debug.Assert(_ptCurrentCell.Y >= 0 && _ptCurrentCell.Y < Rows.Count);
            Debug.Assert(!IsCurrentCellInEditMode);

            DataGridViewCell dataGridViewCell = CurrentCellInternal;
            ThrowInvalidOperationExceptionIfNull(dataGridViewCell);

            if (IsSharedCellReadOnly(dataGridViewCell, _ptCurrentCell.Y) || !ColumnEditable(_ptCurrentCell.X))
            {
                return false;
            }

            Type? editControlType = dataGridViewCell.EditType;
            if (editControlType is null)
            {
                // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell?
                Type? editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell");
                if (editingCellInterface is null)
                {
                    return false;
                }
            }

            DataGridViewCellCancelEventArgs dgvcce = new(_ptCurrentCell.X, _ptCurrentCell.Y);
            OnCellBeginEdit(dgvcce);
            if (dgvcce.Cancel)
            {
                return false;
            }

            Debug.Assert(!IsCurrentCellInEditMode);

            if (_ptCurrentCell.X > -1)
            {
                DataGridViewCell previousCurrentCell = dataGridViewCell;
                dataGridViewCell = CurrentCellInternal;
                if (previousCurrentCell != dataGridViewCell)
                {
                    // The new current cell can be a whole different cell.
                    // In that case, all tests previously done are no longer valid.
                    if (IsSharedCellReadOnly(dataGridViewCell, _ptCurrentCell.Y) || !ColumnEditable(_ptCurrentCell.X))
                    {
                        return false;
                    }

                    editControlType = dataGridViewCell.EditType;
                    if (editControlType is null)
                    {
                        // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell?
                        Type? editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell");
                        if (editingCellInterface is null)
                        {
                            return false;
                        }
                    }
                }
            }
            else
            {
                return false;
            }

            DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCell.GetInheritedStyle(inheritedCellStyle: null, _ptCurrentCell.Y, includeColors: true);

            if (editControlType is null)
            {
                _dataGridViewState1[State1_CurrentCellInEditMode] = true;
                InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCell);
                ((IDataGridViewEditingCell)dataGridViewCell).PrepareEditingCellForEdit(selectAll);
                return true;
            }

            Type? editingCtrlInterface = editControlType.GetInterface("System.Windows.Forms.IDataGridViewEditingControl");
            if (!editControlType.IsSubclassOf(Type.GetType("System.Windows.Forms.Control")!) || editingCtrlInterface is null)
            {
                throw new InvalidCastException(SR.DataGridView_InvalidEditingControl);
            }

            if (_latestEditingControl is not null
                && editControlType.IsInstanceOfType(_latestEditingControl)
                && !_latestEditingControl.GetType().IsSubclassOf(editControlType))
            {
                EditingControl = _latestEditingControl;
                Debug.Assert(((IDataGridViewEditingControl)EditingControl).EditingControlDataGridView == this);
            }
            else
            {
                Debug.Assert(EditingControl is null);
                EditingControl = (Control)Activator.CreateInstance(editControlType)!;
                Debug.Assert(EditingControl is not null);

                ((IDataGridViewEditingControl)EditingControl).EditingControlDataGridView = this;
                if (_latestEditingControl is not null)
                {
                    _latestEditingControl.Dispose();
                    _latestEditingControl = null;
                }
            }

            Debug.Assert(EditingControl is not null);
            if (string.IsNullOrEmpty(EditingControl.AccessibleName))
            {
                EditingControl.AccessibleName = SR.DataGridView_AccEditingControlAccName;
            }

            EditingControl.ImeMode = ImeMode;

            ((IDataGridViewEditingControl)EditingControl).EditingControlRowIndex = _ptCurrentCell.Y;

            InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCell);

            WireEditingControlEvents();

            Debug.Assert(EditingControl is not null);
            Debug.Assert(_editingPanel is not null);
            DataGridViewEditingControlShowingEventArgs dgvese = new(EditingControl, dataGridViewCellStyle);
            OnEditingControlShowing(dgvese);
            Debug.Assert(dgvese.CellStyle is not null);
            if (_editingPanel is null || EditingControl is null)
            {
                return false;
            }

            _editingPanel.BackColor = dgvese.CellStyle.BackColor;
            ((IDataGridViewEditingControl)EditingControl).ApplyCellStyleToEditingControl(dgvese.CellStyle);

            // Get rid of the tooltip if it's showing for the current cell
            if (_toolTipControl.Activated && _ptToolTipCell == _ptCurrentCell)
            {
                _toolTipControl.Activate(activate: false);
            }

            PositionEditingControl(setLocation: true, setSize: true, setFocus: true);

            // Guarding against bugs in customer code.
            // For example setting the CurrentCell to null in DataGridView_OnLostFocus(...) causes this.editingControl
            // to become null.
            if (_editingPanel is null || EditingControl is null)
            {
                return false;
            }
            else
            {
                ((IDataGridViewEditingControl)EditingControl).PrepareEditingControlForEdit(selectAll);
                InvalidateCellPrivate(_ptCurrentCell.X, _ptCurrentCell.Y);
                return true;
            }
        }
        finally
        {
            _dataGridViewOper[OperationInBeginEdit] = false;
        }
    }

    private void BeginRowHeadersResize(int mouseX, int mouseBarOffset)
    {
        if (IsKeyboardOperationActive())
        {
            return;
        }

        Rectangle clip = Rectangle.Union(_layout.RowHeaders, _layout.Data);
        if (_layout.TopLeftHeader.Width > 0)
        {
            clip = Rectangle.Union(_layout.TopLeftHeader, clip);
        }

        if (RightToLeftInternal)
        {
            clip.X -= mouseBarOffset + 1;
            clip.Width -= MinimumRowHeadersWidth - 1;
            // No need to limit the left edge of the cursor clip since maxHeadersThickness is very large.
        }
        else
        {
            clip.X += MinimumRowHeadersWidth - mouseBarOffset - 1;
            // No need to limit the right edge of the cursor clip since maxHeadersThickness is very large.
        }

        CaptureMouse(clip);

        _dataGridViewOper[OperationTrackRowHeadersResize] = true;
        _trackColAnchor = mouseX;
        _mouseBarOffset = mouseBarOffset;
        Debug.Assert(_lastColSplitBar == -1);
        _currentColSplitBar = mouseX;
        Invalidate(CalcColResizeFeedbackRect(_currentColSplitBar));
    }

    private void BeginRowResize(int mouseY, int mouseBarOffset, int index)
    {
        if (IsKeyboardOperationActive())
        {
            return;
        }

        Rectangle clip = Rectangle.Union(_layout.RowHeaders, _layout.Data);
        int topEdge = GetRowYFromIndex(index);
        clip.Y = topEdge + Rows.SharedRow(index).GetMinimumHeight(index) - mouseBarOffset - 1;
        clip.Height = _layout.Data.Y + _layout.Data.Height - topEdge - 1;
        CaptureMouse(clip);

        _dataGridViewOper[OperationTrackRowResize] = true;
        _trackRowAnchor = mouseY;
        _trackRow = index;

        _mouseBarOffset = mouseBarOffset;
        Debug.Assert(_lastRowSplitBar == -1);
        _currentRowSplitBar = mouseY;
        Invalidate(CalcRowResizeFeedbackRect(_currentRowSplitBar));
    }

    private void BuildInheritedColumnHeaderCellStyle(DataGridViewCellStyle inheritedCellStyle, DataGridViewCell cell)
    {
        Debug.Assert(inheritedCellStyle is not null);

        DataGridViewCellStyle? cellStyle = null;
        if (cell.HasStyle)
        {
            cellStyle = cell.Style;
            Debug.Assert(cellStyle is not null);
        }

        DataGridViewCellStyle columnHeadersStyle = ColumnHeadersDefaultCellStyle;
        Debug.Assert(columnHeadersStyle is not null);

        DataGridViewCellStyle dataGridViewStyle = DefaultCellStyle;
        Debug.Assert(dataGridViewStyle is not null);

        if (cellStyle is not null && !cellStyle.BackColor.IsEmpty)
        {
            inheritedCellStyle.BackColor = cellStyle.BackColor;
        }
        else if (!columnHeadersStyle.BackColor.IsEmpty)
        {
            inheritedCellStyle.BackColor = columnHeadersStyle.BackColor;
        }
        else
        {
            inheritedCellStyle.BackColor = dataGridViewStyle.BackColor;
        }

        if (cellStyle is not null && !cellStyle.ForeColor.IsEmpty)
        {
            inheritedCellStyle.ForeColor = cellStyle.ForeColor;
        }
        else if (!columnHeadersStyle.ForeColor.IsEmpty)
        {
            inheritedCellStyle.ForeColor = columnHeadersStyle.ForeColor;
        }
        else
        {
            inheritedCellStyle.ForeColor = dataGridViewStyle.ForeColor;
        }

        if (cellStyle is not null && !cellStyle.SelectionBackColor.IsEmpty)
        {
            inheritedCellStyle.SelectionBackColor = cellStyle.SelectionBackColor;
        }
        else if (!columnHeadersStyle.SelectionBackColor.IsEmpty)
        {
            inheritedCellStyle.SelectionBackColor = columnHeadersStyle.SelectionBackColor;
        }
        else
        {
            inheritedCellStyle.SelectionBackColor = dataGridViewStyle.SelectionBackColor;
        }

        if (cellStyle is not null && !cellStyle.SelectionForeColor.IsEmpty)
        {
            inheritedCellStyle.SelectionForeColor = cellStyle.SelectionForeColor;
        }
        else if (!columnHeadersStyle.SelectionForeColor.IsEmpty)
        {
            inheritedCellStyle.SelectionForeColor = columnHeadersStyle.SelectionForeColor;
        }
        else
        {
            inheritedCellStyle.SelectionForeColor = dataGridViewStyle.SelectionForeColor;
        }

        inheritedCellStyle.Font = cellStyle?.Font ?? columnHeadersStyle.Font ?? dataGridViewStyle.Font;

        if (cellStyle is not null && !cellStyle.IsNullValueDefault)
        {
            inheritedCellStyle.NullValue = cellStyle.NullValue;
        }
        else if (!columnHeadersStyle.IsNullValueDefault)
        {
            inheritedCellStyle.NullValue = columnHeadersStyle.NullValue;
        }
        else
        {
            inheritedCellStyle.NullValue = dataGridViewStyle.NullValue;
        }

        if (cellStyle is not null && !cellStyle.IsDataSourceNullValueDefault)
        {
            inheritedCellStyle.DataSourceNullValue = cellStyle.DataSourceNullValue;
        }
        else if (!columnHeadersStyle.IsDataSourceNullValueDefault)
        {
            inheritedCellStyle.DataSourceNullValue = columnHeadersStyle.DataSourceNullValue;
        }
        else
        {
            inheritedCellStyle.DataSourceNullValue = dataGridViewStyle.DataSourceNullValue;
        }

        if (cellStyle is not null && cellStyle.Format.Length != 0)
        {
            inheritedCellStyle.Format = cellStyle.Format;
        }
        else if (columnHeadersStyle.Format.Length != 0)
        {
            inheritedCellStyle.Format = columnHeadersStyle.Format;
        }
        else
        {
            inheritedCellStyle.Format = dataGridViewStyle.Format;
        }

        if (cellStyle is not null && !cellStyle.IsFormatProviderDefault)
        {
            inheritedCellStyle.FormatProvider = cellStyle.FormatProvider;
        }
        else if (!columnHeadersStyle.IsFormatProviderDefault)
        {
            inheritedCellStyle.FormatProvider = columnHeadersStyle.FormatProvider;
        }
        else
        {
            inheritedCellStyle.FormatProvider = dataGridViewStyle.FormatProvider;
        }

        if (cellStyle is not null && cellStyle.Alignment != DataGridViewContentAlignment.NotSet)
        {
            inheritedCellStyle.AlignmentInternal = cellStyle.Alignment;
        }
        else if (columnHeadersStyle is not null && columnHeadersStyle.Alignment != DataGridViewContentAlignment.NotSet)
        {
            inheritedCellStyle.AlignmentInternal = columnHeadersStyle.Alignment;
        }
        else
        {
            Debug.Assert(dataGridViewStyle.Alignment != DataGridViewContentAlignment.NotSet);
            inheritedCellStyle.AlignmentInternal = dataGridViewStyle.Alignment;
        }

        if (cellStyle is not null && cellStyle.WrapMode != DataGridViewTriState.NotSet)
        {
            inheritedCellStyle.WrapModeInternal = cellStyle.WrapMode;
        }
        else if (columnHeadersStyle is not null && columnHeadersStyle.WrapMode != DataGridViewTriState.NotSet)
        {
            inheritedCellStyle.WrapModeInternal = columnHeadersStyle.WrapMode;
        }
        else
        {
            Debug.Assert(dataGridViewStyle.WrapMode != DataGridViewTriState.NotSet);
            inheritedCellStyle.WrapModeInternal = dataGridViewStyle.WrapMode;
        }

        if (cellStyle is not null && cellStyle.Tag is not null)
        {
            inheritedCellStyle.Tag = cellStyle.Tag;
        }
        else if (columnHeadersStyle!.Tag is not null)
        {
            inheritedCellStyle.Tag = columnHeadersStyle.Tag;
        }
        else
        {
            inheritedCellStyle.Tag = dataGridViewStyle.Tag;
        }

        if (cellStyle is not null && cellStyle.Padding != Padding.Empty)
        {
            inheritedCellStyle.PaddingInternal = cellStyle.Padding;
        }
        else if (columnHeadersStyle!.Padding != Padding.Empty)
        {
            inheritedCellStyle.PaddingInternal = columnHeadersStyle.Padding;
        }
        else
        {
            inheritedCellStyle.PaddingInternal = dataGridViewStyle.Padding;
        }
    }

    private Rectangle CalcColRelocationFeedbackRect(int mouseX)
    {
        Rectangle r, inside = _layout.ColumnHeaders;
        if (_layout.TopLeftHeader.Width > 0)
        {
            inside = Rectangle.Union(_layout.TopLeftHeader, inside);
        }

        if (RightToLeftInternal)
        {
            r = new Rectangle(
                mouseX + _mouseBarOffset - Columns[_trackColumn].Thickness + 1,
                inside.Y,
                Columns[_trackColumn].Thickness,
                inside.Height);
            r.X = Math.Max(inside.Left, r.X);
            r.X = Math.Min(r.X, inside.Right - r.Width);
        }
        else
        {
            r = new Rectangle(
                mouseX + _mouseBarOffset - 1,
                inside.Y,
                Columns[_trackColumn].Thickness,
                inside.Height);
            r.X = Math.Min(inside.Right - r.Width, r.X);
            r.X = Math.Max(r.X, inside.Left);
        }

        return r;
    }

    private Rectangle CalcColResizeFeedbackRect(int mouseX)
    {
        Rectangle inside = _layout.Data;
        Rectangle r = new(
            mouseX + _mouseBarOffset - 1,
            inside.Y,
            3,
            inside.Height);
        if (RightToLeftInternal)
        {
            r.X = Math.Max(inside.Left, r.X);
        }
        else
        {
            r.X = Math.Min(inside.Right - 3, r.X);
            r.X = Math.Max(r.X, 0);
        }

        return r;
    }

    private Rectangle CalcRowResizeFeedbackRect(int mouseY)
    {
        Rectangle inside = _layout.Data;
        Rectangle r = new(
            inside.X,
            mouseY + _mouseBarOffset - 1,
            inside.Width,
            3);
        r.Y = Math.Min(inside.Bottom - 3, r.Y);
        r.Y = Math.Max(r.Y, 0);
        return r;
    }

    public bool CancelEdit()
    {
        return CancelEdit(endEdit: false);
    }

    private bool CancelEdit(bool endEdit)
    {
        if (_ptCurrentCell.X != -1)
        {
            Debug.Assert(_ptCurrentCell.Y != -1);

            int oldCurrentCellX = _ptCurrentCell.X;
            DataGridViewDataErrorEventArgs? dgvdee = CancelEditPrivate();

            if (dgvdee is not null)
            {
                if (dgvdee.ThrowException)
                {
                    throw dgvdee.Exception;
                }

                if (dgvdee.Cancel)
                {
                    return false;
                }
            }

            if (IsCurrentCellInEditMode)
            {
                if (endEdit && EditMode != DataGridViewEditMode.EditOnEnter && EditingControl is not null)
                {
                    bool success = EndEdit(
                        DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.InitialValueRestoration,
                        DataGridViewValidateCellInternal.Never,
                        fireCellLeave: false,
                        fireCellEnter: false,
                        fireRowLeave: false,
                        fireRowEnter: false,
                        fireLeave: false,
                        keepFocus: true,
                        resetCurrentCell: true,
                        resetAnchorCell: true);
                    Debug.Assert(success);
                }
                else
                {
                    DataGridViewDataErrorEventArgs? dgvdee2 = null;
                    IDataGridViewEditingCell? dataGridViewEditingCell = null;
                    try
                    {
                        _dataGridViewState1[State1_IgnoringEditingChanges] = true;
                        if (EditingControl is not null)
                        {
                            ((IDataGridViewEditingControl)EditingControl).EditingControlFormattedValue = _uneditedFormattedValue;
                            ((IDataGridViewEditingControl)EditingControl).EditingControlValueChanged = false;
                        }
                        else
                        {
                            Debug.Assert(_dataGridViewState1[State1_CurrentCellInEditMode]);
                            dataGridViewEditingCell = CurrentCellInternal as IDataGridViewEditingCell;
                            Debug.Assert(dataGridViewEditingCell is not null);
                            dataGridViewEditingCell.EditingCellFormattedValue = _uneditedFormattedValue;
                            dataGridViewEditingCell.EditingCellValueChanged = false;
                        }
                    }
                    catch (Exception exception) when (!exception.IsCriticalException())
                    {
                        dgvdee2 = new(
                            exception,
                            _ptCurrentCell.X,
                            _ptCurrentCell.Y,
                            DataGridViewDataErrorContexts.InitialValueRestoration);
                    }
                    finally
                    {
                        _dataGridViewState1[State1_IgnoringEditingChanges] = false;
                    }

                    if (dgvdee2 is not null)
                    {
                        OnDataErrorInternal(dgvdee2);
                        if (dgvdee2.ThrowException)
                        {
                            throw dgvdee2.Exception;
                        }
                    }

                    if (EditingControl is not null)
                    {
                        ((IDataGridViewEditingControl)EditingControl).PrepareEditingControlForEdit(selectAll: true);
                    }
                    else
                    {
                        Debug.Assert(dataGridViewEditingCell is not null);
                        dataGridViewEditingCell.PrepareEditingCellForEdit(selectAll: true);
                        InvalidateCellPrivate(_ptCurrentCell.X, _ptCurrentCell.Y);
                    }
                }
            }
            else if (_ptCurrentCell.X == -1 && Focused)
            {
                Debug.Assert((AllowUserToAddRowsInternal && Rows.Count == 1) || (!AllowUserToAddRowsInternal && Rows.Count == 0));

                if (Rows.Count > 0)
                {
                    if (Columns.Count > oldCurrentCellX && Columns[oldCurrentCellX].Visible)
                    {
                        int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                        if (rowIndex != -1)
                        {
                            bool success = SetAndSelectCurrentCellAddress(
                                oldCurrentCellX,
                                rowIndex,
                                setAnchorCellAddress: true,
                                validateCurrentCell: false,
                                throughMouseClick: false,
                                clearSelection: false,
                                forceCurrentCellSelection: false);
                            Debug.Assert(success);
                        }
                    }
                    else
                    {
                        MakeFirstDisplayedCellCurrentCell(includeNewRow: true);
                    }
                }
            }
        }

        return true;
    }

    private DataGridViewDataErrorEventArgs? CancelEditPrivate()
    {
        bool currentCellDirty = IsCurrentCellDirty;
        bool currentRowDirty = IsCurrentRowDirty;

        if (IsCurrentCellInEditMode)
        {
            // Do not push original value back into the cell.
            if (EditingControl is not null)
            {
                ((IDataGridViewEditingControl)EditingControl).EditingControlValueChanged = false;
            }
            else
            {
                Debug.Assert(_dataGridViewState1[State1_CurrentCellInEditMode]);
                ((IDataGridViewEditingCell)CurrentCellInternal).EditingCellValueChanged = false;
            }

            IsCurrentCellDirtyInternal = false;
        }

        if (DataSource is not null || VirtualMode)
        {
            if ((currentRowDirty && !currentCellDirty)
                || (_dataGridViewState1[State1_NewRowEdited] && !_dataGridViewState1[State1_EditedRowChanged]))
            {
                bool discardNewRow = _dataGridViewState1[State1_NewRowEdited];
                IsCurrentRowDirtyInternal = false;
                if (VirtualMode)
                {
                    QuestionEventArgs qe = new(discardNewRow);
                    OnCancelRowEdit(qe);
                    discardNewRow &= qe.Response;
                }

                if (DataSource is not null)
                {
                    int oldCurrentCellX = _ptCurrentCell.X;
                    DataConnection!.CancelRowEdit(restoreRow: true, addNewFinished: _dataGridViewState1[State1_NewRowEdited]);
                    if ((DataConnection.List?.Count ?? 0) == 0)
                    {
                        // There are no rows left in the back end.
                        if (currentCellDirty || _ptCurrentCell.Y == -1 || _ptCurrentCell.X == -1)
                        {
                            if (!IsColumnOutOfBounds(oldCurrentCellX) && Columns[oldCurrentCellX].Visible)
                            {
                                Debug.Assert(Rows.GetFirstRow(DataGridViewElementStates.Visible) == 0);
                                // Setting the current cell to the current column in the first row
                                // will create the new row if the user was editing the cell.
                                SetAndSelectCurrentCellAddress(
                                    oldCurrentCellX,
                                    rowIndex: 0,
                                    setAnchorCellAddress: true,
                                    validateCurrentCell: false,
                                    throughMouseClick: false,
                                    clearSelection: true,
                                    forceCurrentCellSelection: false);
                            }
                        }
                        else
                        {
                            // Else, simply add a new row.
                            DataConnection.OnNewRowNeeded();
                        }
                    }

                    // CancelRowEdit discarded the new row if we were editing the new row.
                    discardNewRow = false;
                }

                if (_ptCurrentCell.Y > -1)
                {
                    InvalidateRowPrivate(_ptCurrentCell.Y);
                    DataGridViewCell dataGridViewCell = CurrentCellInternal;
                    if (IsCurrentCellInEditMode)
                    {
                        DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCell.GetInheritedStyle(inheritedCellStyle: null, _ptCurrentCell.Y, includeColors: true);
                        if (EditingControl is not null)
                        {
                            InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCell);
                            if (((IDataGridViewEditingControl)EditingControl).RepositionEditingControlOnValueChange)
                            {
                                PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
                            }
                        }
                        else
                        {
                            Debug.Assert(_dataGridViewState1[State1_CurrentCellInEditMode]);
                            InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCell);
                        }
                    }
                }

                if (discardNewRow && _ptCurrentCell.Y == NewRowIndex - 1)
                {
                    DiscardNewRow();
                }
            }
        }
        else
        {
            if (!IsCurrentRowDirty && _ptCurrentCell.Y == NewRowIndex - 1 && _dataGridViewState1[State1_NewRowCreatedByEditing])
            {
                DiscardNewRow();
            }
        }

        return null;
    }

    internal bool CancelToolTipPopup(ToolTip toolTip)
    {
        if (_toolTipControl.ToolTip == toolTip || KeyboardToolTip == toolTip)
        {
            // Our own tool tip wants to show its text.
            return false;
        }
        else
        {
            // This is an external tool tip control which wants to show a tool tip over the DataGridView.
            // ToolTips from the data Grid view ( the error text, or the formatted text that does not fit in,
            // or the tool tip text from the cell) and the ShowCellToolTips take precedence over the external tool tip.
            return string.IsNullOrEmpty(ToolTipPrivate) && ShowCellToolTips;
        }
    }

    private bool CanSort(DataGridViewColumn dataGridViewColumn)
    {
        return dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && (!VirtualMode || dataGridViewColumn.IsDataBound);
    }

    private bool IsSortable(DataGridViewColumn dataGridViewColumn)
    {
        return dataGridViewColumn.SortMode != DataGridViewColumnSortMode.NotSortable && (!VirtualMode || dataGridViewColumn.IsDataBound);
    }

    // determines if a data bound cell can be validated or not
    private bool CanValidateDataBoundDataGridViewCell(DataGridViewCell? dataGridViewCurrentCell)
    {
        if (dataGridViewCurrentCell is null)
        {
            if (_ptCurrentCell.X > -1)
            {
                dataGridViewCurrentCell = CurrentCellInternal;
            }
        }

        if (dataGridViewCurrentCell is null)
        {
            return true;
        }

        Debug.Assert(dataGridViewCurrentCell.OwningColumn is not null);

        if (!dataGridViewCurrentCell.OwningColumn.IsDataBoundInternal)
        {
            // we are not data bound so it's not up to us to decide to stop validation
            return true;
        }

        if (_dataGridViewOper[OperationInDispose])
        {
            // Dispose is not the place to validate data. Also, chances are that the data source is also disposing itself.
            return false;
        }

        if (DataConnection is null)
        {
            // if there is no dataConnection then it is not up to this function to stop validation.
            return true;
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////
        //                                                                                             //
        // FROM HERE DOWN THE DATA CONNECTION DETERMINES IF THE DATAGRIDVIEW SHOULD VALIDATE THE CELL. //
        //                                                                                             //
        /////////////////////////////////////////////////////////////////////////////////////////////////
        if (DataConnection.ProcessingMetaDataChanges)
        {
            // don't validate a cell in a data bound column while the property descriptors change under us
            return false;
        }

        if (DataConnection.CancellingRowEdit && !DataConnection.RestoreRow)
        {
            // don't validate a cell in a data bound column while we are cancelling a row edit and the old row is not restored
            return false;
        }

        if ((DataConnection.CurrencyManager?.Count ?? 0) <= _ptCurrentCell.Y)
        {
            // don't validate a row beyond the last row in the back end list
            return false;
        }

        if (DataConnection.PositionChangingOutsideDataGridView)
        {
            // the position changed outside the data grid view and we haven't validated the data grid view cell already
            // we can't validate it now because if the user cancels validation then we end up
            // with a position different than the position in the currency manager
            return false;
        }

        if (DataConnection.ListWasReset)
        {
            // The list was reset outside data grid view.
            // We can't validate it now because we would be pushing a value into a different object ( possibly located in a different list ).
            return false;
        }

        return true;
    }

    private void CaptureMouse(Rectangle cursorClip)
    {
        Capture = true;
        Cursor.Clip = RectangleToScreen(cursorClip);
    }

    private void ClearRegionCache()
    {
        _cachedScrollableRegion = null;
    }

    public void ClearSelection()
    {
        _noDimensionChangeCount++;
        _noSelectionChangeCount++;

        bool switchedToBulkPaint = false;

        if (_selectedBandIndexes.Count > BulkPaintThreshold || _individualSelectedCells.Count > BulkPaintThreshold)
        {
            _inBulkPaintCount++;
            switchedToBulkPaint = true;
        }

        try
        {
            RemoveIndividuallySelectedCells();
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                    {
                        // If we change the design and start using this.selectedBandIndexes in this SelectionMode,
                        // we'll have to clear those selections too.
                        break;
                    }

                case DataGridViewSelectionMode.FullRowSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    {
                        while (_selectedBandIndexes.Count > 0)
                        {
                            SetSelectedRowCore(_selectedBandIndexes.HeadInt, false);
                        }

                        // Force repainting of the current column's header cell to remove highlighting
                        if (_ptCurrentCell.X != -1 && SelectionMode == DataGridViewSelectionMode.FullRowSelect)
                        {
                            InvalidateCellPrivate(_ptCurrentCell.X, -1);
                        }

                        break;
                    }

                case DataGridViewSelectionMode.FullColumnSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    {
                        while (_selectedBandIndexes.Count > 0)
                        {
                            SetSelectedColumnCore(_selectedBandIndexes.HeadInt, false);
                        }

                        break;
                    }
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
            NoSelectionChangeCount--;
            if (switchedToBulkPaint)
            {
                ExitBulkPaint(-1, -1);
            }
        }
    }

    protected void ClearSelection(int columnIndexException, int rowIndexException, bool selectExceptionElement)
    {
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.CellSelect:
            case DataGridViewSelectionMode.FullColumnSelect:
            case DataGridViewSelectionMode.ColumnHeaderSelect:
                {
                    ArgumentOutOfRangeException.ThrowIfNegative(columnIndexException);
                    ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndexException, Columns.Count);

                    break;
                }

            case DataGridViewSelectionMode.FullRowSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
                {
                    ArgumentOutOfRangeException.ThrowIfLessThan(columnIndexException, -1);
                    ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndexException, Columns.Count);

                    break;
                }
        }

        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.CellSelect:
            case DataGridViewSelectionMode.FullRowSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
                {
                    ArgumentOutOfRangeException.ThrowIfNegative(rowIndexException);
                    ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndexException, Rows.Count);

                    break;
                }

            case DataGridViewSelectionMode.FullColumnSelect:
            case DataGridViewSelectionMode.ColumnHeaderSelect:
                {
                    ArgumentOutOfRangeException.ThrowIfLessThan(rowIndexException, -1);
                    ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndexException, Rows.Count);

                    break;
                }
        }

        // Clears all selection except the row/column/cell specified as parameter
        _noDimensionChangeCount++;
        _noSelectionChangeCount++;

        bool switchedToBulkPaint = false;

        if (_selectedBandIndexes.Count > BulkPaintThreshold || _individualSelectedCells.Count > BulkPaintThreshold)
        {
            _inBulkPaintCount++;
            switchedToBulkPaint = true;
        }

        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                    {
                        // If we change the design and start using this.selectedBandIndexes in this SelectionMode,
                        // we'll have to clear those selections too.
                        RemoveIndividuallySelectedCells(columnIndexException, rowIndexException);
                        break;
                    }

                case DataGridViewSelectionMode.FullRowSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    {
                        int bandIndex = 0;
                        while (bandIndex < _selectedBandIndexes.Count)
                        {
                            if (_selectedBandIndexes[bandIndex] != rowIndexException)
                            {
                                // deselect currently selected row
                                SetSelectedRowCore(_selectedBandIndexes[bandIndex], false);
                            }
                            else
                            {
                                bandIndex++;
                            }
                        }

                        if (SelectionMode == DataGridViewSelectionMode.RowHeaderSelect)
                        {
                            RemoveIndividuallySelectedCells(columnIndexException, rowIndexException);
                        }

                        break;
                    }

                case DataGridViewSelectionMode.FullColumnSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    {
                        int bandIndex = 0;
                        while (bandIndex < _selectedBandIndexes.Count)
                        {
                            if (_selectedBandIndexes[bandIndex] != columnIndexException)
                            {
                                // deselect currently selected column
                                SetSelectedColumnCore(_selectedBandIndexes[bandIndex], false);
                            }
                            else
                            {
                                bandIndex++;
                            }
                        }

                        if (SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)
                        {
                            RemoveIndividuallySelectedCells(columnIndexException, rowIndexException);
                        }

                        break;
                    }
            }

            if (selectExceptionElement)
            {
                SetSelectedElementCore(columnIndexException, rowIndexException, true);
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
            NoSelectionChangeCount--;
            if (switchedToBulkPaint)
            {
                ExitBulkPaint(-1, -1);
            }
        }
    }

    private bool ColumnEditable(int columnIndex)
    {
        Debug.Assert(columnIndex >= 0 && columnIndex < Columns.Count, $"Invalid columnIndex: {columnIndex}");
        return !Columns[columnIndex].IsDataBound || DataConnection is null || DataConnection.AllowEdit;
    }

    private bool ColumnNeedsDisplayedState(DataGridViewColumn dataGridViewColumn)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);

        if (!dataGridViewColumn.Visible)
        {
            return false;
        }

        if (dataGridViewColumn.Frozen)
        {
            DataGridViewColumn? firstVisibleFrozenColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            Debug.Assert(firstVisibleFrozenColumn is not null);
            if (firstVisibleFrozenColumn.Index == dataGridViewColumn.Index)
            {
                return DisplayedBandsInfo.NumDisplayedFrozenCols > 0;
            }

            Debug.Assert(Columns.DisplayInOrder(firstVisibleFrozenColumn.Index, dataGridViewColumn.Index));
            return Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, firstVisibleFrozenColumn.Index, dataGridViewColumn.Index) < DisplayedBandsInfo.NumDisplayedFrozenCols;
        }
        else
        {
            int firstDisplayedScrollingColumnIndex = DisplayedBandsInfo.FirstDisplayedScrollingCol;
            if (firstDisplayedScrollingColumnIndex != -1)
            {
                if (firstDisplayedScrollingColumnIndex == dataGridViewColumn.Index)
                {
                    return DisplayedBandsInfo.NumDisplayedScrollingCols > 0;
                }

                if (Columns.DisplayInOrder(firstDisplayedScrollingColumnIndex, dataGridViewColumn.Index))
                {
                    return Columns.GetColumnCount(DataGridViewElementStates.Visible, firstDisplayedScrollingColumnIndex, dataGridViewColumn.Index) < DisplayedBandsInfo.NumDisplayedScrollingCols;
                }
            }
        }

        return false;
    }

    private bool ColumnRelocationTarget(MouseEventArgs e, HitTestInfo hti, out int previousColumnIndex)
    {
        previousColumnIndex = -1;
        if (hti._typeInternal is DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom
            or DataGridViewHitTestTypeInternal.ColumnHeader
            or DataGridViewHitTestTypeInternal.ColumnResizeLeft
            or DataGridViewHitTestTypeInternal.ColumnResizeRight
            or DataGridViewHitTestTypeInternal.ColumnHeaderLeft
            or DataGridViewHitTestTypeInternal.ColumnHeaderRight)
        {
            Debug.Assert(hti._col != -1);
            if (hti._typeInternal is DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom
                or DataGridViewHitTestTypeInternal.ColumnHeader)
            {
                int xColumnLeftEdge = GetColumnXFromIndex(hti._col);
                int wColumn = Columns[hti._col].Width;
                if ((RightToLeftInternal && e.X < xColumnLeftEdge - wColumn / 2)
                    || (!RightToLeftInternal && e.X > xColumnLeftEdge + wColumn / 2))
                {
                    // Insert column on the right of hti.col
                    previousColumnIndex = hti._col;
                }
                else
                {
                    // Insert column on the left of hti.col
                    DataGridViewColumn? dataGridViewColumnPrev = Columns.GetPreviousColumn(
                        Columns[hti._col],
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);
                    if (dataGridViewColumnPrev is not null)
                    {
                        previousColumnIndex = dataGridViewColumnPrev.Index;
                    }
                }
            }
            else
            {
                previousColumnIndex = hti._typeInternal is DataGridViewHitTestTypeInternal.ColumnResizeRight or DataGridViewHitTestTypeInternal.ColumnHeaderRight
                    ? hti._col
                    : hti._adjacentCol;
            }

            DataGridViewColumn? dataGridViewColumnNext = null;
            if (previousColumnIndex != -1)
            {
                dataGridViewColumnNext = Columns.GetNextColumn(
                    Columns[previousColumnIndex],
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.None);
            }

            if (_trackColumn != previousColumnIndex
                && !(previousColumnIndex == -1 && hti._col == _trackColumn)
                && (dataGridViewColumnNext is null || _trackColumn != dataGridViewColumnNext.Index))
            {
                return true;
            }
        }
        else if (hti._typeInternal is DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft
            or DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight)
        {
            Debug.Assert(hti._col != -1);
            if (hti._col != _trackColumn)
            {
                return true;
            }
        }

        return false;
    }

    private static bool ColumnsDisplayInOrder(
        int columnIndex1,
        int columnDisplayIndex1,
        int columnIndex2,
        int columnDisplayIndex2) =>
        columnDisplayIndex1 < columnDisplayIndex2 || (columnDisplayIndex1 == columnDisplayIndex2 && columnIndex1 < columnIndex2);

    public bool CommitEdit(DataGridViewDataErrorContexts context)
    {
        if (IsCurrentCellInEditMode)
        {
            DataGridViewCell? dataGridViewCurrentCell = CurrentCellInternal;
            DataGridViewDataErrorEventArgs? dgvdee = CommitEdit(
                ref dataGridViewCurrentCell,
                context,
                DataGridViewValidateCellInternal.Never,
                fireCellLeave: false,
                fireCellEnter: false,
                fireRowLeave: false,
                fireRowEnter: false,
                fireLeave: false);
            if (dgvdee is not null)
            {
                if (dgvdee.ThrowException)
                {
                    throw dgvdee.Exception;
                }

                if (dgvdee.Cancel)
                {
                    return false;
                }
            }
        }

        return true;
    }

    private DataGridViewDataErrorEventArgs? CommitEdit(
        ref DataGridViewCell? dataGridViewCurrentCell,
        DataGridViewDataErrorContexts context,
        DataGridViewValidateCellInternal validateCell,
        bool fireCellLeave,
        bool fireCellEnter,
        bool fireRowLeave,
        bool fireRowEnter,
        bool fireLeave)
    {
        if (validateCell == DataGridViewValidateCellInternal.Always)
        {
            Debug.Assert(_ptCurrentCell.X > -1);
            if (fireCellLeave)
            {
                if (_ptCurrentCell.X == -1)
                {
                    return null;
                }

                OnCellLeave(ref dataGridViewCurrentCell, _ptCurrentCell.X, _ptCurrentCell.Y);
            }

            if (fireRowLeave)
            {
                if (_ptCurrentCell.X == -1)
                {
                    return null;
                }

                OnRowLeave(ref dataGridViewCurrentCell, _ptCurrentCell.X, _ptCurrentCell.Y);
            }

            if (fireLeave)
            {
                base.OnLeave(EventArgs.Empty);

                // Microsoft: can we be smarter about this? What if validating the current cell below forces a repaint on the cell?
                // we would end up repainting the current cell twice.
                //
                // invalidate the current cell so the data grid view does not paint the focus rectangle any longer
                if (_ptCurrentCell.X > -1 && _ptCurrentCell.Y > -1)
                {
                    InvalidateCellPrivate(columnIndex: _ptCurrentCell.X, rowIndex: _ptCurrentCell.Y);
                }
            }

            // OnCellValidating returns true if the dev cancelled the validation.
            bool validateFormattedValue = CanValidateDataBoundDataGridViewCell(dataGridViewCurrentCell);
            if (validateFormattedValue)
            {
                if (_ptCurrentCell.X == -1)
                {
                    return null;
                }

                if (OnCellValidating(ref dataGridViewCurrentCell, _ptCurrentCell.X, _ptCurrentCell.Y, context))
                {
                    if (fireRowEnter)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            return null;
                        }

                        OnRowEnter(
                            ref dataGridViewCurrentCell,
                            _ptCurrentCell.X,
                            _ptCurrentCell.Y,
                            canCreateNewRow: true,
                            validationFailureOccurred: true);
                    }

                    if (fireCellEnter)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            return null;
                        }

                        OnCellEnter(ref dataGridViewCurrentCell, _ptCurrentCell.X, _ptCurrentCell.Y);
                    }

                    if (_ptCurrentCell.X == -1)
                    {
                        return null;
                    }

                    DataGridViewDataErrorEventArgs dgvdee = new(
                        exception: null,
                        _ptCurrentCell.X,
                        _ptCurrentCell.Y,
                        context)
                    {
                        Cancel = true
                    };
                    return dgvdee;
                }

                if (!IsCurrentCellInEditMode || !IsCurrentCellDirty)
                {
                    if (_ptCurrentCell.X == -1)
                    {
                        return null;
                    }

                    OnCellValidated(ref dataGridViewCurrentCell, _ptCurrentCell.X, _ptCurrentCell.Y);
                }
            }
        }

        if (_ptCurrentCell.X == -1 || !IsCurrentCellInEditMode)
        {
            return null;
        }

        Debug.Assert(((EditingControl is not null && ((IDataGridViewEditingControl)EditingControl).EditingControlValueChanged)
                || (_dataGridViewState1[State1_CurrentCellInEditMode] && ((IDataGridViewEditingCell)CurrentCellInternal).EditingCellValueChanged)) == IsCurrentCellDirty
            || _dataGridViewState1[State1_IgnoringEditingChanges]);

        if (IsCurrentCellDirty)
        {
            bool validateAndPushFormattedValue = CanValidateDataBoundDataGridViewCell(dataGridViewCurrentCell);
            if (validateAndPushFormattedValue)
            {
                if (validateCell == DataGridViewValidateCellInternal.WhenChanged)
                {
                    Debug.Assert(_ptCurrentCell.X > -1);
                    if (_ptCurrentCell.X == -1)
                    {
                        return null;
                    }

                    if (OnCellValidating(ref dataGridViewCurrentCell, _ptCurrentCell.X, _ptCurrentCell.Y, context))
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            return null;
                        }

                        DataGridViewDataErrorEventArgs dgvdee = new(
                            exception: null,
                            _ptCurrentCell.X,
                            _ptCurrentCell.Y,
                            context)
                        {
                            Cancel = true
                        };
                        return dgvdee;
                    }
                }

                object? formattedValue;

                if (EditingControl is not null)
                {
                    formattedValue = ((IDataGridViewEditingControl)EditingControl).GetEditingControlFormattedValue(context);
                }
                else
                {
                    Debug.Assert(_dataGridViewState1[State1_CurrentCellInEditMode]);
                    formattedValue = ((IDataGridViewEditingCell)CurrentCellInternal).GetEditingCellFormattedValue(context);
                }

                // We already check if dataGridViewCurrentCell is null inside PushFormattedValue.
                if (!PushFormattedValue(ref dataGridViewCurrentCell!, formattedValue, out Exception? exception))
                {
                    if (_ptCurrentCell.X == -1)
                    {
                        return null;
                    }

                    DataGridViewDataErrorEventArgs dgvdee = new(
                        exception,
                        _ptCurrentCell.X,
                        _ptCurrentCell.Y,
                        context)
                    {
                        Cancel = true
                    };
                    OnDataErrorInternal(dgvdee);
                    return dgvdee;
                }

                if (!IsCurrentCellInEditMode)
                {
                    return null;
                }

                _uneditedFormattedValue = formattedValue;
            }

            if (EditingControl is not null)
            {
                ((IDataGridViewEditingControl)EditingControl).EditingControlValueChanged = false;
            }
            else
            {
                Debug.Assert(_dataGridViewState1[State1_CurrentCellInEditMode]);
                ((IDataGridViewEditingCell)CurrentCellInternal).EditingCellValueChanged = false;
            }

            IsCurrentCellDirtyInternal = false;
            IsCurrentRowDirtyInternal = true;

            if (validateAndPushFormattedValue)
            {
                if (validateCell is DataGridViewValidateCellInternal.Always or DataGridViewValidateCellInternal.WhenChanged)
                {
                    if (_ptCurrentCell.X == -1)
                    {
                        return null;
                    }

                    OnCellValidated(ref dataGridViewCurrentCell, _ptCurrentCell.X, _ptCurrentCell.Y);
                }
            }
        }

        return null;
    }

    private bool CommitEdit(
        DataGridViewDataErrorContexts context,
        bool forCurrentCellChange,
        bool forCurrentRowChange)
    {
        // If we're already within a CellValidating event handler, don't try to commit the cell again.
        if (_dataGridViewOper[OperationInCellValidating])
        {
            return false;
        }

        DataGridViewCell? dataGridViewCurrentCell = CurrentCellInternal;
        DataGridViewDataErrorEventArgs? dgvdee = CommitEdit(
            ref dataGridViewCurrentCell,
            context,
            validateCell: forCurrentCellChange ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.WhenChanged,
            fireCellLeave: forCurrentCellChange,
            fireCellEnter: forCurrentCellChange,
            fireRowLeave: forCurrentRowChange,
            fireRowEnter: forCurrentRowChange,
            fireLeave: false);
        if (dgvdee is not null)
        {
            if (dgvdee.ThrowException)
            {
                throw dgvdee.Exception;
            }

            if (dgvdee.Cancel)
            {
                return false;
            }

            // Restore old value
            dgvdee = CancelEditPrivate();
            if (dgvdee is not null)
            {
                if (dgvdee.ThrowException)
                {
                    throw dgvdee.Exception;
                }

                if (dgvdee.Cancel)
                {
                    return false;
                }
            }
        }

        // See if we can leave the row
        if (forCurrentRowChange && forCurrentCellChange)
        {
            Debug.Assert(_ptCurrentCell.X > -1);
            if (_ptCurrentCell.X == -1)
            {
                return false;
            }

            int columnIndex = _ptCurrentCell.X;
            int rowIndex = _ptCurrentCell.Y;
            // OnRowValidating returns true when the row validation was cancelled.
            if (OnRowValidating(ref dataGridViewCurrentCell, columnIndex, rowIndex))
            {
                if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
                {
                    return false;
                }

                OnRowEnter(
                    ref dataGridViewCurrentCell,
                    columnIndex,
                    rowIndex,
                    canCreateNewRow: true,
                    validationFailureOccurred: true);
                if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
                {
                    return false;
                }

                OnCellEnter(ref dataGridViewCurrentCell, columnIndex, rowIndex);
                return false;
            }

            if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
            {
                return false;
            }

            OnRowValidated(ref dataGridViewCurrentCell, columnIndex, rowIndex);
        }

        return true;
    }

    private bool CommitEditForOperation(int columnIndex, int rowIndex, bool forCurrentCellChange)
    {
        if (forCurrentCellChange)
        {
            if (!EndEdit(
                DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange,
                validateCell: DataGridViewValidateCellInternal.Always,
                fireCellLeave: true,
                fireCellEnter: true,
                fireRowLeave: _ptCurrentCell.Y != rowIndex,
                fireRowEnter: _ptCurrentCell.Y != rowIndex,
                fireLeave: false,
                keepFocus: EditMode != DataGridViewEditMode.EditOnEnter,
                resetCurrentCell: false,
                resetAnchorCell: false))
            {
                return false;
            }

            if (_ptCurrentCell.Y != rowIndex && _ptCurrentCell.Y != -1)
            {
                DataGridViewCell? dataGridViewCellTmp = null;
                int columnIndexTmp = _ptCurrentCell.X;
                int rowIndexTmp = _ptCurrentCell.Y;
                if (OnRowValidating(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp))
                {
                    // Row validation was cancelled
                    if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp))
                    {
                        return false;
                    }

                    OnRowEnter(
                        ref dataGridViewCellTmp,
                        columnIndexTmp,
                        rowIndexTmp,
                        canCreateNewRow: true,
                        validationFailureOccurred: true);
                    if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp))
                    {
                        return false;
                    }

                    OnCellEnter(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp);
                    if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp))
                    {
                        return false;
                    }

                    // Re-enter editing mode if needed
                    if (Focused
                        && !IsCurrentCellInEditMode
                        && (EditMode == DataGridViewEditMode.EditOnEnter
                            || (EditMode != DataGridViewEditMode.EditProgrammatically && CurrentCellInternal.EditType is null)))
                    {
                        BeginEditInternal(selectAll: true);
                    }

                    return false;
                }

                if (IsInnerCellOutOfBounds(columnIndexTmp, rowIndexTmp))
                {
                    return false;
                }

                OnRowValidated(ref dataGridViewCellTmp, columnIndexTmp, rowIndexTmp);
            }
        }
        else
        {
            if (!CommitEdit(
                DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll,
                forCurrentCellChange: false,
                forCurrentRowChange: _ptCurrentCell.Y != rowIndex))
            {
                return false;
            }
        }

        // Row validation was not cancelled, but operation needs to be re-evaluated.
        Debug.Assert(columnIndex < Columns.Count);
        if (IsColumnOutOfBounds(columnIndex))
        {
            return false;
        }

        if (rowIndex >= Rows.Count)
        {
            // CurrentCell was reset because the commit deleted row(s).
            // Since the user wants to change the current cell, we don't
            // want to end up with no CurrentCell. We pick the last visible
            // row in the grid which may be the 'new row'.
            int lastVisibleRowIndex = Rows.GetLastRow(DataGridViewElementStates.Visible);
            if (forCurrentCellChange && _ptCurrentCell.X == -1 && lastVisibleRowIndex != -1)
            {
                bool success = SetAndSelectCurrentCellAddress(
                    columnIndex,
                    lastVisibleRowIndex,
                    setAnchorCellAddress: true,
                    validateCurrentCell: false,
                    throughMouseClick: false,
                    clearSelection: false,
                    forceCurrentCellSelection: false);
                Debug.Assert(success);
            }

            // Interrupt operation because it has become invalid.
            return false;
        }

        if (rowIndex > -1 && (Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) == 0)
        {
            // Interrupt operation because target row has become invisible.
            return false;
        }

        return true;
    }

    internal void CompleteCellsCollection(DataGridViewRow dataGridViewRow)
    {
        Debug.Assert(dataGridViewRow is not null);
        int cellsInCollection = dataGridViewRow.Cells.Count;
        if (Columns.Count > cellsInCollection)
        {
            int cellCount = 0;
            DataGridViewCell[] cells = new DataGridViewCell[Columns.Count - cellsInCollection];
            for (int columnIndex = cellsInCollection; columnIndex < Columns.Count; columnIndex++)
            {
                if (Columns[columnIndex].CellTemplate is null)
                {
                    throw new InvalidOperationException(SR.DataGridView_AColumnHasNoCellTemplate);
                }

                DataGridViewCell dgvcNew = (DataGridViewCell)Columns[columnIndex].CellTemplate!.Clone();
                cells[cellCount] = dgvcNew;
                cellCount++;
            }

            dataGridViewRow.Cells.AddRange(cells);
        }
    }

    /// <summary>
    ///  Determines which column is the first visible scrolling
    ///  column given the object's horizontalOffset.
    /// </summary>
    private int ComputeFirstVisibleScrollingColumn()
    {
        if (Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) >= _layout.Data.Width)
        {
            // Not enough room for scrolling columns.
            FirstDisplayedScrollingColumnHiddenWidth = 0;
            return -1;
        }

        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);

        if (_horizontalOffset == 0)
        {
            FirstDisplayedScrollingColumnHiddenWidth = 0;
            return (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        }

        int cx = 0;
        while (dataGridViewColumn is not null)
        {
            cx += dataGridViewColumn.Thickness;
            if (cx > _horizontalOffset)
            {
                break;
            }

            dataGridViewColumn = Columns.GetNextColumn(
                dataGridViewColumn,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
        }

        if (dataGridViewColumn is null)
        {
            Debug.Assert(cx <= _horizontalOffset);
            dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
            if (dataGridViewColumn is null)
            {
                FirstDisplayedScrollingColumnHiddenWidth = 0;
                return -1;
            }
            else
            {
                if (FirstDisplayedScrollingColumnHiddenWidth != _horizontalOffset)
                {
                    FirstDisplayedScrollingColumnHiddenWidth = 0;
                }

                return dataGridViewColumn.Index;
            }
        }
        else
        {
            FirstDisplayedScrollingColumnHiddenWidth = dataGridViewColumn.Thickness - (cx - _horizontalOffset);
            return dataGridViewColumn.Index;
        }
    }

    private int ComputeHeightOfFittingTrailingScrollingRows(int totalVisibleFrozenHeight)
    {
        int displayHeight = _layout.Data.Height - totalVisibleFrozenHeight;
        int rowHeight = 0, rowHeights = 0;
        int indexTmp = Rows.Count;

        if (indexTmp == 0 || displayHeight <= 0)
        {
            return 0;
        }
        else
        {
            indexTmp--;
        }

        DataGridViewElementStates rowState = Rows.GetRowState(indexTmp);
        if ((rowState & DataGridViewElementStates.Frozen) != 0)
        {
            return 0;
        }

        if ((rowState & DataGridViewElementStates.Visible) == 0)
        {
            indexTmp = Rows.GetPreviousRow(
                indexTmp,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.Frozen);
        }

        if (indexTmp != -1)
        {
            rowHeight = Rows.SharedRow(indexTmp).GetHeight(indexTmp);
            if (rowHeight > displayHeight)
            {
                return rowHeight;
            }
        }

        while (indexTmp != -1 && rowHeights + rowHeight <= displayHeight)
        {
            rowHeights += rowHeight;
            indexTmp = Rows.GetPreviousRow(
                indexTmp,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.Frozen);
            if (indexTmp != -1)
            {
                rowHeight = Rows.SharedRow(indexTmp).GetHeight(indexTmp);
            }
        }

        return rowHeights;
    }

    private int ComputeHeightOfScrolledOffRows()
    {
        //
        int height = 0;
        if (DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0)
        {
            int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
            if (rowIndex != -1)
            {
                while (rowIndex != DisplayedBandsInfo.FirstDisplayedScrollingRow)
                {
                    Debug.Assert(rowIndex < DisplayedBandsInfo.FirstDisplayedScrollingRow);
                    height += Rows.SharedRow(rowIndex).GetHeight(rowIndex);
                    rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                }
            }
        }

        return height;
    }

    private int ComputeHeightOfTrailingScrollingRows()
    {
        if (DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0)
        {
            int lastVisibleRowIndex = Rows.GetLastRow(DataGridViewElementStates.Visible);
            return Rows.GetRowsHeight(DataGridViewElementStates.Visible, DisplayedBandsInfo.FirstDisplayedScrollingRow, lastVisibleRowIndex) +
                   Rows.SharedRow(lastVisibleRowIndex).GetHeight(lastVisibleRowIndex);
        }

        return 0;
    }

    private bool ComputeLayout()
    {
        ClearRegionCache();

        LayoutData newLayout = new(_layout);
        Rectangle oldResizeRect = _layout.ResizeBoxRect;

        // Inside region
        newLayout.Inside = _normalClientRectangle.Width > 0 || _normalClientRectangle.Height > 0
            ? _normalClientRectangle
            : ClientRectangle;

        Rectangle inside = newLayout.Inside;
        int borderWidth = BorderWidth;
        inside.Inflate(-borderWidth, -borderWidth);
        if (inside.Height < 0)
        {
            inside.Height = 0;
        }

        if (inside.Width < 0)
        {
            inside.Width = 0;
        }

        Rectangle insideLeft = inside;

        // Headers
        if (_layout.ColumnHeadersVisible)
        {
            Rectangle colHeaders = insideLeft;
            colHeaders.Height = Math.Min(_columnHeadersHeight, colHeaders.Height);
            insideLeft.Y += colHeaders.Height;
            insideLeft.Height -= colHeaders.Height;
            Debug.Assert(insideLeft.Height >= 0);
            newLayout.ColumnHeaders = colHeaders;
        }
        else
        {
            newLayout.ColumnHeaders = Rectangle.Empty;
        }

        if (_layout.RowHeadersVisible)
        {
            Rectangle rowHeaders = insideLeft;
            rowHeaders.Width = Math.Min(RowHeadersWidth, rowHeaders.Width);
            if (RightToLeftInternal)
            {
                rowHeaders.X += insideLeft.Width - rowHeaders.Width;
            }
            else
            {
                insideLeft.X += rowHeaders.Width;
            }

            insideLeft.Width -= rowHeaders.Width;
            Debug.Assert(insideLeft.Width >= 0);
            newLayout.RowHeaders = rowHeaders;

            if (_layout.ColumnHeadersVisible)
            {
                Rectangle topLeft;
                Rectangle colHeaders = newLayout.ColumnHeaders;
                topLeft = colHeaders;
                topLeft.Width = Math.Min(RowHeadersWidth, topLeft.Width);
                colHeaders.Width -= topLeft.Width;
                if (RightToLeftInternal)
                {
                    topLeft.X += insideLeft.Width;
                }
                else
                {
                    colHeaders.X += topLeft.Width;
                }

                Debug.Assert(colHeaders.Width >= 0);
                newLayout.TopLeftHeader = topLeft;
                newLayout.ColumnHeaders = colHeaders;
            }
            else
            {
                newLayout.TopLeftHeader = Rectangle.Empty;
            }
        }
        else
        {
            newLayout.RowHeaders = Rectangle.Empty;
            newLayout.TopLeftHeader = Rectangle.Empty;
        }

        // Adjust insideLeft in case static top / left edge needs to be painted
        if (SingleVerticalBorderAdded)
        {
            if (!RightToLeftInternal)
            {
                insideLeft.X++;
            }

            if (insideLeft.Width > 0)
            {
                insideLeft.Width--;
            }
        }

        if (SingleHorizontalBorderAdded)
        {
            insideLeft.Y++;
            if (insideLeft.Height > 0)
            {
                insideLeft.Height--;
            }
        }

        // Data region
        newLayout.Data = insideLeft;
        newLayout.Inside = inside;

        Debug.Assert(newLayout.Data.X >= 0);
        Debug.Assert(newLayout.Data.Y >= 0);
        Debug.Assert(newLayout.Data.Width >= 0);
        Debug.Assert(newLayout.Data.Height >= 0);

        _layout = newLayout;
        _layout._dirty = false;

        bool columnsAdjusted = AdjustFillingColumns();

        _layout = newLayout;
        Debug.Assert(!_layout._dirty);
        LayoutScrollBars();

        // if the user shrank the grid client area, then OnResize invalidated the old
        // resize area. however, we need to invalidate the left upper corner in the new ResizeArea
        // note that we can't take the Invalidate call from the OnResize method, because if the
        // user enlarges the form then the old area will not be invalidated.
        //
        if (!oldResizeRect.Equals(_layout.ResizeBoxRect) && !_layout.ResizeBoxRect.IsEmpty)
        {
            Invalidate(_layout.ResizeBoxRect);
        }

        return columnsAdjusted;
    }

    private void ComputeLayoutShortcut(bool computeVisibleRows)
    {
        // Called instead of ComputeLayout when a row is added, inserted or deleted beyond the limits of
        // the layout.Data area.
        // this.layout is unchanged - only the potential vertical scrollbar is affected.

        if (computeVisibleRows)
        {
            ComputeVisibleRows();
        }
#if DEBUG
        else
        {
            int oldNumTotallyVisibleFrozenRows = DisplayedBandsInfo.NumTotallyDisplayedFrozenRows;
            int oldNumVisibleScrollingRows = DisplayedBandsInfo.NumDisplayedScrollingRows;
            int oldNumTotallyVisibleScrollingRows = DisplayedBandsInfo.NumTotallyDisplayedScrollingRows;
            int oldFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
            ComputeVisibleRows();
            Debug.Assert(oldNumTotallyVisibleFrozenRows == DisplayedBandsInfo.NumTotallyDisplayedFrozenRows);
            Debug.Assert(oldNumVisibleScrollingRows == DisplayedBandsInfo.NumDisplayedScrollingRows);
            Debug.Assert(oldNumTotallyVisibleScrollingRows == DisplayedBandsInfo.NumTotallyDisplayedScrollingRows);
            Debug.Assert(oldFirstVisibleScrollingRow == DisplayedBandsInfo.FirstDisplayedScrollingRow);
        }
#endif

#if DEBUG
        int newFirstVisibleScrollingCol = ComputeFirstVisibleScrollingColumn();
        Debug.Assert(newFirstVisibleScrollingCol == DisplayedBandsInfo.FirstDisplayedScrollingCol);

        int oldLastTotallyVisibleScrollingCol = DisplayedBandsInfo.LastTotallyDisplayedScrollingCol;
        int oldFirstVisibleScrollingCol = DisplayedBandsInfo.FirstDisplayedScrollingCol;
        ComputeVisibleColumns();
        Debug.Assert(oldLastTotallyVisibleScrollingCol == DisplayedBandsInfo.LastTotallyDisplayedScrollingCol);
        Debug.Assert(oldFirstVisibleScrollingCol == DisplayedBandsInfo.FirstDisplayedScrollingCol);
#endif

        if (_vertScrollBar.Enabled)
        {
            int totalVisibleHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible);
            int totalVisibleFrozenHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            int oldVertScrollBarValue = _vertScrollBar.Value;
            int oldThumbHeight = Math.Max(((_vertScrollBar.Height - 2 * SystemInformation.VerticalScrollBarArrowHeight) * _vertScrollBar.LargeChange) / _vertScrollBar.Maximum, 8);

            _vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight;
            Debug.Assert(_vertScrollBar.Maximum > 0);
            _vertScrollBar.Value = ComputeHeightOfScrolledOffRows();
            _vertScrollBar.LargeChange = _layout.Data.Height - totalVisibleFrozenHeight;
            VerticalScrollingOffset = _vertScrollBar.Value;

            if (_vertScrollBar.Visible
                && (oldVertScrollBarValue != VerticalScrollingOffset
                    || oldThumbHeight != Math.Max(
                        (_vertScrollBar.Height - 2 * SystemInformation.VerticalScrollBarArrowHeight) * _vertScrollBar.LargeChange / _vertScrollBar.Maximum,
                        8)))
            {
                // Only update the vertical scroll bar is the thumb moved or resized.
                _vertScrollBar.Invalidate();
            }

            Debug.Assert(VerticalScrollingOffset == _vertScrollBar.Value);
        }
    }

    private void ComputeVisibleColumns()
    {
        DataGridViewColumn? dataGridViewColumn;
        int numVisibleScrollingCols = 0, visibleScrollingColumnsTmp = 0;
        int displayWidth = _layout.Data.Width, cx = 0;
        int numDisplayedFrozenCols = 0, firstDisplayedFrozenCol = -1, lastDisplayedFrozenCol = -1;
        int firstDisplayedScrollingCol = DisplayedBandsInfo.FirstDisplayedScrollingCol;

        // the same problem with negative numbers:
        // if the width passed in is negative, then return 0
        if (displayWidth <= 0 || Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0)
        {
            DisplayedBandsInfo.FirstDisplayedFrozenCol = -1;
            DisplayedBandsInfo.NumDisplayedFrozenCols = 0;
            DisplayedBandsInfo.FirstDisplayedScrollingCol = -1;
            DisplayedBandsInfo.NumDisplayedScrollingCols = 0;
            DisplayedBandsInfo.LastDisplayedFrozenCol = -1;
            DisplayedBandsInfo.LastTotallyDisplayedScrollingCol = -1;
            return;
        }

        dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.None);
        while (dataGridViewColumn is not null)
        {
            if (!dataGridViewColumn.Frozen && dataGridViewColumn.Visible)
            {
                break;
            }

            if (dataGridViewColumn.Visible)
            {
                if (firstDisplayedFrozenCol == -1)
                {
                    firstDisplayedFrozenCol = dataGridViewColumn.Index;
                }

                cx += dataGridViewColumn.Width;
                numDisplayedFrozenCols++;
                lastDisplayedFrozenCol = dataGridViewColumn.Index;
                if (cx >= displayWidth)
                {
                    break;
                }
            }

            dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.None, DataGridViewElementStates.None);
        }

        Debug.Assert(cx <= Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen));

        if (cx < displayWidth && firstDisplayedScrollingCol >= 0)
        {
            dataGridViewColumn = Columns[firstDisplayedScrollingCol];
            if (dataGridViewColumn.Frozen)
            {
                dataGridViewColumn = Columns.GetFirstColumn(
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.Frozen);
                FirstDisplayedScrollingColumnHiddenWidth = 0;
                if (dataGridViewColumn is null)
                {
                    DisplayedBandsInfo.FirstDisplayedFrozenCol = firstDisplayedFrozenCol;
                    DisplayedBandsInfo.LastDisplayedFrozenCol = lastDisplayedFrozenCol;
                    DisplayedBandsInfo.NumDisplayedFrozenCols = numDisplayedFrozenCols;
                    DisplayedBandsInfo.FirstDisplayedScrollingCol = DisplayedBandsInfo.LastTotallyDisplayedScrollingCol = -1;
                    DisplayedBandsInfo.NumDisplayedScrollingCols = 0;
                    return;
                }
                else
                {
                    firstDisplayedScrollingCol = dataGridViewColumn.Index;
                }
            }

            cx -= FirstDisplayedScrollingColumnHiddenWidth;
            while (cx < displayWidth && dataGridViewColumn is not null)
            {
                cx += dataGridViewColumn.Thickness;
                visibleScrollingColumnsTmp++;
                dataGridViewColumn = Columns.GetNextColumn(
                    dataGridViewColumn,
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.None);
            }

            numVisibleScrollingCols = visibleScrollingColumnsTmp;

            // if we inflate the data area then we paint columns to the left of firstDisplayedScrollingCol
            if (cx < displayWidth)
            {
                bool invalidate = false;
                Debug.Assert(firstDisplayedScrollingCol >= 0);
                // first minimize value of this.negOffset
                if (FirstDisplayedScrollingColumnHiddenWidth > 0)
                {
                    invalidate = true;
                    if (displayWidth - cx > FirstDisplayedScrollingColumnHiddenWidth)
                    {
                        cx += FirstDisplayedScrollingColumnHiddenWidth;
                        _horizontalOffset -= FirstDisplayedScrollingColumnHiddenWidth;
                        FirstDisplayedScrollingColumnHiddenWidth = 0;
                    }
                    else
                    {
                        _horizontalOffset -= displayWidth - cx;
                        FirstDisplayedScrollingColumnHiddenWidth -= displayWidth - cx;
                        cx = displayWidth;
                    }
                }

                // second try to scroll entire columns
                if (cx < displayWidth && _horizontalOffset > 0)
                {
                    Debug.Assert(FirstDisplayedScrollingColumnHiddenWidth == 0);
                    dataGridViewColumn = Columns.GetPreviousColumn((Columns[firstDisplayedScrollingCol]),
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.Frozen);
                    while (dataGridViewColumn is not null && cx + dataGridViewColumn.Thickness <= displayWidth)
                    {
                        cx += dataGridViewColumn.Thickness;
                        visibleScrollingColumnsTmp++;
                        invalidate = true;
                        firstDisplayedScrollingCol = dataGridViewColumn.Index;
                        _horizontalOffset -= dataGridViewColumn.Thickness;
                        dataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn,
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.Frozen);
                    }
                }

                // third try to partially scroll in first scrolled off column
                if (cx < displayWidth && _horizontalOffset > 0 && firstDisplayedScrollingCol != 0)
                {
                    Debug.Assert(FirstDisplayedScrollingColumnHiddenWidth == 0);
                    dataGridViewColumn = Columns.GetPreviousColumn(
                        (Columns[firstDisplayedScrollingCol]),
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.Frozen);
                    ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                    Debug.Assert(dataGridViewColumn.Thickness > displayWidth - cx);
                    firstDisplayedScrollingCol = dataGridViewColumn.Index;
                    FirstDisplayedScrollingColumnHiddenWidth = dataGridViewColumn.Thickness - displayWidth + cx;
                    _horizontalOffset -= displayWidth - cx;
                    visibleScrollingColumnsTmp++;
                    invalidate = true;
                    cx = displayWidth;
                    Debug.Assert(FirstDisplayedScrollingColumnHiddenWidth == GetNegOffsetFromHorizontalOffset(_horizontalOffset));
                }

                // update the number of visible columns to the new reality
                Debug.Assert(numVisibleScrollingCols <= visibleScrollingColumnsTmp, "the number of displayed columns can only grow");
                numVisibleScrollingCols = visibleScrollingColumnsTmp;

                if (invalidate)
                {
                    InvalidateData();
                    Invalidate(_layout.ColumnHeaders);
                }
            }

            int jumpFromFirstVisibleScrollingCol = numVisibleScrollingCols - 1;
            if (cx > displayWidth)
            {
                jumpFromFirstVisibleScrollingCol--;
            }

            Debug.Assert(jumpFromFirstVisibleScrollingCol >= -1);

            if (jumpFromFirstVisibleScrollingCol < 0)
            {
                DisplayedBandsInfo.LastTotallyDisplayedScrollingCol = -1; // no totally visible scrolling column at all
            }
            else
            {
                Debug.Assert(firstDisplayedScrollingCol >= 0);
                dataGridViewColumn = Columns[firstDisplayedScrollingCol];
                for (int jump = 0; jump < jumpFromFirstVisibleScrollingCol; jump++)
                {
                    dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);
                    ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                }

                DisplayedBandsInfo.LastTotallyDisplayedScrollingCol = dataGridViewColumn.Index;
            }
        }
        else
        {
            DisplayedBandsInfo.LastTotallyDisplayedScrollingCol = -1;
        }

        DisplayedBandsInfo.FirstDisplayedFrozenCol = firstDisplayedFrozenCol;
        DisplayedBandsInfo.LastDisplayedFrozenCol = lastDisplayedFrozenCol;
        DisplayedBandsInfo.NumDisplayedFrozenCols = numDisplayedFrozenCols;
        DisplayedBandsInfo.FirstDisplayedScrollingCol = firstDisplayedScrollingCol;
        DisplayedBandsInfo.NumDisplayedScrollingCols = numVisibleScrollingCols;
        Debug.Assert((DisplayedBandsInfo.NumDisplayedScrollingCols > 0 && DisplayedBandsInfo.FirstDisplayedScrollingCol != -1)
            || (DisplayedBandsInfo.NumDisplayedScrollingCols == 0 && DisplayedBandsInfo.FirstDisplayedScrollingCol == -1));
    }

    private void ComputeVisibleRows()
    {
        int firstDisplayedFrozenRow = -1;
        int firstDisplayedScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
        int lastDisplayedFrozenRow = -1, lastDisplayedScrollingRow = -1;
        int numTotallyDisplayedFrozenRows = 0;
        int displayHeight = _layout.Data.Height;
        int cy = 0;
        int visibleScrollingRows = 0;
        int nRows = Rows.Count;
        int rowIndex;

        // when minimizing the dataGridView window, we will get negative values for the
        // layout.Data.Width and layout.Data.Height

        if (displayHeight <= 0 || nRows == 0)
        {
            DisplayedBandsInfo.NumDisplayedFrozenRows = DisplayedBandsInfo.NumTotallyDisplayedFrozenRows =
                DisplayedBandsInfo.NumDisplayedScrollingRows = DisplayedBandsInfo.NumTotallyDisplayedScrollingRows = 0;
            DisplayedBandsInfo.FirstDisplayedFrozenRow = DisplayedBandsInfo.FirstDisplayedScrollingRow =
                DisplayedBandsInfo.LastDisplayedFrozenRow = DisplayedBandsInfo.LastDisplayedScrollingRow = -1;
            return;
        }

        for (rowIndex = 0; rowIndex < nRows; rowIndex++)
        {
            Debug.Assert(cy < displayHeight);
            DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
            if ((rowState & DataGridViewElementStates.Frozen) == 0 && (rowState & DataGridViewElementStates.Visible) != 0)
            {
                break;
            }

            if ((rowState & DataGridViewElementStates.Visible) != 0)
            {
                cy += Rows.SharedRow(rowIndex).GetHeight(rowIndex);
                if (firstDisplayedFrozenRow == -1)
                {
                    firstDisplayedFrozenRow = rowIndex;
                }

                lastDisplayedFrozenRow = rowIndex;
                if (cy <= displayHeight)
                {
                    numTotallyDisplayedFrozenRows++;
                }

                if (cy >= displayHeight)
                {
                    break;
                }
            }
        }

        DisplayedBandsInfo.NumDisplayedFrozenRows = cy > displayHeight
            ? numTotallyDisplayedFrozenRows + 1
            : numTotallyDisplayedFrozenRows;

        // loop exited when:
        // - all rows are frozen and fit in displayHeight: rowIndex == nRows, cy <= displayHeight
        // - rowIndex is not frozen: rowIndex < nRows, cy <= displayHeight
        // - there are more frozen rows than can fit in displayHeight: rowIndex <= nRows, cy > displayHeight

        if (cy < displayHeight && rowIndex < nRows)
        {
            if (firstDisplayedScrollingRow == -1)
            {
                firstDisplayedScrollingRow = rowIndex;
            }

            while (firstDisplayedScrollingRow < nRows
                && ((Rows.GetRowState(firstDisplayedScrollingRow) & DataGridViewElementStates.Frozen) != 0
                    || (Rows.GetRowState(firstDisplayedScrollingRow) & DataGridViewElementStates.Visible) == 0))
            {
                firstDisplayedScrollingRow++;
            }

            for (int i = firstDisplayedScrollingRow; i < nRows; i++)
            {
                if ((Rows.GetRowState(i) & DataGridViewElementStates.Visible) != 0)
                {
                    cy += Rows.SharedRow(i).GetHeight(i);
                    visibleScrollingRows++;
                    lastDisplayedScrollingRow = i;
                }

                if (cy >= displayHeight)
                {
                    break;
                }
            }

            if (cy < displayHeight)
            {
                for (int i = firstDisplayedScrollingRow - 1; i >= numTotallyDisplayedFrozenRows; i--)
                {
                    if ((Rows.GetRowState(i) & (DataGridViewElementStates.Frozen | DataGridViewElementStates.Visible)) == DataGridViewElementStates.Visible)
                    {
                        int height = Rows.SharedRow(i).GetHeight(i);
                        if (cy + height > displayHeight)
                        {
                            break;
                        }

                        cy += height;
                        firstDisplayedScrollingRow = i;
                        visibleScrollingRows++;
                        lastDisplayedScrollingRow = i;
                    }
                }
            }

            DisplayedBandsInfo.NumDisplayedScrollingRows = visibleScrollingRows;
            DisplayedBandsInfo.NumTotallyDisplayedScrollingRows = cy > displayHeight
                ? visibleScrollingRows - 1
                : visibleScrollingRows;

            if (visibleScrollingRows == 0)
            {
                firstDisplayedScrollingRow = -1;
                Debug.Assert(lastDisplayedScrollingRow == -1);
            }
        }
        else
        {
            DisplayedBandsInfo.NumDisplayedScrollingRows = DisplayedBandsInfo.NumTotallyDisplayedScrollingRows = 0;
            firstDisplayedScrollingRow = -1;
        }

        Debug.Assert(firstDisplayedFrozenRow < nRows, "firstDisplayedFrozenRow larger than number of rows");
        Debug.Assert(lastDisplayedFrozenRow < nRows, "lastDisplayedFrozenRow larger than number of rows");
        Debug.Assert(lastDisplayedScrollingRow < nRows, "lastDisplayedScrollingRow larger than number of rows");

        DisplayedBandsInfo.FirstDisplayedFrozenRow = firstDisplayedFrozenRow;
        DisplayedBandsInfo.FirstDisplayedScrollingRow = firstDisplayedScrollingRow;
        DisplayedBandsInfo.NumTotallyDisplayedFrozenRows = numTotallyDisplayedFrozenRows;
        DisplayedBandsInfo.LastDisplayedFrozenRow = lastDisplayedFrozenRow;
        DisplayedBandsInfo.LastDisplayedScrollingRow = lastDisplayedScrollingRow;

        Debug.Assert(DisplayedBandsInfo.NumTotallyDisplayedFrozenRows >= 0, "the number of visible frozen rows can't be negative");
        Debug.Assert(DisplayedBandsInfo.NumDisplayedScrollingRows >= 0, "the number of visible scrolling rows can't be negative");
        Debug.Assert(DisplayedBandsInfo.NumTotallyDisplayedScrollingRows >= 0, "the number of totally visible scrolling rows can't be negative");
        Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow < nRows, "firstDisplayedScrollingRow larger than number of rows");
    }

    private Point ConvertCellToGridCoord(int columnIndex, int rowIndex, int x, int y)
    {
        int columnX, rowY;
        if (columnIndex > -1)
        {
            columnX = GetColumnXFromIndex(columnIndex);
            if (RightToLeftInternal)
            {
                columnX -= Columns[columnIndex].Width;
            }
        }
        else
        {
            columnX = RightToLeftInternal ? _layout.RowHeaders.Left - 1 : _layout.RowHeaders.Left;
        }

        rowY = rowIndex > -1 ? GetRowYFromIndex(rowIndex) : _layout.ColumnHeaders.Top;

        return new Point(columnX + x, rowY + y);
    }

    private void CorrectColumnDisplayIndexesAfterDeletion(DataGridViewColumn dataGridViewColumn)
    {
        // Column indexes have already been adjusted.
        // This column has already been detached and has retained its old Index and DisplayIndex

        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(dataGridViewColumn.DataGridView is null);
        Debug.Assert(dataGridViewColumn.Index >= 0);
        Debug.Assert(dataGridViewColumn.DisplayIndex >= 0);

        try
        {
            _dataGridViewOper[OperationInDisplayIndexAdjustments] = true;

            // All remaining columns with a DisplayIndex greater than dataGridViewColumn.DisplayIndex need to be decremented
            foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
            {
                if (dataGridViewColumnTmp.DisplayIndex > dataGridViewColumn.DisplayIndex)
                {
                    dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex - 1;
                    dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on
                }
            }

#if DEBUG
            Debug.Assert(Columns.VerifyColumnDisplayIndexes());
#endif
            // Now raise all the OnColumnDisplayIndexChanged events
            FlushDisplayIndexChanged(raiseEvent: true);
        }
        finally
        {
            _dataGridViewOper[OperationInDisplayIndexAdjustments] = false;
            FlushDisplayIndexChanged(raiseEvent: false);
        }
    }

    private void CorrectColumnDisplayIndexesAfterInsertion(DataGridViewColumn dataGridViewColumn)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(dataGridViewColumn.DataGridView == this);
        // dataGridViewColumn.DisplayIndex has been set already.
        Debug.Assert(dataGridViewColumn.DisplayIndex >= 0);

        try
        {
            _dataGridViewOper[OperationInDisplayIndexAdjustments] = true;

            // All other columns with a DisplayIndex equal or greater than dataGridViewColumn.DisplayIndex need to be incremented
            foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
            {
                if (dataGridViewColumnTmp != dataGridViewColumn && dataGridViewColumnTmp.DisplayIndex >= dataGridViewColumn.DisplayIndex)
                {
                    dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex + 1;
                    dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on
                }
            }

#if DEBUG
            Debug.Assert(Columns.VerifyColumnDisplayIndexes());
#endif
            // Now raise all the OnColumnDisplayIndexChanged events
            FlushDisplayIndexChanged(raiseEvent: true);
        }
        finally
        {
            _dataGridViewOper[OperationInDisplayIndexAdjustments] = false;
            FlushDisplayIndexChanged(raiseEvent: false);
        }
    }

    private void CorrectColumnFrozenState(DataGridViewColumn dataGridViewColumn, int anticipatedColumnIndex)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(anticipatedColumnIndex >= 0 && anticipatedColumnIndex <= Columns.Count);

        int anticipatedColumnDisplayIndex;
        if (dataGridViewColumn.DisplayIndex == -1 || dataGridViewColumn.DisplayIndex > Columns.Count)
        {
            anticipatedColumnDisplayIndex = anticipatedColumnIndex; // By default, we pick the Index as the DisplayIndex.
        }
        else
        {
            Debug.Assert(dataGridViewColumn.DisplayIndex >= 0 && dataGridViewColumn.DisplayIndex <= Columns.Count);
            anticipatedColumnDisplayIndex = dataGridViewColumn.DisplayIndex; // The specified DisplayIndex is just fine.
        }

        DataGridViewColumn? dataGridViewColumnPrev;
        int displayIndex = anticipatedColumnDisplayIndex - 1;
        do
        {
            dataGridViewColumnPrev = Columns.GetColumnAtDisplayIndex(displayIndex);
            displayIndex--;
        }
        while (displayIndex >= 0 && (dataGridViewColumnPrev is null || !dataGridViewColumnPrev.Visible));
        if (dataGridViewColumnPrev is not null && !dataGridViewColumnPrev.Frozen && dataGridViewColumn.Frozen)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAddFrozenColumn);
        }
        else
        {
            DataGridViewColumn? dataGridViewColumnNext;
            displayIndex = anticipatedColumnDisplayIndex;
            do
            {
                dataGridViewColumnNext = Columns.GetColumnAtDisplayIndex(displayIndex);
                displayIndex++;
            }
            while (displayIndex < Columns.Count && (dataGridViewColumnNext is null || !dataGridViewColumnNext.Visible));
            if (dataGridViewColumnNext is not null && dataGridViewColumnNext.Frozen && !dataGridViewColumn.Frozen)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddNonFrozenColumn);
            }
        }
    }

    private void CorrectColumnFrozenStates(DataGridViewColumn[] dataGridViewColumns)
    {
        DataGridView dataGridViewTmp = new();
        DataGridViewColumn dataGridViewColumnClone;
        foreach (DataGridViewColumn dataGridViewColumn in Columns)
        {
            dataGridViewColumnClone = (DataGridViewColumn)dataGridViewColumn.Clone();
            // DataGridViewColumn.Clone does not replicate the DisplayIndex value.
            dataGridViewColumnClone.DisplayIndex = dataGridViewColumn.DisplayIndex;
            dataGridViewTmp.Columns.Add(dataGridViewColumnClone);
        }

        foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns)
        {
            dataGridViewColumnClone = (DataGridViewColumn)dataGridViewColumn.Clone();
            dataGridViewColumnClone.DisplayIndex = dataGridViewColumn.DisplayIndex;
            dataGridViewTmp.Columns.Add(dataGridViewColumnClone);
        }
    }

    private void CorrectColumnFrozenStates(DataGridViewColumn dataGridViewColumn, bool frozenStateChanging)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        DataGridViewColumn? dataGridViewColumnTmp;
        if ((dataGridViewColumn.Frozen && !frozenStateChanging) || (!dataGridViewColumn.Frozen && frozenStateChanging))
        {
            // make sure the previous visible columns are frozen as well
            dataGridViewColumnTmp = Columns.GetPreviousColumn(
                dataGridViewColumn,
                DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen,
                DataGridViewElementStates.None);
            if (dataGridViewColumnTmp is null)
            {
                DataGridViewColumn? dataGridViewColumnFirst = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                if (dataGridViewColumnFirst != dataGridViewColumn)
                {
                    dataGridViewColumnTmp = dataGridViewColumnFirst;
                }
            }

            while (dataGridViewColumnTmp is not null && Columns.DisplayInOrder(dataGridViewColumnTmp.Index, dataGridViewColumn.Index))
            {
                dataGridViewColumnTmp.Frozen = true;
                dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp,
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.Frozen);
            }
        }
        else
        {
            // make sure the next visible columns are not frozen
            dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumn,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.Frozen);
            if (dataGridViewColumnTmp is null)
            {
                DataGridViewColumn dataGridViewColumnLast = dataGridViewColumn;
                do
                {
                    dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnLast,
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);
                    if (dataGridViewColumnTmp is not null)
                    {
                        dataGridViewColumnLast = dataGridViewColumnTmp;
                    }
                }
                while (dataGridViewColumnTmp is not null);
                if (dataGridViewColumnLast != dataGridViewColumn)
                {
                    dataGridViewColumnTmp = dataGridViewColumnLast;
                }
            }

            while (dataGridViewColumnTmp is not null && Columns.DisplayInOrder(dataGridViewColumn.Index, dataGridViewColumnTmp.Index))
            {
                dataGridViewColumnTmp.Frozen = false;
                dataGridViewColumnTmp = Columns.GetPreviousColumn(dataGridViewColumnTmp,
                    DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen,
                    DataGridViewElementStates.None);
            }
        }
    }

    private void CorrectColumnFrozenStatesForMove(DataGridViewColumn dataGridViewColumn, int newDisplayIndex)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(newDisplayIndex != dataGridViewColumn.DisplayIndex);
        Debug.Assert(!_dataGridViewOper[OperationInDisplayIndexAdjustments]);

        // No check necessary when:
        // - column is invisible.
        // - DisplayIndex decreases and column is frozen.
        // - DisplayIndex increases and column is unfrozen.

        if (!dataGridViewColumn.Visible
            || (newDisplayIndex < dataGridViewColumn.DisplayIndex && dataGridViewColumn.Frozen)
            || (newDisplayIndex > dataGridViewColumn.DisplayIndex && !dataGridViewColumn.Frozen))
        {
            return;
        }

        int colCount = Columns.Count, displayIndex;

        if (newDisplayIndex < dataGridViewColumn.DisplayIndex)
        {
            // DisplayIndex decreases.
            // Throw an exception if the visible unfrozen column is placed before a frozen column
            // Get the closest visible column placed after the displaced column
            DataGridViewColumn? dataGridViewColumnNext;
            displayIndex = newDisplayIndex;
            do
            {
                dataGridViewColumnNext = Columns.GetColumnAtDisplayIndex(displayIndex);
                displayIndex++;
            }
            while (displayIndex < colCount && (dataGridViewColumnNext is null || dataGridViewColumnNext == dataGridViewColumn || !dataGridViewColumnNext.Visible));

            if (dataGridViewColumnNext is not null && dataGridViewColumnNext.Frozen)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotMoveNonFrozenColumn);
            }
        }
        else
        {
            // DisplayIndex increases.
            // Throw an exception if the visible frozen column is placed after a non-frozen column
            // Get the closest visible column placed before the displaced column
            DataGridViewColumn? dataGridViewColumnPrev;
            displayIndex = newDisplayIndex;
            do
            {
                dataGridViewColumnPrev = Columns.GetColumnAtDisplayIndex(displayIndex);
                displayIndex--;
            }
            while (displayIndex >= 0 && (dataGridViewColumnPrev is null || !dataGridViewColumnPrev.Visible));

            if (dataGridViewColumnPrev is not null && !dataGridViewColumnPrev.Frozen)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotMoveFrozenColumn);
            }
        }
    }

    private void CorrectColumnIndexesAfterDeletion(DataGridViewColumn dataGridViewColumn)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        for (int columnIndex = dataGridViewColumn.Index; columnIndex < Columns.Count; columnIndex++)
        {
            Columns[columnIndex].Index = Columns[columnIndex].Index - 1;
            Debug.Assert(Columns[columnIndex].Index == columnIndex);
        }
    }

    private void CorrectColumnIndexesAfterInsertion(DataGridViewColumn dataGridViewColumn, int insertionCount)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(insertionCount > 0);
        for (int columnIndex = dataGridViewColumn.Index + insertionCount; columnIndex < Columns.Count; columnIndex++)
        {
            Columns[columnIndex].Index = columnIndex;
        }
    }

    private void CorrectFocus(bool onlyIfGridHasFocus)
    {
        if ((!onlyIfGridHasFocus || Focused) && EditingControl is not null)
        {
            Debug.Assert(CurrentCellInternal is not null);
            EditingControl.Focus();
        }
    }

    private void CorrectRowFrozenState(DataGridViewRow dataGridViewRow, DataGridViewElementStates rowState, int anticipatedRowIndex)
    {
        Debug.Assert(dataGridViewRow is not null);
        Debug.Assert(anticipatedRowIndex >= 0 && anticipatedRowIndex <= Rows.Count);

        int previousRowIndex = Rows.GetPreviousRow(
            anticipatedRowIndex,
            DataGridViewElementStates.Visible,
            DataGridViewElementStates.None);

        if (previousRowIndex != -1
            && (Rows.GetRowState(previousRowIndex) & DataGridViewElementStates.Frozen) == 0
            && (rowState & DataGridViewElementStates.Frozen) != 0)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAddFrozenRow);
        }
        else
        {
            int nextRowIndex = Rows.GetNextRow(
                (previousRowIndex == -1) ? anticipatedRowIndex - 1 : previousRowIndex,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);

            if (nextRowIndex != -1
                && (Rows.GetRowState(nextRowIndex) & DataGridViewElementStates.Frozen) != 0
                && (rowState & DataGridViewElementStates.Frozen) == 0)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddNonFrozenRow);
            }
        }
    }

    private void CorrectRowFrozenStates(DataGridViewRow[] dataGridViewRows, int rowIndexInserted)
    {
        bool nextVisibleRowPresent = false, previousRowFrozen = true, nextRowFrozen = false, currentRowFrozen;

        // Check if there is a visible row before the insertion point, and if it's frozen
        int rowIndexTmp = Rows.GetPreviousRow(rowIndexInserted, DataGridViewElementStates.Visible);
        if (rowIndexTmp != -1)
        {
            previousRowFrozen = (Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen;
        }

        // Check if there is a visible row at or after the insertion point, and if it's frozen
        rowIndexTmp = Rows.GetNextRow(rowIndexInserted - 1, DataGridViewElementStates.Visible);
        if (rowIndexTmp != -1)
        {
            nextVisibleRowPresent = true;
            nextRowFrozen = (Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen;
        }

        for (int arrayIndex = 0; arrayIndex < dataGridViewRows.Length; arrayIndex++)
        {
            currentRowFrozen = dataGridViewRows[arrayIndex].Frozen;
            if (!previousRowFrozen && currentRowFrozen)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddFrozenRow);
            }

            previousRowFrozen = currentRowFrozen;
            if (arrayIndex == dataGridViewRows.Length - 1 && !currentRowFrozen && nextVisibleRowPresent && nextRowFrozen)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddNonFrozenRow);
            }
        }
    }

    private void CorrectRowFrozenStates(DataGridViewRow dataGridViewRow, int rowIndex, bool frozenStateChanging)
    {
        Debug.Assert(dataGridViewRow is not null);
        int rowIndexTmp;
        if (((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) != 0 && !frozenStateChanging)
            || ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0 && frozenStateChanging))
        {
            // make sure the previous visible rows are frozen as well
            rowIndexTmp = Rows.GetPreviousRow(rowIndex,
                DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            if (rowIndexTmp == -1)
            {
                int dataGridViewRowFirst = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                if (dataGridViewRowFirst != rowIndex)
                {
                    rowIndexTmp = dataGridViewRowFirst;
                }
            }

            while (rowIndexTmp != -1 && rowIndexTmp < rowIndex)
            {
                Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Frozen, true);
                rowIndexTmp = Rows.GetNextRow(rowIndexTmp,
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.Frozen);
            }
        }
        else
        {
            // make sure the next visible rows are not frozen
            rowIndexTmp = Rows.GetNextRow(rowIndex,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.Frozen);
            if (rowIndexTmp == -1)
            {
                int dataGridViewRowLast = rowIndex;
                do
                {
                    rowIndexTmp = Rows.GetNextRow(dataGridViewRowLast,
                        DataGridViewElementStates.Visible);
                    if (rowIndexTmp != -1)
                    {
                        dataGridViewRowLast = rowIndexTmp;
                    }
                }
                while (rowIndexTmp != -1);
                if (dataGridViewRowLast != rowIndex)
                {
                    rowIndexTmp = dataGridViewRowLast;
                }
            }

            while (rowIndexTmp != -1 && rowIndexTmp > rowIndex)
            {
                Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Frozen, false);
                rowIndexTmp = Rows.GetPreviousRow(rowIndexTmp,
                    DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            }
        }
    }

    private void CorrectRowIndexesAfterDeletion(int rowIndexDeleted)
    {
        Debug.Assert(rowIndexDeleted >= 0);
        int rowsCount = Rows.Count;
        for (int rowIndex = rowIndexDeleted; rowIndex < rowsCount; rowIndex++)
        {
            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
            if (dataGridViewRow.Index >= 0)
            {
                dataGridViewRow.Index--;
                Debug.Assert(dataGridViewRow.Index == rowIndex);
            }
        }

        // Fix 'new' row index if existant
        if (NewRowIndex == rowIndexDeleted)
        {
            NewRowIndex = -1; // No more 'new' row.
        }
        else if (NewRowIndex != -1)
        {
            NewRowIndex--;
        }
    }

    private void CorrectRowIndexesAfterInsertion(int rowIndexInserted, int insertionCount)
    {
        Debug.Assert(rowIndexInserted >= 0);
        Debug.Assert(insertionCount > 0);
        int rowsCount = Rows.Count;
        for (int rowIndex = rowIndexInserted + insertionCount; rowIndex < rowsCount; rowIndex++)
        {
            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
            if (dataGridViewRow.Index >= 0)
            {
                dataGridViewRow.Index += insertionCount;
                Debug.Assert(dataGridViewRow.Index == rowIndex);
            }
        }

        // Lastly update the 'new' row index if needed.
        if (NewRowIndex != -1)
        {
            NewRowIndex += insertionCount;
        }
    }

    protected override AccessibleObject CreateAccessibilityInstance()
    {
        return new DataGridViewAccessibleObject(this);
    }

    protected override ControlCollection CreateControlsInstance()
    {
        return new DataGridViewControlCollection(this);
    }

    /// <summary>
    ///  Constructs the new instance of the Columns collection objects. Subclasses
    ///  should not call base.CreateColumnsInstance.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual DataGridViewColumnCollection CreateColumnsInstance()
    {
        return new DataGridViewColumnCollection(this);
    }

    /// <summary>
    ///  Constructs the new instance of the Rows collection objects. Subclasses
    ///  should not call base.CreateRowsInstance.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual DataGridViewRowCollection CreateRowsInstance()
    {
        return new DataGridViewRowCollection(this);
    }

    private RECT[]? CreateScrollableRegion(Rectangle scroll)
    {
        if (_cachedScrollableRegion is not null)
        {
            return _cachedScrollableRegion;
        }

        using (Region region = new(scroll))
        {
            HRGN hrgn = default;
            using (Graphics graphics = CreateGraphicsInternal())
            {
                hrgn = (HRGN)region.GetHrgn(graphics);
            }

            if (!hrgn.IsNull)
            {
                _cachedScrollableRegion = hrgn.GetRegionRects();
                region.ReleaseHrgn((IntPtr)hrgn);
            }
        }

        return _cachedScrollableRegion;
    }

    private void DiscardNewRow()
    {
        Debug.Assert(Rows.Count > 1);
        Debug.Assert(NewRowIndex != -1);

        DataGridViewRowCancelEventArgs dgvrce = new(Rows[NewRowIndex]);
        OnUserDeletingRow(dgvrce);
        if (dgvrce.Cancel)
        {
            return;
        }

        // Delete the 'new' row
        Debug.Assert(NewRowIndex == Rows.Count - 1);
        DataGridViewRow dataGridViewRow = Rows[NewRowIndex];
        Rows.RemoveAtInternal(NewRowIndex, force: false);
        Debug.Assert(dataGridViewRow.Index == -1);
        DataGridViewRowEventArgs dgvre = new(dataGridViewRow);
        OnUserDeletedRow(dgvre);

        // CorrectRowIndexesAfterDeletion resets this.newRowIndex to -1.
        Debug.Assert(NewRowIndex == -1);

        if (AllowUserToAddRowsInternal)
        {
            NewRowIndex = Rows.Count - 1;
            Debug.Assert((Rows.GetRowState(NewRowIndex) & DataGridViewElementStates.Visible) != 0);
            Debug.Assert(_ptCurrentCell.Y == NewRowIndex);

            OnDefaultValuesNeeded(new DataGridViewRowEventArgs(Rows[NewRowIndex]));
            InvalidateRowPrivate(NewRowIndex);
        }
    }

    private void DiscardZonesInScrollingArea(
        ref Rectangle rectScrollingArea,
        int emptyBackgroundWidth,
        int emptyBackgroundHeight,
        int frozenVisibleRowsHeight,
        bool discardFrozenColumns,
        bool discardFrozenRows)
    {
        // Discard empty background
        rectScrollingArea.Width -= emptyBackgroundWidth;
        rectScrollingArea.Height -= emptyBackgroundHeight;
        if (RightToLeftInternal)
        {
            rectScrollingArea.X += emptyBackgroundWidth;
        }

        if (discardFrozenColumns)
        {
            // Discard frozen columns
            int frozenVisibleColumnsWidth = Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            if (!RightToLeftInternal)
            {
                rectScrollingArea.X += frozenVisibleColumnsWidth;
            }

            rectScrollingArea.Width -= frozenVisibleColumnsWidth;
        }

        if (discardFrozenRows)
        {
            // Discard frozen rows
            rectScrollingArea.Y += frozenVisibleRowsHeight;
            rectScrollingArea.Height -= frozenVisibleRowsHeight;
        }
    }

    public int DisplayedColumnCount(bool includePartialColumns)
    {
        int cxMax = _layout.Data.Width, cx = 0;
        int completeColumns = 0, partialColumns = 0;
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        while (dataGridViewColumn is not null && cx < cxMax)
        {
            partialColumns++;
            cx += dataGridViewColumn.Thickness;
            if (cx <= cxMax)
            {
                completeColumns++;
                dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                    DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen,
                    DataGridViewElementStates.None);
            }
        }

        if (cx < cxMax && DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0)
        {
            if (FirstDisplayedScrollingColumnHiddenWidth > 0)
            {
                cx -= FirstDisplayedScrollingColumnHiddenWidth;
                completeColumns--;
            }

            dataGridViewColumn = Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol];
            Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen);

            while (dataGridViewColumn is not null && cx < cxMax)
            {
                partialColumns++;
                cx += dataGridViewColumn.Thickness;
                if (cx <= cxMax)
                {
                    completeColumns++;
                    dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);
                }
            }
        }

        return includePartialColumns ? partialColumns : completeColumns;
    }

    public int DisplayedRowCount(bool includePartialRow)
    {
        return includePartialRow
            ? (DisplayedBandsInfo.NumDisplayedFrozenRows + DisplayedBandsInfo.NumDisplayedScrollingRows)
            : (DisplayedBandsInfo.NumTotallyDisplayedFrozenRows + DisplayedBandsInfo.NumTotallyDisplayedScrollingRows);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _dataGridViewOper[OperationInDispose] = true;
            try
            {
                if (Properties.TryGetValue(s_propToolTip, out ToolTip? keyboardToolTip))
                {
                    keyboardToolTip.Dispose();
                }

                // Remove any Columns contained in this control
                for (int i = 0; i < Columns.Count; i++)
                {
                    Columns[i].Dispose();
                }

                Columns.Clear();

                UnwireScrollBarsEvents();

                _vertScrollBar?.Dispose();
                _vertScrollBar = null!;

                _horizScrollBar?.Dispose();
                _horizScrollBar = null!;

                _placeholderStringFormat?.Dispose();
                _placeholderStringFormat = null;

                _latestEditingControl?.Dispose();
                _latestEditingControl = null;

                EditingControl?.Dispose();
                EditingControl = null;

                _editingPanel?.Dispose();
                _editingPanel = null;

                Debug.Assert(NoSelectionChangeCount == 0);

                DataConnection?.Dispose();

                // DGV should dispose the tool tip control before disposing itself.
                _toolTipControl.Dispose();
            }
            finally
            {
                _dataGridViewOper[OperationInDispose] = false;
            }
        }

        base.Dispose(disposing);
    }

    private void DrawColHeaderShadow(Graphics g, int mouseX)
    {
        Rectangle r = CalcColRelocationFeedbackRect(mouseX);
        DrawShadowRect(r);
        if (_dataGridViewState2[State2_ShowColumnRelocationInsertion])
        {
            Rectangle rectInsertionBar = new(0, _layout.ColumnHeaders.Top, InsertionBarWidth, _layout.ColumnHeaders.Height);
            // this.trackColumnEdge is the column preceding the insertion point
            if (_trackColumnEdge == -1)
            {
                // Insert as first column
                rectInsertionBar.X = GetColumnXFromIndex(Columns.GetFirstColumn(DataGridViewElementStates.Visible)!.Index);
                if (RightToLeftInternal)
                {
                    rectInsertionBar.X -= InsertionBarWidth;
                }
            }
            else
            {
                int offsetFromCenter = 0;
                if (Columns.GetNextColumn(Columns[_trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None) is null)
                {
                    if (!RightToLeftInternal)
                    {
                        offsetFromCenter = InsertionBarWidth;
                    }
                }
                else
                {
                    offsetFromCenter = RightToLeftInternal ? InsertionBarWidth / 2 - 1 : InsertionBarWidth / 2 + 1;
                }

                rectInsertionBar.X = RightToLeftInternal
                    ? Math.Max(
                        _layout.ColumnHeaders.X,
                        GetColumnXFromIndex(_trackColumnEdge) - Columns[_trackColumnEdge].Width - offsetFromCenter)
                    : Math.Min(
                        GetColumnXFromIndex(_trackColumnEdge) + Columns[_trackColumnEdge].Width - offsetFromCenter,
                        _layout.ColumnHeaders.Right - InsertionBarWidth);
            }

            if (ApplyVisualStylesToHeaderCells)
            {
                using var brush = SystemColors.HotTrack.GetCachedSolidBrushScope();
                g.FillRectangle(brush, rectInsertionBar);
            }
            else
            {
                ControlPaint.FillReversibleRectangle(RectangleToScreen(rectInsertionBar), Color.White);
            }
        }
    }

    /// <summary>
    ///  Draws an XOR region to give UI feedback for Column Resizing.
    ///  This looks just like the Splitter control's UI when resizing.
    /// </summary>
    private void DrawColSplitBar(int mouseX)
    {
        Rectangle r = CalcColResizeFeedbackRect(mouseX);
        DrawSplitBar(r);
    }

    /// <summary>
    ///  Draws an XOR region to give UI feedback for Row Resizing.
    ///  This looks just like the Splitter control's UI when resizing.
    /// </summary>
    private void DrawRowSplitBar(int mouseY)
    {
        Rectangle r = CalcRowResizeFeedbackRect(mouseY);
        DrawSplitBar(r);
    }

    private void DrawShadowRect(Rectangle r)
    {
        const byte DATAGRIDVIEW_shadowEdgeThickness = 3;

        using GetDcScope dc = new(HWND, HRGN.Null, GET_DCX_FLAGS.DCX_CACHE | GET_DCX_FLAGS.DCX_LOCKWINDOWUPDATE);
        HBRUSH halftone = ControlPaint.CreateHalftoneHBRUSH();
        HGDIOBJ saveBrush = PInvokeCore.SelectObject(dc, halftone);

        PInvoke.PatBlt(dc, r.X, r.Y, r.Width, DATAGRIDVIEW_shadowEdgeThickness, ROP_CODE.PATINVERT);
        PInvoke.PatBlt(dc, r.X, r.Y + r.Height - DATAGRIDVIEW_shadowEdgeThickness, r.Width, DATAGRIDVIEW_shadowEdgeThickness, ROP_CODE.PATINVERT);
        PInvoke.PatBlt(dc, r.X, r.Y + DATAGRIDVIEW_shadowEdgeThickness, DATAGRIDVIEW_shadowEdgeThickness, r.Height - 2 * DATAGRIDVIEW_shadowEdgeThickness, ROP_CODE.PATINVERT);
        PInvoke.PatBlt(dc, r.X + r.Width - DATAGRIDVIEW_shadowEdgeThickness, r.Y + DATAGRIDVIEW_shadowEdgeThickness, DATAGRIDVIEW_shadowEdgeThickness, r.Height - 2 * DATAGRIDVIEW_shadowEdgeThickness, ROP_CODE.PATINVERT);

        PInvokeCore.SelectObject(dc, saveBrush);
        PInvokeCore.DeleteObject(halftone);
    }

    /// <summary>
    ///  Draws an XOR region to give UI feedback for Column/Row Resizing.
    ///  This looks just like the Splitter control's UI when resizing.
    /// </summary>
    private void DrawSplitBar(Rectangle r)
    {
        using GetDcScope dc = new(HWND, HRGN.Null, GET_DCX_FLAGS.DCX_CACHE | GET_DCX_FLAGS.DCX_LOCKWINDOWUPDATE);
        HBRUSH halftone = ControlPaint.CreateHalftoneHBRUSH();
        HGDIOBJ saveBrush = PInvokeCore.SelectObject(dc, halftone);
        PInvoke.PatBlt(dc, r.X, r.Y, r.Width, r.Height, ROP_CODE.PATINVERT);
        PInvokeCore.SelectObject(dc, saveBrush);
        PInvokeCore.DeleteObject(halftone);
        GC.KeepAlive(this);
    }

    private void EditingControls_CommonMouseEventHandler(object? sender, MouseEventArgs e, DataGridViewMouseEvent dgvme)
    {
        Debug.Assert(_editingPanel is not null);
        Debug.Assert(_ptCurrentCell.X != -1);
        int adjustedX = _editingPanel.Location.X + e.X;
        int adjustedY = _editingPanel.Location.Y + e.Y;
        if (sender == EditingControl && EditingControl is not null)
        {
            adjustedX += EditingControl.Location.X;
            adjustedY += EditingControl.Location.Y;
        }

        if (dgvme == DataGridViewMouseEvent.MouseDown && e.Clicks == 1)
        {
            // Reset the flag that records single-click exposed as double-click.
            _dataGridViewOper[OperationLastEditCtrlClickDoubled] = false;
        }

        MouseEventArgs me = new(
            e.Button,
            e.Clicks,
            adjustedX,
            adjustedY,
            e.Delta);

        HitTestInfo hti = HitTest(me.X, me.Y);
        int mouseX = me.X - hti.ColumnX;
        if (RightToLeftInternal)
        {
            mouseX += ((hti._col == -1) ? RowHeadersWidth : Columns[hti._col].Thickness);
        }

        DataGridViewCellMouseEventArgs dgvcme = new(
            hti._col,
            hti._row,
            mouseX,
            me.Y - hti.RowY,
            me);

        try
        {
            _dataGridViewState2[State2_MessageFromEditingCtrls] = true;

            // Check to see if this is a CellMouseDoubleClick situation
            if ((dgvme == DataGridViewMouseEvent.MouseDown || dgvme == DataGridViewMouseEvent.Click || dgvme == DataGridViewMouseEvent.MouseClick)
                && (DateTime.Now.Ticks - _lastMouseClickInfo.TimeStamp) / 10000 <= SystemInformation.DoubleClickTime
                && e.Button == _lastMouseClickInfo.Button
                && e.Clicks == 1
                && dgvcme.ColumnIndex == _lastMouseClickInfo.Col
                && dgvcme.RowIndex == _lastMouseClickInfo.Row)
            {
                Size hotDoubleClickZone = SystemInformation.DoubleClickSize;
                if (Math.Abs(dgvcme.X - _lastMouseClickInfo.X) <= hotDoubleClickZone.Width / 2
                    && Math.Abs(dgvcme.Y - _lastMouseClickInfo.Y) <= hotDoubleClickZone.Height / 2)
                {
                    me = new MouseEventArgs(
                        e.Button,
                        2,
                        adjustedX,
                        adjustedY,
                        e.Delta);
                    dgvcme = new DataGridViewCellMouseEventArgs(
                        dgvcme.ColumnIndex,
                        dgvcme.RowIndex,
                        dgvcme.X,
                        dgvcme.Y,
                        me);
                    switch (dgvme)
                    {
                        case DataGridViewMouseEvent.MouseDown:
                            {
                                OnMouseDown(me);
                                if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                                {
                                    OnCellMouseDown(dgvcme);
                                }

                                break;
                            }

                        case DataGridViewMouseEvent.Click:
                            {
                                OnDoubleClick(me);
                                if (e.Button == MouseButtons.Left && dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                                {
                                    OnCellDoubleClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex));
                                }

                                break;
                            }

                        case DataGridViewMouseEvent.MouseClick:
                            {
                                // Set the flag that prevents the triple-click to be exposed as a double-click
                                _dataGridViewOper[OperationLastEditCtrlClickDoubled] = true;

                                OnMouseDoubleClick(me);
                                if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                                {
                                    OnCellMouseDoubleClick(dgvcme);
                                }

                                break;
                            }
                    }

                    return;
                }
            }

            if (_dataGridViewOper[OperationLastEditCtrlClickDoubled])
            {
                // Make sure that the triple-click is exposed as a single-click and not a double-click.
                if (e.Clicks == 2)
                {
                    me = new MouseEventArgs(
                        e.Button,
                        1,
                        adjustedX,
                        adjustedY,
                        e.Delta);
                    dgvcme = new DataGridViewCellMouseEventArgs(
                        hti._col,
                        hti._row,
                        mouseX,
                        me.Y - hti.RowY,
                        me);
                }

                switch (dgvme)
                {
                    case DataGridViewMouseEvent.DoubleClick:
                        dgvme = DataGridViewMouseEvent.Click;
                        break;
                    case DataGridViewMouseEvent.MouseDoubleClick:
                        dgvme = DataGridViewMouseEvent.MouseClick;
                        break;
                }
            }

            switch (dgvme)
            {
                case DataGridViewMouseEvent.Click:
                    OnClick(me);
                    if (e.Button == MouseButtons.Left && dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                    {
                        OnCellClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex));
                    }

                    break;
                case DataGridViewMouseEvent.DoubleClick:
                    OnDoubleClick(me);
                    if (e.Button == MouseButtons.Left && dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                    {
                        OnCellDoubleClick(new DataGridViewCellEventArgs(dgvcme.ColumnIndex, dgvcme.RowIndex));
                    }

                    break;
                case DataGridViewMouseEvent.MouseClick:
                    OnMouseClick(me);
                    if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                    {
                        OnCellMouseClick(dgvcme);
                    }

                    break;
                case DataGridViewMouseEvent.MouseDoubleClick:
                    OnMouseDoubleClick(me);
                    if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                    {
                        OnCellMouseDoubleClick(dgvcme);
                    }

                    break;
                case DataGridViewMouseEvent.MouseDown:
                    OnMouseDown(me);
                    if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                    {
                        OnCellMouseDown(dgvcme);
                    }

                    break;
                case DataGridViewMouseEvent.MouseUp:
                    if (_dataGridViewState2[State2_NextMouseUpIsDouble])
                    {
                        MouseEventArgs meTmp = new(
                            e.Button,
                            2,
                            adjustedX,
                            adjustedY,
                            e.Delta);
                        dgvcme = new DataGridViewCellMouseEventArgs(
                            dgvcme.ColumnIndex,
                            dgvcme.RowIndex,
                            dgvcme.X,
                            dgvcme.Y,
                            meTmp);
                    }

                    OnCellMouseUp(dgvcme);
                    OnMouseUp(me);
                    break;
                case DataGridViewMouseEvent.MouseMove:
                    OnCellMouseMove(dgvcme);
                    break;
            }
        }
        finally
        {
            _dataGridViewState2[State2_MessageFromEditingCtrls] = false;
        }
    }

    private void EditingControls_Click(object? sender, EventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        Debug.Assert(_ptCurrentCell.X != -1);
        if (e is MouseEventArgs me)
        {
            EditingControls_CommonMouseEventHandler(sender, me, DataGridViewMouseEvent.Click);
        }
    }

    private void EditingControls_DoubleClick(object? sender, EventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        Debug.Assert(_ptCurrentCell.X != -1);
        if (e is MouseEventArgs me)
        {
            EditingControls_CommonMouseEventHandler(sender, me, DataGridViewMouseEvent.DoubleClick);
        }
    }

    private void EditingControls_MouseClick(object? sender, MouseEventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseClick);
    }

    private void EditingControls_MouseDoubleClick(object? sender, MouseEventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseDoubleClick);
    }

    private void EditingControls_MouseDown(object? sender, MouseEventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseDown);
    }

    private void EditingControls_MouseEnter(object? sender, EventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        if (sender == _editingPanel)
        {
            Debug.Assert(EditingControl is not null);
            Debug.Assert(!_dataGridViewState1[State1_CustomCursorSet]);
            _dataGridViewState1[State1_CustomCursorSet] = true;
            _oldCursor = Cursor;
            CursorInternal = ((IDataGridViewEditingControl)EditingControl).EditingPanelCursor;
        }

        if (_dataGridViewState2[State2_MouseEnterExpected])
        {
            OnMouseEnter(EventArgs.Empty);
        }

        UpdateMouseEnteredCell(hti: null, e: null);
    }

    private void EditingControls_MouseLeave(object? sender, EventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        if (sender == _editingPanel)
        {
            Debug.Assert(EditingControl is not null);
            if (_dataGridViewState1[State1_CustomCursorSet])
            {
                _dataGridViewState1[State1_CustomCursorSet] = false;
                CursorInternal = _oldCursor;
            }
        }

        UpdateMouseEnteredCell(hti: null, e: null);

        Point ptMouse = PointToClient(MousePosition);
        if (!ClientRectangle.Contains(ptMouse))
        {
            OnMouseLeave(EventArgs.Empty);
        }
    }

    private void EditingControls_MouseMove(object? sender, MouseEventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseMove);
    }

    private void EditingControls_MouseUp(object? sender, MouseEventArgs e)
    {
        Debug.Assert(sender == EditingControl || sender == _editingPanel);
        EditingControls_CommonMouseEventHandler(sender, e, DataGridViewMouseEvent.MouseUp);
    }

    private void EndColumnHeadersResize(MouseEventArgs e)
    {
        try
        {
            if (_currentRowSplitBar != -1)
            {
                Invalidate(CalcRowResizeFeedbackRect(_currentRowSplitBar), true);
                _lastRowSplitBar = _currentRowSplitBar = -1;
            }

            int y = Math.Min(e.Y + _mouseBarOffset, _layout.Data.Bottom - 1);
            int delta = y - _layout.ColumnHeaders.Y - ColumnHeadersHeight + 1;
            if (_trackRowAnchor != y && delta != 0)
            {
                ColumnHeadersHeight += delta;
            }
        }
        finally
        {
            ReleaseMouse();
        }
    }

    private void EndColumnRelocation(MouseEventArgs e, HitTestInfo hti)
    {
        try
        {
            if (_lastHeaderShadow != -1)
            {
                _dataGridViewState2[State2_ShowColumnRelocationInsertion] = false;
                _trackColumnEdge = -1;
                _lastHeaderShadow = -1;
                Invalidate(Rectangle.Union(_layout.TopLeftHeader, _layout.ColumnHeaders));
            }

            if (ColumnRelocationTarget(e, hti, out int previousColumnIndex))
            {
                if (previousColumnIndex == -1)
                {
                    Columns[_trackColumn].DisplayIndex = 0;
                }
                else if (Columns[_trackColumn].DisplayIndex > Columns[previousColumnIndex].DisplayIndex)
                {
                    Columns[_trackColumn].DisplayIndex = Columns[previousColumnIndex].DisplayIndex + 1;
                }
                else
                {
                    Columns[_trackColumn].DisplayIndex = Columns[previousColumnIndex].DisplayIndex;
                }
            }
        }
        finally
        {
            ReleaseMouse();
        }
    }

    private void EndColumnResize(MouseEventArgs e)
    {
        try
        {
            EndColumnResize(e.X);
        }
        finally
        {
            ReleaseMouse();
        }
    }

    private void EndColumnResize(int x)
    {
        int newX, delta;
        if (RightToLeftInternal)
        {
            newX = Math.Max(x + _mouseBarOffset, _layout.Data.X);
            delta = GetColumnXFromIndex(_trackColumn) - Columns[_trackColumn].Thickness - newX + 1;
        }
        else
        {
            newX = Math.Min(x + _mouseBarOffset, _layout.Data.Right - 1);
            delta = newX - (GetColumnXFromIndex(_trackColumn) + Columns[_trackColumn].Thickness) + 1;
        }

        if (_trackColAnchor != newX && delta != 0)
        {
            int proposed = Columns[_trackColumn].Thickness + delta;
            Debug.Assert(proposed >= Columns[_trackColumn].MinimumThickness);
            Debug.Assert(proposed <= DataGridViewBand.MaxBandThickness);
            Columns[_trackColumn].Thickness = proposed;
        }
    }

    public bool EndEdit()
    {
        return EndEdit(DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit);
    }

    public bool EndEdit(DataGridViewDataErrorContexts context) => EditMode == DataGridViewEditMode.EditOnEnter
        ? CommitEdit(context)
        : EndEdit(
            context,
            validateCell: DataGridViewValidateCellInternal.Never,
            fireCellLeave: false,
            fireCellEnter: false,
            fireRowLeave: false,
            fireRowEnter: false,
            fireLeave: false,
            keepFocus: true,
            resetCurrentCell: true,
            resetAnchorCell: true);

    private bool EndEdit(
        DataGridViewDataErrorContexts context,
        DataGridViewValidateCellInternal validateCell,
        bool fireCellLeave,
        bool fireCellEnter,
        bool fireRowLeave,
        bool fireRowEnter,
        bool fireLeave,
        bool keepFocus,
        bool resetCurrentCell,
        bool resetAnchorCell)
    {
        if (_ptCurrentCell.X == -1)
        {
            return true;
        }

        _dataGridViewOper[OperationInEndEdit] = true;
        try
        {
            int curRowIndex = _ptCurrentCell.Y;
            int curColIndex = _ptCurrentCell.X;
            DataGridViewCell? dataGridViewCurrentCell = CurrentCellInternal;
            DataGridViewDataErrorEventArgs? dgvdee = CommitEdit(
                ref dataGridViewCurrentCell,
                context,
                validateCell,
                fireCellLeave,
                fireCellEnter,
                fireRowLeave,
                fireRowEnter,
                fireLeave);
            if (dgvdee is not null)
            {
                if (dgvdee.ThrowException)
                {
                    throw dgvdee.Exception;
                }

                if (dgvdee.Cancel)
                {
                    return false;
                }

                // Restore old value
                dgvdee = CancelEditPrivate();
                if (dgvdee is not null)
                {
                    if (dgvdee.ThrowException)
                    {
                        throw dgvdee.Exception;
                    }

                    if (dgvdee.Cancel)
                    {
                        return false;
                    }
                }
            }

            if (!IsCurrentCellInEditMode)
            {
                return true;
            }

            if (curRowIndex != _ptCurrentCell.Y || curColIndex != _ptCurrentCell.X)
            {
                return true;
            }

            if (EditingControl is not null)
            {
                UnwireEditingControlEvents();
                _dataGridViewState2[State2_MouseOverRemovedEditingCtrl] = MouseOverEditingControl;
                _dataGridViewState2[State2_MouseOverRemovedEditingPanel] = MouseOverEditingPanel;
                _dataGridViewState1[State1_EditingControlChanging] = true;
                try
                {
                    dataGridViewCurrentCell!.DetachEditingControl();
                }
                finally
                {
                    _dataGridViewState1[State1_EditingControlChanging] = false;
                }

                ImeMode editingControlImeMode = EditingControl.CachedImeMode; // If in restricted mode, ImeMode will be Disable.
                _latestEditingControl = EditingControl;
                Debug.Assert(_editingPanel is null || _editingPanel.Controls.Count == 0);
                EditingControl = null;
                InvalidateCellPrivate(_ptCurrentCell.X, _ptCurrentCell.Y);

                if (EditMode == DataGridViewEditMode.EditOnEnter)
                {
                    if (resetCurrentCell)
                    {
                        bool success = SetCurrentCellAddressCore(
                            -1,
                            -1,
                            resetAnchorCell,
                            validateCurrentCell: false,
                            throughMouseClick: false);
                        Debug.Assert(success);
                    }
                }

                if (keepFocus)
                {
                    Focus();
                }

                ImeMode = editingControlImeMode;
            }
            else
            {
                Debug.Assert(_dataGridViewState1[State1_CurrentCellInEditMode]);
                _dataGridViewState1[State1_CurrentCellInEditMode] = false;
                InvalidateCellPrivate(_ptCurrentCell.X, _ptCurrentCell.Y);
            }

            if (!IsInnerCellOutOfBounds(curColIndex, curRowIndex))
            {
                DataGridViewCellEventArgs dgvce = new(curColIndex, curRowIndex);
                OnCellEndEdit(dgvce);
            }

            return true;
        }
        finally
        {
            _dataGridViewOper[OperationInEndEdit] = false;
        }
    }

    private void EndRowHeadersResize(MouseEventArgs e)
    {
        try
        {
            if (_currentColSplitBar != -1)
            {
                Invalidate(CalcColResizeFeedbackRect(_currentColSplitBar), true);
                _lastColSplitBar = _currentColSplitBar = -1;
            }

            int x, delta;
            if (RightToLeftInternal)
            {
                x = Math.Max(e.X + _mouseBarOffset, _layout.Data.Left - 1);
                delta = _layout.RowHeaders.Right - RowHeadersWidth - x - 1;
            }
            else
            {
                x = Math.Min(e.X + _mouseBarOffset, _layout.Data.Right - 1);
                delta = x - _layout.RowHeaders.X - RowHeadersWidth + 1;
            }

            if (_trackColAnchor != x && delta != 0)
            {
                RowHeadersWidth += delta;
            }
        }
        finally
        {
            ReleaseMouse();
        }
    }

    private void EndRowResize(MouseEventArgs e)
    {
        try
        {
            if (_currentRowSplitBar != -1)
            {
                Invalidate(CalcRowResizeFeedbackRect(_currentRowSplitBar), true);
                _lastRowSplitBar = _currentRowSplitBar = -1;
            }

            DataGridViewRow dataGridViewRow = Rows.SharedRow(_trackRow);
            dataGridViewRow.GetHeightInfo(_trackRow, out int height, out int minimumHeight);
            int y = Math.Min(e.Y + _mouseBarOffset, _layout.Data.Bottom - 1);
            int delta = y - (GetRowYFromIndex(_trackRow) + height) + 1;
            if (_trackRowAnchor != y && delta != 0)
            {
                int proposedHeight = height + delta;
                proposedHeight = Math.Max(proposedHeight, minimumHeight);
                if (!OnRowHeightInfoPushed(_trackRow, proposedHeight, minimumHeight))
                {
                    if (dataGridViewRow.Index == -1)
                    {
                        dataGridViewRow = Rows[_trackRow];  // Un-sharing row
                    }

                    Debug.Assert(_autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None);
                    dataGridViewRow.ThicknessInternal = proposedHeight;
                }
            }
        }
        finally
        {
            ReleaseMouse();
        }
    }

    private void ExitBulkLayout(bool invalidInAdjustFillingColumns)
    {
        if (_inBulkLayoutCount > 0)
        {
            _inBulkLayoutCount--;
            if (_inBulkLayoutCount == 0)
            {
                PerformLayoutPrivate(
                    useRowShortcut: false,
                    computeVisibleRows: false,
                    invalidInAdjustFillingColumns,
                    repositionEditingControl: false);
            }
        }
    }

    private void ExitBulkPaint(int columnIndex, int rowIndex)
    {
        if (_inBulkPaintCount > 0)
        {
            _inBulkPaintCount--;
            if (_inBulkPaintCount == 0)
            {
                if (columnIndex >= 0)
                {
                    InvalidateColumnInternal(columnIndex);
                }
                else if (rowIndex >= 0)
                {
                    InvalidateRowPrivate(rowIndex);
                }
                else
                {
                    Invalidate();
                }
            }
        }
    }

    private void FirstVisibleScrollingRowTempted(int rowIndex)
    {
        Debug.Assert(rowIndex >= 0);
        Debug.Assert(rowIndex < Rows.Count);
        Debug.Assert((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0);
        Debug.Assert((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0);

        int displayHeight = _layout.Data.Height;
        if (displayHeight <= 0)
        {
            return;
        }

        int totalVisibleFrozenHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        if (totalVisibleFrozenHeight < displayHeight)
        {
            DisplayedBandsInfo.FirstDisplayedScrollingRow = rowIndex;
        }
    }

    private void FlushDisplayedChanged()
    {
        if (DisplayedBandsInfo.Dirty && Visible)
        {
            // Handle the rows

            if (!RowHeadersVisible && Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0)
            {
                // All rows are hidden
                UpdateRowsDisplayedState(displayed: false);
            }
            else
            {
                Rectangle rectScreen = Screen.FromControl(this).WorkingArea;
                int maxDisplayedRows = rectScreen.Height / DataGridViewBand.MinBandThickness;

                // Make sure all displayed scrolling rows have the Displayed state.
                int rowIndexTmp = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                if (rowIndexTmp != -1)
                {
                    int numDisplayedScrollingRows = DisplayedBandsInfo.NumDisplayedScrollingRows;
                    Debug.Assert(numDisplayedScrollingRows > 0);
                    while (numDisplayedScrollingRows > 0)
                    {
                        Debug.Assert(rowIndexTmp != -1);
                        if ((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0)
                        {
                            Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true);
                        }

                        rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible);
                        numDisplayedScrollingRows--;
                    }

                    int rowIndexTmp2 = rowIndexTmp;

                    // Make sure all scrolling rows before FirstDisplayedScrollingRow have their Displayed state set to false
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow != -1);
                    rowIndexTmp = Rows.GetPreviousRow(DisplayedBandsInfo.FirstDisplayedScrollingRow, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                    while (rowIndexTmp != -1 && (Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0)
                    {
                        Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false);
                        rowIndexTmp = Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                    }

                    // Make sure all rows below last displayed scrolling row have Displayed state set to false (next loop)
                    rowIndexTmp = rowIndexTmp2;
                }
                else
                {
                    // No displayed scrolling rows. Make sure all non-frozen rows have their Displayed state set to false (next loop)
                    rowIndexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                }

                while (rowIndexTmp != -1 && (Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0)
                {
                    Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false);
                    rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible);
                }

                // Make sure all displayed frozen rows have their Displayed state set to true
                int numDisplayedFrozenRows = DisplayedBandsInfo.NumDisplayedFrozenRows;
                rowIndexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                while (numDisplayedFrozenRows > 0)
                {
                    Debug.Assert(rowIndexTmp != -1);
                    if ((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0)
                    {
                        Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true);
                    }

                    rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                    numDisplayedFrozenRows--;
                }

                // Make sure all non-displayed frozen rows have their Displayed state set to false
                while (rowIndexTmp != -1 && (Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0)
                {
                    Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false);
                    rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                }

                // Treat the cases where the old displayed rows are completely disjoint from the new displayed rows
                int lastDisplayedFrozenRowIndex = -1;
                int lastDisplayedScrollingRowIndex = -1;

                if (DisplayedBandsInfo.NumDisplayedFrozenRows > 0)
                {
                    int firstDisplayedFrozenRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                    Debug.Assert(firstDisplayedFrozenRowIndex != -1);
                    lastDisplayedFrozenRowIndex = DisplayedBandsInfo.NumDisplayedFrozenRows > 1
                        ? Rows.GetNextRow(
                            firstDisplayedFrozenRowIndex,
                            DataGridViewElementStates.Visible,
                            skipRows: DisplayedBandsInfo.NumDisplayedFrozenRows - 2)
                        : firstDisplayedFrozenRowIndex;
                }

                if (DisplayedBandsInfo.FirstDisplayedScrollingRow != -1)
                {
                    lastDisplayedScrollingRowIndex = DisplayedBandsInfo.NumDisplayedScrollingRows > 1
                        ? Rows.GetNextRow(
                            DisplayedBandsInfo.FirstDisplayedScrollingRow,
                            DataGridViewElementStates.Visible,
                            skipRows: DisplayedBandsInfo.NumDisplayedScrollingRows - 2)
                        : DisplayedBandsInfo.FirstDisplayedScrollingRow;
                }

                rowIndexTmp = DisplayedBandsInfo.OldFirstDisplayedScrollingRow;
                while (rowIndexTmp != -1
                    && rowIndexTmp < DisplayedBandsInfo.FirstDisplayedScrollingRow
                    && !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex))
                {
                    if ((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0)
                    {
                        break;
                    }
                    else
                    {
                        Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false);
                        rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible);
                    }
                }

                rowIndexTmp = DisplayedBandsInfo.OldFirstDisplayedScrollingRow;
                if (rowIndexTmp != -1
                    && rowIndexTmp < Rows.Count
                    && (DisplayedBandsInfo.FirstDisplayedScrollingRow == -1 || DisplayedBandsInfo.FirstDisplayedScrollingRow < rowIndexTmp)
                    && !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex))
                {
                    while (rowIndexTmp != -1)
                    {
                        if ((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0)
                        {
                            break;
                        }
                        else
                        {
                            Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false);
                            rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible);
                        }
                    }
                }

                if (DisplayedBandsInfo.RowInsertionOccurred)
                {
                    // Adjust the scrolling rows that were pushed down by the rows insertion
                    rowIndexTmp = DisplayedBandsInfo.OldFirstDisplayedScrollingRow;
                    if (rowIndexTmp != -1)
                    {
                        rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible, DisplayedBandsInfo.OldNumDisplayedScrollingRows - 1);
                        if (rowIndexTmp == -1)
                        {
                            rowIndexTmp = Rows.GetLastRow(DataGridViewElementStates.Visible);
                        }

                        int rowCount = 0;
                        while (rowIndexTmp != -1
                            && rowCount <= maxDisplayedRows
                            && !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex))
                        {
                            if ((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0)
                            {
                                Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false);
                            }

                            rowIndexTmp = Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible);
                            rowCount++;
                        }
                    }

                    // Adjust the frozen rows that were pushed down by the rows insertion
                    rowIndexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                    if (rowIndexTmp != -1)
                    {
                        rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DisplayedBandsInfo.OldNumDisplayedFrozenRows - 1);
                        if (rowIndexTmp == -1)
                        {
                            rowIndexTmp = Rows.GetLastRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                        }

                        int rowCount = 0;
                        while (rowIndexTmp != -1
                            && rowCount <= maxDisplayedRows
                            && !RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex))
                        {
                            if ((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0)
                            {
                                Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, false);
                            }

                            rowIndexTmp = Rows.GetPreviousRow(rowIndexTmp, DataGridViewElementStates.Visible);
                            rowCount++;
                        }
                    }
                }

#if DEBUG
                for (rowIndexTmp = 0; rowIndexTmp < Rows.Count; rowIndexTmp++)
                {
                    DataGridViewElementStates rowStateDbg = Rows.GetRowState(rowIndexTmp);
                    bool rowNeedsDisplayedState = RowNeedsDisplayedState(rowIndexTmp, lastDisplayedFrozenRowIndex, lastDisplayedScrollingRowIndex);
                    if (((rowStateDbg & DataGridViewElementStates.Displayed) != 0) != rowNeedsDisplayedState)
                    {
                        Debug.Fail("Unexpected Displayed state for row");
                    }
                }
#endif
            }

            // Handle the columns

            if (!ColumnHeadersVisible && Rows.GetRowCount(DataGridViewElementStates.Visible) == 0)
            {
                // All columns are hidden
                UpdateColumnsDisplayedState(displayed: false);
            }
            else
            {
                // Make sure all displayed scrolling columns have the Displayed state.
                DataGridViewColumn? dataGridViewColumnTmp;
                int columnIndexTmp = DisplayedBandsInfo.FirstDisplayedScrollingCol;
                if (columnIndexTmp != -1)
                {
                    int numDisplayedScrollingCols = DisplayedBandsInfo.NumDisplayedScrollingCols;
                    Debug.Assert(numDisplayedScrollingCols > 0);
                    dataGridViewColumnTmp = Columns[columnIndexTmp];
                    while (numDisplayedScrollingCols > 0)
                    {
                        Debug.Assert(dataGridViewColumnTmp is not null);
                        if (!dataGridViewColumnTmp.Displayed)
                        {
                            dataGridViewColumnTmp.Displayed = true;
                        }

                        dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                        numDisplayedScrollingCols--;
                    }

                    DataGridViewColumn? dataGridViewColumnTmp2 = dataGridViewColumnTmp;

                    // Make sure all scrolling columns before FirstDisplayedScrollingCol have their Displayed state set to false
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingCol != -1);
                    dataGridViewColumnTmp = Columns.GetPreviousColumn(Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol], DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                    while (dataGridViewColumnTmp is not null && dataGridViewColumnTmp.Displayed)
                    {
                        dataGridViewColumnTmp.Displayed = false;
                        dataGridViewColumnTmp = Columns.GetPreviousColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                    }

                    // Make sure all columns after last displayed scrolling column have Displayed state set to false (next loop)
                    dataGridViewColumnTmp = dataGridViewColumnTmp2;
                }
                else
                {
                    // No displayed scrolling columns. Make sure all non-frozen columns have their Displayed state set to false (next loop)
                    dataGridViewColumnTmp = Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                }

                while (dataGridViewColumnTmp is not null && dataGridViewColumnTmp.Displayed)
                {
                    dataGridViewColumnTmp.Displayed = false;
                    dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                }

                // Make sure all displayed frozen columns have their Displayed state set to true
                int numDisplayedFrozenCols = DisplayedBandsInfo.NumDisplayedFrozenCols;
                dataGridViewColumnTmp = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                while (numDisplayedFrozenCols > 0)
                {
                    Debug.Assert(dataGridViewColumnTmp is not null);
                    if (!dataGridViewColumnTmp.Displayed)
                    {
                        dataGridViewColumnTmp.Displayed = true;
                    }

                    dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None);
                    numDisplayedFrozenCols--;
                }

                // Make sure all non-displayed frozen columns have their Displayed state set to false
                while (dataGridViewColumnTmp is not null && dataGridViewColumnTmp.Displayed)
                {
                    dataGridViewColumnTmp.Displayed = false;
                    dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None);
                }

                // Treat the cases where the old displayed columns are completely disjoint from the new displayed columns

                columnIndexTmp = DisplayedBandsInfo.OldFirstDisplayedScrollingCol;
                while (columnIndexTmp != -1
                    && columnIndexTmp < Columns.Count
                    && DisplayedBandsInfo.FirstDisplayedScrollingCol != -1
                    && columnIndexTmp != DisplayedBandsInfo.FirstDisplayedScrollingCol
                    && Columns.DisplayInOrder(columnIndexTmp, DisplayedBandsInfo.FirstDisplayedScrollingCol)
                    && !ColumnNeedsDisplayedState(Columns[columnIndexTmp]))
                {
                    dataGridViewColumnTmp = Columns[columnIndexTmp];
                    if (!dataGridViewColumnTmp.Displayed)
                    {
                        break;
                    }
                    else
                    {
                        dataGridViewColumnTmp.Displayed = false;
                        dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                        columnIndexTmp = (dataGridViewColumnTmp is null) ? -1 : dataGridViewColumnTmp.Index;
                    }
                }

                columnIndexTmp = DisplayedBandsInfo.OldFirstDisplayedScrollingCol;
                if (columnIndexTmp != -1
                    && columnIndexTmp < Columns.Count
                    && (DisplayedBandsInfo.FirstDisplayedScrollingCol == -1
                        || (DisplayedBandsInfo.FirstDisplayedScrollingCol != columnIndexTmp
                            && Columns.DisplayInOrder(DisplayedBandsInfo.FirstDisplayedScrollingCol, columnIndexTmp)))
                    && !ColumnNeedsDisplayedState(Columns[columnIndexTmp]))
                {
                    dataGridViewColumnTmp = Columns[columnIndexTmp];
                    while (dataGridViewColumnTmp is not null)
                    {
                        if (!dataGridViewColumnTmp.Displayed)
                        {
                            break;
                        }
                        else
                        {
                            dataGridViewColumnTmp.Displayed = false;
                            dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                        }
                    }
                }

                if (DisplayedBandsInfo.ColumnInsertionOccurred)
                {
                    dataGridViewColumnTmp = Columns[Columns.Count - 1];
                    while (dataGridViewColumnTmp is not null && !ColumnNeedsDisplayedState(dataGridViewColumnTmp))
                    {
                        if (dataGridViewColumnTmp.Displayed)
                        {
                            dataGridViewColumnTmp.Displayed = false;
                        }

                        dataGridViewColumnTmp = Columns.GetPreviousColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                    }
                }

#if DEBUG
                for (columnIndexTmp = 0; columnIndexTmp < Columns.Count; columnIndexTmp++)
                {
                    DataGridViewElementStates colStateDbg = Columns[columnIndexTmp].State;
                    bool columnNeedsDisplayedState = ColumnNeedsDisplayedState(Columns[columnIndexTmp]);
                    if (((colStateDbg & DataGridViewElementStates.Displayed) != 0) != columnNeedsDisplayedState)
                    {
                        Debug.Fail("Unexpected Displayed state for column");
                    }
                }
#endif
            }

            DisplayedBandsInfo.Dirty = false;
        }
    }

    private void FlushDisplayIndexChanged(bool raiseEvent)
    {
        foreach (DataGridViewColumn dataGridViewColumn in Columns)
        {
            if (dataGridViewColumn.DisplayIndexHasChanged)
            {
                dataGridViewColumn.DisplayIndexHasChanged = false;
                if (raiseEvent)
                {
                    OnColumnDisplayIndexChanged(dataGridViewColumn);
                }
            }
        }
    }

    private void FlushSelectionChanged()
    {
        if (_dataGridViewState2[State2_RaiseSelectionChanged])
        {
            OnSelectionChanged(EventArgs.Empty);
        }
    }

    protected override AccessibleObject? GetAccessibilityObjectById(int objectId)
    {
        // Decrement the objectId because in our implementation of AccessibilityClient notification objectId's
        // are 1 - based. 0 == CHILDID_SELF which corresponds to the AccessibleObject itself
        return AccessibilityObject.GetChild(objectId - 1);
    }

    internal TypeConverter GetCachedTypeConverter(Type type)
    {
        if (_converters.TryGetValue(type, out TypeConverter? converter))
        {
            return converter;
        }

        converter = TypeDescriptor.GetConverter(type);
        _converters.Add(type, converter);
        return converter;
    }

    internal Rectangle GetCellAdjustedDisplayRectangle(int columnIndex, int rowIndex, bool cutOverflow)
    {
        Rectangle rect = GetCellDisplayRectangle(columnIndex, rowIndex, cutOverflow);
        if (!rect.IsEmpty)
        {
            if (SingleVerticalBorderAdded && columnIndex == FirstDisplayedColumnIndex)
            {
                if (!RightToLeftInternal)
                {
                    rect.X--;
                }

                rect.Width++;
            }

            if (SingleHorizontalBorderAdded && rowIndex == FirstDisplayedRowIndex)
            {
                rect.Y--;
                rect.Height++;
            }
        }

        return rect;
    }

    public int GetCellCount(DataGridViewElementStates includeFilter)
    {
        if ((includeFilter & ~(DataGridViewElementStates.Displayed | DataGridViewElementStates.Frozen | DataGridViewElementStates.Resizable |
                               DataGridViewElementStates.ReadOnly | DataGridViewElementStates.Selected | DataGridViewElementStates.Visible)) != 0)
        {
            throw new ArgumentException(string.Format(SR.DataGridView_InvalidDataGridViewElementStateCombination, nameof(includeFilter)));
        }

        int cellCount = 0;
        bool displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired;

        if ((includeFilter & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected)
        {
            if (includeFilter == DataGridViewElementStates.Selected)
            {
                cellCount = _individualSelectedCells.Count;
                switch (SelectionMode)
                {
                    case DataGridViewSelectionMode.CellSelect:
                        {
                            // If we change the design and decide that SelectAll() should use band selection,
                            // we need to take the bands into account.
                            return cellCount;
                        }

                    case DataGridViewSelectionMode.FullColumnSelect:
                    case DataGridViewSelectionMode.ColumnHeaderSelect:
                        {
                            return cellCount + _selectedBandIndexes.Count * Rows.Count;
                        }

                    case DataGridViewSelectionMode.FullRowSelect:
                    case DataGridViewSelectionMode.RowHeaderSelect:
                        {
                            return cellCount + _selectedBandIndexes.Count * Columns.Count;
                        }
                }
            }

            displayedRequired = (includeFilter & DataGridViewElementStates.Displayed) == DataGridViewElementStates.Displayed;
            frozenRequired = (includeFilter & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen;
            resizableRequired = (includeFilter & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable;
            readOnlyRequired = (includeFilter & DataGridViewElementStates.ReadOnly) == DataGridViewElementStates.ReadOnly;
            visibleRequired = (includeFilter & DataGridViewElementStates.Visible) == DataGridViewElementStates.Visible;

            foreach (DataGridViewCell dataGridViewCell in _individualSelectedCells)
            {
                if (GetCellCount_CellIncluded(dataGridViewCell, dataGridViewCell.RowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired))
                {
                    cellCount++;
                }
            }

            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                    {
                        // If we change the design and decide that SelectAll() should use band selection,
                        // we need to take the bands into account.
                        return cellCount;
                    }

                case DataGridViewSelectionMode.FullColumnSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    {
                        for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
                        {
                            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                            foreach (int columnIndex in _selectedBandIndexes)
                            {
                                DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex];
                                if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired))
                                {
                                    cellCount++;
                                }
                            }
                        }

                        return cellCount;
                    }

                case DataGridViewSelectionMode.FullRowSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    {
                        foreach (int rowIndex in _selectedBandIndexes)
                        {
                            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                            foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells)
                            {
                                if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired))
                                {
                                    cellCount++;
                                }
                            }
                        }

                        return cellCount;
                    }
            }
        }

        if ((includeFilter == DataGridViewElementStates.ReadOnly && ReadOnly) || includeFilter == DataGridViewElementStates.None)
        {
            return Rows.Count * Columns.Count;
        }

        displayedRequired = (includeFilter & DataGridViewElementStates.Displayed) == DataGridViewElementStates.Displayed;
        frozenRequired = (includeFilter & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen;
        resizableRequired = (includeFilter & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable;
        readOnlyRequired = (includeFilter & DataGridViewElementStates.ReadOnly) == DataGridViewElementStates.ReadOnly;
        visibleRequired = (includeFilter & DataGridViewElementStates.Visible) == DataGridViewElementStates.Visible;

        for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
        {
            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
            if (!visibleRequired || (Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0)
            {
                foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells)
                {
                    if (GetCellCount_CellIncluded(dataGridViewCell, rowIndex, displayedRequired, frozenRequired, resizableRequired, readOnlyRequired, visibleRequired))
                    {
                        cellCount++;
                    }
                }
            }
        }

        return cellCount;
    }

    private bool GetCellCount_CellIncluded(
        DataGridViewCell dataGridViewCell,
        int rowIndex,
        bool displayedRequired,
        bool frozenRequired,
        bool resizableRequired,
        bool readOnlyRequired,
        bool visibleRequired)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        Debug.Assert(rowIndex >= 0);
        Debug.Assert(dataGridViewCell.OwningColumn is not null);
        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
        if (displayedRequired)
        {
            bool cellDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0
                && dataGridViewCell.OwningColumn.Displayed;

            if (!cellDisplayed)
            {
                return false;
            }
        }

        if (frozenRequired)
        {
            bool cellFrozen = (rowState & DataGridViewElementStates.Frozen) != 0
                || dataGridViewCell.OwningColumn.Frozen
                || dataGridViewCell.StateIncludes(DataGridViewElementStates.Frozen);

            if (!cellFrozen)
            {
                return false;
            }
        }

        if (resizableRequired)
        {
            if (!RowIsResizable(rowIndex) && dataGridViewCell.OwningColumn.Resizable != DataGridViewTriState.True)
            {
                return false;
            }
        }

        if (readOnlyRequired)
        {
            bool cellReadOnly = ReadOnly
                || (rowState & DataGridViewElementStates.ReadOnly) != 0
                || dataGridViewCell.OwningColumn.ReadOnly
                || dataGridViewCell.StateIncludes(DataGridViewElementStates.ReadOnly);

            if (!cellReadOnly)
            {
                return false;
            }
        }

        if (visibleRequired)
        {
            bool cellVisible = (rowState & DataGridViewElementStates.Visible) != 0
                && dataGridViewCell.OwningColumn.Visible;

            if (!cellVisible)
            {
                return false;
            }
        }

        return true;
    }

    public Rectangle GetCellDisplayRectangle(int columnIndex, int rowIndex, bool cutOverflow)
    {
        Rectangle rowRect;
        Rectangle columnRect;

        if (columnIndex >= 0)
        {
            ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);
            columnRect = GetColumnDisplayRectanglePrivate(columnIndex, cutOverflow);
        }
        else
        {
            ArgumentOutOfRangeException.ThrowIfNotEqual(columnIndex, -1);
            columnRect = rowIndex >= 0 ? _layout.RowHeaders : _layout.TopLeftHeader;
        }

        if (rowIndex >= 0)
        {
            ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);
            rowRect = GetRowDisplayRectanglePrivate(rowIndex, cutOverflow);
        }
        else
        {
            ArgumentOutOfRangeException.ThrowIfNotEqual(rowIndex, -1);
            rowRect = columnIndex >= 0 ? _layout.ColumnHeaders : _layout.TopLeftHeader;
        }

        if (!cutOverflow)
        {
            int height = rowRect.Bottom - columnRect.Bottom;
            if (height > 0)
            {
                columnRect.Height += height;
            }

            int width;
            if (RightToLeftInternal)
            {
                width = rowRect.X - columnRect.X;
                if (width > 0)
                {
                    rowRect.Width += width;
                    rowRect.X -= width;
                }
            }
            else
            {
                width = columnRect.Right - rowRect.Right;
                if (width > 0)
                {
                    rowRect.Width += width;
                }
            }
        }

        rowRect.Intersect(columnRect);

        return rowRect;
    }

    internal DataGridViewCell GetCellInternal(int columnIndex, int rowIndex)
    {
        Debug.Assert(columnIndex >= -1 && columnIndex < Columns.Count);
        Debug.Assert(rowIndex >= -1 && rowIndex < Rows.Count);
        if (rowIndex >= 0)
        {
            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
            Debug.Assert(dataGridViewRow is not null);
            return columnIndex >= 0 ? dataGridViewRow.Cells[columnIndex] : dataGridViewRow.HeaderCell;
        }
        else
        {
            return columnIndex >= 0 ? Columns[columnIndex].HeaderCell : (DataGridViewCell)TopLeftHeaderCell;
        }
    }

    public virtual DataObject? GetClipboardContent()
    {
        if (ClipboardCopyMode == DataGridViewClipboardCopyMode.Disable)
        {
            throw new NotSupportedException(SR.DataGridView_DisabledClipboardCopy);
        }

        if (CurrentCellIsEditedAndOnlySelectedCell)
        {
            return null;
        }

        string[] formats =
        [
            DataFormats.Html,
            DataFormats.Text,
            DataFormats.UnicodeText,
            DataFormats.CommaSeparatedValue
        ];

        DataObject dataObject = new();
        bool includeColumnHeaders;
        bool includeRowHeaders;
        StringBuilder? sbContent = null;
        DataGridViewColumn? dataGridViewColumn;
        DataGridViewColumn? prevDataGridViewColumn;
        DataGridViewColumn? nextDataGridViewColumn;

        string? cellContent;
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.FullRowSelect:
                if (Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) == 0)
                {
                    return null;
                }

                if (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText)
                {
                    includeColumnHeaders = (Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Selected) == -1);
                    includeRowHeaders = true;
                }
                else
                {
                    includeColumnHeaders = includeRowHeaders = (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText);
                }

                includeColumnHeaders &= ColumnHeadersVisible;
                includeRowHeaders &= RowHeadersVisible;

                foreach (string format in formats)
                {
                    if (sbContent is null)
                    {
                        sbContent = new StringBuilder(1024);
                    }
                    else
                    {
                        sbContent.Length = 0;
                    }

                    if (includeColumnHeaders)
                    {
                        if (RightToLeftInternal)
                        {
                            // Cycle through the visible columns in their reverse display order
                            dataGridViewColumn = Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                            if (dataGridViewColumn is not null)
                            {
                                prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: true,
                                    lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                    inFirstRow: true,
                                    inLastRow: false,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                while (prevDataGridViewColumn is not null)
                                {
                                    dataGridViewColumn = prevDataGridViewColumn;
                                    prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                    cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(
                                        -1,
                                        firstCell: false,
                                        lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                        inFirstRow: true,
                                        inLastRow: false,
                                        format) as string;
                                    if (cellContent is not null)
                                    {
                                        sbContent.Append(cellContent);
                                    }
                                }
                            }

                            if (includeRowHeaders)
                            {
                                cellContent = TopLeftHeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0,
                                    lastCell: true,
                                    inFirstRow: true,
                                    inLastRow: false,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }
                        }
                        else
                        {
                            if (includeRowHeaders)
                            {
                                cellContent = TopLeftHeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: true,
                                    lastCell: Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0,
                                    inFirstRow: true,
                                    inLastRow: false,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }

                            // Cycle through the visible columns in their display order
                            dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                            if (dataGridViewColumn is not null)
                            {
                                nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: !includeRowHeaders,
                                    lastCell: nextDataGridViewColumn is null,
                                    inFirstRow: true,
                                    inLastRow: false,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                while (nextDataGridViewColumn is not null)
                                {
                                    dataGridViewColumn = nextDataGridViewColumn;
                                    nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                    cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(
                                        -1,
                                        firstCell: false,
                                        lastCell: nextDataGridViewColumn is null,
                                        inFirstRow: true,
                                        inLastRow: false,
                                        format) as string;
                                    if (cellContent is not null)
                                    {
                                        sbContent.Append(cellContent);
                                    }
                                }
                            }
                        }
                    }

                    // Cycle through the visible selected rows.
                    bool firstRowIndex = true;
                    int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected);
                    Debug.Assert(rowIndex != -1);
                    int nextRowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected);
                    while (rowIndex != -1)
                    {
                        if (RightToLeftInternal)
                        {
                            dataGridViewColumn = Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None);

                            // Cycle through the visible columns in their reverse display order
                            if (dataGridViewColumn is not null)
                            {
                                prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                cellContent = Rows.SharedRow(rowIndex)
                                    .Cells[dataGridViewColumn.Index]
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: true,
                                        lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                while (prevDataGridViewColumn is not null)
                                {
                                    dataGridViewColumn = prevDataGridViewColumn;
                                    prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                    cellContent = Rows.SharedRow(rowIndex)
                                        .Cells[dataGridViewColumn.Index]
                                        .GetClipboardContentInternal(
                                            rowIndex,
                                            firstCell: false,
                                            lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                            inFirstRow: !includeColumnHeaders && firstRowIndex,
                                            inLastRow: nextRowIndex == -1,
                                            format) as string;
                                    if (cellContent is not null)
                                    {
                                        sbContent.Append(cellContent);
                                    }
                                }
                            }

                            // Get the row header clipboard content
                            if (includeRowHeaders)
                            {
                                cellContent = Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(
                                    rowIndex,
                                    firstCell: Columns.GetColumnCount(DataGridViewElementStates.Visible) == 0,
                                    lastCell: true,
                                    inFirstRow: !includeColumnHeaders && firstRowIndex,
                                    inLastRow: nextRowIndex == -1,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }
                        }
                        else
                        {
                            dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);

                            // Get the row header clipboard content
                            if (includeRowHeaders)
                            {
                                cellContent = Rows.SharedRow(rowIndex).HeaderCell.GetClipboardContentInternal(
                                    rowIndex,
                                    firstCell: true,
                                    lastCell: dataGridViewColumn is null,
                                    inFirstRow: !includeColumnHeaders && firstRowIndex,
                                    inLastRow: nextRowIndex == -1,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }

                            // Cycle through the visible columns in their display order
                            if (dataGridViewColumn is not null)
                            {
                                nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                cellContent = Rows.SharedRow(rowIndex)
                                    .Cells[dataGridViewColumn.Index]
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: !includeRowHeaders,
                                        lastCell: nextDataGridViewColumn is null,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                while (nextDataGridViewColumn is not null)
                                {
                                    dataGridViewColumn = nextDataGridViewColumn;
                                    nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                    cellContent = Rows.SharedRow(rowIndex)
                                        .Cells[dataGridViewColumn.Index]
                                        .GetClipboardContentInternal(
                                            rowIndex,
                                            firstCell: false,
                                            lastCell: nextDataGridViewColumn is null,
                                            inFirstRow: !includeColumnHeaders && firstRowIndex,
                                            inLastRow: nextRowIndex == -1,
                                            format) as string;
                                    if (cellContent is not null)
                                    {
                                        sbContent.Append(cellContent);
                                    }
                                }
                            }
                        }

                        rowIndex = nextRowIndex;
                        if (rowIndex != -1)
                        {
                            nextRowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected);
                        }

                        firstRowIndex = false;
                    }

                    if (string.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase))
                    {
                        GetClipboardContentForHtml(sbContent, out MemoryStream utf8Stream);
                        dataObject.SetData(format, autoConvert: false, utf8Stream);
                    }
                    else
                    {
                        dataObject.SetData(format, autoConvert: false, sbContent.ToString());
                    }
                }

                break;

            case DataGridViewSelectionMode.FullColumnSelect:
                if (Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) == 0)
                {
                    return null;
                }

                if (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText)
                {
                    includeColumnHeaders = true;
                    includeRowHeaders = (Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Selected) is null);
                }
                else
                {
                    includeColumnHeaders = includeRowHeaders = (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText);
                }

                includeColumnHeaders &= ColumnHeadersVisible;
                includeRowHeaders &= RowHeadersVisible;

                int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);

                foreach (string format in formats)
                {
                    if (sbContent is null)
                    {
                        sbContent = new StringBuilder(1024);
                    }
                    else
                    {
                        sbContent.Length = 0;
                    }

                    if (includeColumnHeaders)
                    {
                        if (RightToLeftInternal)
                        {
                            // Cycle through the visible & selected columns in their display order
                            DataGridViewColumn? lastDataGridViewColumn = Columns.GetLastColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                            dataGridViewColumn = lastDataGridViewColumn;
                            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                            if (dataGridViewColumn is not null)
                            {
                                prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                                cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: true,
                                    lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                    inFirstRow: true,
                                    inLastRow: firstVisibleRowIndex == -1,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                while (prevDataGridViewColumn is not null)
                                {
                                    dataGridViewColumn = prevDataGridViewColumn;
                                    prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                                    cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(
                                        -1,
                                        firstCell: false,
                                        lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                        inFirstRow: true,
                                        inLastRow: firstVisibleRowIndex == -1,
                                        format) as string;
                                    if (cellContent is not null)
                                    {
                                        sbContent.Append(cellContent);
                                    }
                                }
                            }

                            if (includeRowHeaders)
                            {
                                cellContent = TopLeftHeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: lastDataGridViewColumn is null,
                                    lastCell: true,
                                    inFirstRow: true,
                                    inLastRow: firstVisibleRowIndex == -1,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }
                        }
                        else
                        {
                            dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected);

                            if (includeRowHeaders)
                            {
                                cellContent = TopLeftHeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: true,
                                    lastCell: dataGridViewColumn is null,
                                    inFirstRow: true,
                                    inLastRow: firstVisibleRowIndex == -1,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }

                            // Cycle through the visible & selected columns in their display order
                            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                            if (dataGridViewColumn is not null)
                            {
                                nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                                cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: !includeRowHeaders,
                                    lastCell: nextDataGridViewColumn is null,
                                    inFirstRow: true,
                                    inLastRow: firstVisibleRowIndex == -1,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                while (nextDataGridViewColumn is not null)
                                {
                                    dataGridViewColumn = nextDataGridViewColumn;
                                    nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                                    cellContent = dataGridViewColumn.HeaderCell.GetClipboardContentInternal(
                                        -1,
                                        firstCell: false,
                                        lastCell: nextDataGridViewColumn is null,
                                        inFirstRow: true,
                                        inLastRow: firstVisibleRowIndex == -1,
                                        format) as string;
                                    if (cellContent is not null)
                                    {
                                        sbContent.Append(cellContent);
                                    }
                                }
                            }
                        }
                    }

                    // Cycle through the visible rows.
                    bool firstRowIndex = true;
                    int rowIndex = firstVisibleRowIndex;
                    int nextRowIndex = -1;
                    if (rowIndex != -1)
                    {
                        nextRowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                    }

                    while (rowIndex != -1)
                    {
                        if (RightToLeftInternal)
                        {
                            DataGridViewColumn? lastDataGridViewColumn = Columns.GetLastColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);

                            // Cycle through the visible & selected columns in their reverse display order
                            dataGridViewColumn = lastDataGridViewColumn;
                            if (dataGridViewColumn is not null)
                            {
                                prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                                cellContent = Rows.SharedRow(rowIndex)
                                    .Cells[dataGridViewColumn.Index]
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: true,
                                        lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                while (prevDataGridViewColumn is not null)
                                {
                                    dataGridViewColumn = prevDataGridViewColumn;
                                    prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                                    cellContent = Rows.SharedRow(rowIndex)
                                        .Cells[dataGridViewColumn.Index]
                                        .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: false,
                                        lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                    if (cellContent is not null)
                                    {
                                        sbContent.Append(cellContent);
                                    }
                                }
                            }

                            // Get the row header clipboard content
                            if (includeRowHeaders)
                            {
                                cellContent = Rows.SharedRow(rowIndex)
                                    .HeaderCell
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: lastDataGridViewColumn is null,
                                        lastCell: true,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }
                        }
                        else
                        {
                            dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected);

                            // Get the row header clipboard content
                            if (includeRowHeaders)
                            {
                                cellContent = Rows.SharedRow(rowIndex)
                                    .HeaderCell
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: true,
                                        lastCell: dataGridViewColumn is null,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }

                            // Cycle through the visible & selected columns in their display order
                            if (dataGridViewColumn is not null)
                            {
                                nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                                cellContent = Rows.SharedRow(rowIndex)
                                    .Cells[dataGridViewColumn.Index]
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: !includeRowHeaders,
                                        lastCell: nextDataGridViewColumn is null,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                while (nextDataGridViewColumn is not null)
                                {
                                    dataGridViewColumn = nextDataGridViewColumn;
                                    nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible | DataGridViewElementStates.Selected, DataGridViewElementStates.None);
                                    cellContent = Rows.SharedRow(rowIndex)
                                        .Cells[dataGridViewColumn.Index]
                                        .GetClipboardContentInternal(
                                            rowIndex,
                                            firstCell: false,
                                            lastCell: nextDataGridViewColumn is null,
                                            inFirstRow: !includeColumnHeaders && firstRowIndex,
                                            inLastRow: nextRowIndex == -1,
                                            format) as string;
                                    if (cellContent is not null)
                                    {
                                        sbContent.Append(cellContent);
                                    }
                                }
                            }
                        }

                        rowIndex = nextRowIndex;
                        if (rowIndex != -1)
                        {
                            nextRowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                        }

                        firstRowIndex = false;
                    }

                    if (string.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase))
                    {
                        GetClipboardContentForHtml(sbContent, out MemoryStream utf8Stream);
                        dataObject.SetData(format, autoConvert: false, utf8Stream);
                    }
                    else
                    {
                        dataObject.SetData(format, autoConvert: false, sbContent.ToString());
                    }
                }

                break;

            case DataGridViewSelectionMode.CellSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
            case DataGridViewSelectionMode.ColumnHeaderSelect:
                bool selectedVisibleCellExists = false;
                bool selectedVisibleColumnExists = false;
                bool selectedVisibleRowExists = false;
                if (SelectionMode == DataGridViewSelectionMode.RowHeaderSelect)
                {
                    selectedVisibleRowExists = Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) != 0;
                    selectedVisibleCellExists = selectedVisibleRowExists && Columns.GetColumnCount(DataGridViewElementStates.Visible) != 0;
                }
                else if (SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)
                {
                    selectedVisibleColumnExists = Columns.GetColumnCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Selected) != 0;
                    selectedVisibleCellExists = selectedVisibleColumnExists && Rows.GetRowCount(DataGridViewElementStates.Visible) != 0;
                }

                if (!selectedVisibleCellExists && _individualSelectedCells.Count > 0)
                {
                    foreach (DataGridViewCell dataGridViewCell in _individualSelectedCells)
                    {
                        if (dataGridViewCell.Visible)
                        {
                            selectedVisibleCellExists = true;
                            break;
                        }
                    }
                }

                if (!selectedVisibleCellExists)
                {
                    return null;
                }

                // There is at least one selected visible cell.
                if (SelectionMode == DataGridViewSelectionMode.CellSelect)
                {
                    includeColumnHeaders = includeRowHeaders = (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText);
                    includeColumnHeaders &= ColumnHeadersVisible;
                    includeRowHeaders &= RowHeadersVisible;
                }
                else
                {
                    includeColumnHeaders = includeRowHeaders = false;
                    if (ColumnHeadersVisible)
                    {
                        if (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText)
                        {
                            if (selectedVisibleColumnExists)
                            {
                                includeColumnHeaders = true;
                            }
                        }
                        else
                        {
                            includeColumnHeaders = (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText);
                        }
                    }

                    if (RowHeadersVisible)
                    {
                        if (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableWithAutoHeaderText)
                        {
                            if (selectedVisibleRowExists)
                            {
                                includeRowHeaders = true;
                            }
                        }
                        else
                        {
                            includeRowHeaders = (_clipboardCopyMode == DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText);
                        }
                    }
                }

                // Get the four corners of the 'selected table'
                int lRowIndex = int.MaxValue;
                int uRowIndex = -1;
                DataGridViewColumn? lColumn = null;
                DataGridViewColumn? uColumn = null;
                if (SelectionMode == DataGridViewSelectionMode.RowHeaderSelect)
                {
                    DataGridViewColumn? firstVisibleColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                    DataGridViewColumn? lastVisibleColumn = Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None);

                    Debug.Assert(firstVisibleColumn is not null);
                    Debug.Assert(lastVisibleColumn is not null);
                    foreach (int rowIndex in _selectedBandIndexes)
                    {
                        if ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0)
                        {
                            if (rowIndex < lRowIndex)
                            {
                                lRowIndex = rowIndex;
                            }

                            if (rowIndex > uRowIndex)
                            {
                                uRowIndex = rowIndex;
                            }

                            lColumn = firstVisibleColumn;
                            uColumn = lastVisibleColumn;
                        }
                    }
                }
                else if (SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)
                {
                    firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                    int lastVisibleRowIndex = Rows.GetLastRow(DataGridViewElementStates.Visible);
                    Debug.Assert(firstVisibleRowIndex != -1);
                    Debug.Assert(lastVisibleRowIndex != -1);
                    foreach (int columnIndex in _selectedBandIndexes)
                    {
                        if (Columns[columnIndex].Visible)
                        {
                            if (lColumn is null || Columns.DisplayInOrder(columnIndex, lColumn.Index))
                            {
                                lColumn = Columns[columnIndex];
                            }

                            if (uColumn is null || Columns.DisplayInOrder(uColumn.Index, columnIndex))
                            {
                                uColumn = Columns[columnIndex];
                            }

                            lRowIndex = firstVisibleRowIndex;
                            uRowIndex = lastVisibleRowIndex;
                        }
                    }
                }

                // Go through the individually selected cells to potentially stretch the current 'selected table'.
                foreach (DataGridViewCell dataGridViewCell in _individualSelectedCells)
                {
                    if (dataGridViewCell.Visible)
                    {
                        if (dataGridViewCell.RowIndex < lRowIndex)
                        {
                            lRowIndex = dataGridViewCell.RowIndex;
                        }

                        if (dataGridViewCell.RowIndex > uRowIndex)
                        {
                            uRowIndex = dataGridViewCell.RowIndex;
                        }

                        if (lColumn is null || Columns.DisplayInOrder(dataGridViewCell.ColumnIndex, lColumn.Index))
                        {
                            lColumn = dataGridViewCell.OwningColumn;
                        }

                        if (uColumn is null || Columns.DisplayInOrder(uColumn.Index, dataGridViewCell.ColumnIndex))
                        {
                            uColumn = dataGridViewCell.OwningColumn;
                        }
                    }
                }

                Debug.Assert(lRowIndex != -1);
                Debug.Assert(uRowIndex != -1);
                Debug.Assert(lColumn is not null);
                Debug.Assert(uColumn is not null);
                Debug.Assert(lColumn.Index == uColumn.Index || Columns.DisplayInOrder(lColumn.Index, uColumn.Index));
                Debug.Assert(lRowIndex <= uRowIndex);

                foreach (string format in formats)
                {
                    if (sbContent is null)
                    {
                        sbContent = new StringBuilder(1024);
                    }
                    else
                    {
                        sbContent.Length = 0;
                    }

                    if (includeColumnHeaders)
                    {
                        if (RightToLeftInternal)
                        {
                            // Cycle through the visible columns from uColumn to lColumn
                            dataGridViewColumn = uColumn;
                            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                            while (dataGridViewColumn is not null)
                            {
                                if (dataGridViewColumn != lColumn)
                                {
                                    prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                    Debug.Assert(prevDataGridViewColumn is not null);
                                }
                                else
                                {
                                    prevDataGridViewColumn = null;
                                }

                                cellContent = dataGridViewColumn.HeaderCell
                                    .GetClipboardContentInternal(
                                        -1,
                                        firstCell: dataGridViewColumn == uColumn,
                                        lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                        inFirstRow: true,
                                        inLastRow: false,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                dataGridViewColumn = prevDataGridViewColumn;
                            }

                            if (includeRowHeaders)
                            {
                                cellContent = TopLeftHeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: false,
                                    lastCell: true,
                                    inFirstRow: true,
                                    inLastRow: false,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }
                        }
                        else
                        {
                            if (includeRowHeaders)
                            {
                                cellContent = TopLeftHeaderCell.GetClipboardContentInternal(
                                    -1,
                                    firstCell: true,
                                    lastCell: false,
                                    inFirstRow: true,
                                    inLastRow: false,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }

                            // Cycle through the visible columns from lColumn to uColumn
                            dataGridViewColumn = lColumn;
                            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                            while (dataGridViewColumn is not null)
                            {
                                if (dataGridViewColumn != uColumn)
                                {
                                    nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                    Debug.Assert(nextDataGridViewColumn is not null);
                                }
                                else
                                {
                                    nextDataGridViewColumn = null;
                                }

                                cellContent = dataGridViewColumn.HeaderCell
                                    .GetClipboardContentInternal(
                                        -1,
                                        firstCell: !includeRowHeaders && dataGridViewColumn == lColumn,
                                        lastCell: nextDataGridViewColumn is null,
                                        inFirstRow: true,
                                        inLastRow: false,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                dataGridViewColumn = nextDataGridViewColumn;
                            }
                        }
                    }

                    // Cycle through the visible rows from lRowIndex to uRowIndex.
                    bool firstRowIndex = true;
                    int rowIndex = lRowIndex;
                    int nextRowIndex = -1;
                    Debug.Assert(rowIndex != -1);
                    while (rowIndex != -1)
                    {
                        if (rowIndex != uRowIndex)
                        {
                            nextRowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                            Debug.Assert(nextRowIndex != -1);
                        }
                        else
                        {
                            nextRowIndex = -1;
                        }

                        if (RightToLeftInternal)
                        {
                            // Cycle through the visible columns from uColumn to lColumn
                            dataGridViewColumn = uColumn;
                            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                            while (dataGridViewColumn is not null)
                            {
                                if (dataGridViewColumn != lColumn)
                                {
                                    prevDataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                    Debug.Assert(prevDataGridViewColumn is not null);
                                }
                                else
                                {
                                    prevDataGridViewColumn = null;
                                }

                                cellContent = Rows.SharedRow(rowIndex)
                                    .Cells[dataGridViewColumn.Index]
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: dataGridViewColumn == uColumn,
                                        lastCell: !includeRowHeaders && prevDataGridViewColumn is null,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                dataGridViewColumn = prevDataGridViewColumn;
                            }

                            if (includeRowHeaders)
                            {
                                // Get the row header clipboard content
                                cellContent = Rows.SharedRow(rowIndex)
                                    .HeaderCell
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: false,
                                        lastCell: true,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }
                        }
                        else
                        {
                            if (includeRowHeaders)
                            {
                                // Get the row header clipboard content
                                cellContent = Rows.SharedRow(rowIndex)
                                    .HeaderCell
                                    .GetClipboardContentInternal(
                                        rowIndex,
                                        firstCell: true,
                                        lastCell: false,
                                        inFirstRow: !includeColumnHeaders && firstRowIndex,
                                        inLastRow: nextRowIndex == -1,
                                        format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }
                            }

                            // Cycle through the visible columns from lColumn to uColumn
                            dataGridViewColumn = lColumn;
                            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                            while (dataGridViewColumn is not null)
                            {
                                if (dataGridViewColumn != uColumn)
                                {
                                    nextDataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                    Debug.Assert(nextDataGridViewColumn is not null);
                                }
                                else
                                {
                                    nextDataGridViewColumn = null;
                                }

                                cellContent = Rows.SharedRow(rowIndex)
                                    .Cells[dataGridViewColumn.Index]
                                    .GetClipboardContentInternal(
                                    rowIndex,
                                    firstCell: !includeRowHeaders && dataGridViewColumn == lColumn,
                                    lastCell: nextDataGridViewColumn is null,
                                    inFirstRow: !includeColumnHeaders && firstRowIndex,
                                    inLastRow: nextRowIndex == -1,
                                    format) as string;
                                if (cellContent is not null)
                                {
                                    sbContent.Append(cellContent);
                                }

                                dataGridViewColumn = nextDataGridViewColumn;
                            }
                        }

                        rowIndex = nextRowIndex;
                        firstRowIndex = false;
                    }

                    if (string.Equals(format, DataFormats.Html, StringComparison.OrdinalIgnoreCase))
                    {
                        GetClipboardContentForHtml(sbContent, out MemoryStream utf8Stream);
                        dataObject.SetData(format, autoConvert: false, utf8Stream);
                    }
                    else
                    {
                        dataObject.SetData(format, autoConvert: false, sbContent.ToString());
                    }
                }

                break;
        }

        return dataObject;
    }

    private static void GetClipboardContentForHtml(StringBuilder sbContent, out MemoryStream utf8Stream)
    {
        byte[] sourceBytes = Encoding.Unicode.GetBytes(sbContent.ToString());
        byte[] destinationBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, sourceBytes);

        int bytecountEndOfFragment = 135 + destinationBytes.Length;
        int bytecountEndOfHtml = bytecountEndOfFragment + 36;
        string prefix = $"""
            Version:1.0
            StartHTML:00000097
            EndHTML:{bytecountEndOfHtml:00000000}
            StartFragment:00000133
            EndFragment:{bytecountEndOfFragment:00000000}
            <HTML>
            <BODY>
            <!--StartFragment-->

            """;
        sbContent.Insert(0, prefix);
        sbContent.Append("""

                <!--EndFragment-->
                </BODY>
                </HTML>
                """);

        sourceBytes = Encoding.Unicode.GetBytes(sbContent.ToString());
        destinationBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, sourceBytes);

        utf8Stream = new MemoryStream(bytecountEndOfHtml + 1);
        utf8Stream.Write(destinationBytes, 0, bytecountEndOfHtml);
        utf8Stream.WriteByte(0);

        Debug.Assert(destinationBytes[97] == '<');
        Debug.Assert(destinationBytes[bytecountEndOfHtml - 1] == '>');
        Debug.Assert(destinationBytes[133] == '<');
        Debug.Assert(destinationBytes[bytecountEndOfFragment] == '<');
    }

    // Rectangle returned includes the potential column header
    public Rectangle GetColumnDisplayRectangle(int columnIndex, bool cutOverflow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(columnIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);

        return GetColumnDisplayRectanglePrivate(columnIndex, cutOverflow);
    }

    private Rectangle GetColumnDisplayRectanglePrivate(int columnIndex, bool cutOverflow)
    {
        Debug.Assert(columnIndex >= 0 && columnIndex < Columns.Count);

        if (!Columns[columnIndex].Displayed)
        {
            return Rectangle.Empty;
        }

        Rectangle data = _layout.Data;
        int cx;
        bool columnFound = false;
        DataGridViewColumn? dataGridViewColumn;
        cx = RightToLeftInternal ? data.Right : data.X;

        for (dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            dataGridViewColumn is not null && !columnFound;)
        {
            if ((RightToLeftInternal && cx < data.X) || (!RightToLeftInternal && cx > data.Right))
            {
                break;
            }

            if (dataGridViewColumn.Index == columnIndex)
            {
                columnFound = true;
            }
            else
            {
                if (RightToLeftInternal)
                {
                    cx -= dataGridViewColumn.Thickness;
                }
                else
                {
                    cx += dataGridViewColumn.Thickness;
                }

                dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                    DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen,
                    DataGridViewElementStates.None);
            }
        }

        if (!columnFound && DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0)
        {
            for (dataGridViewColumn = Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol];
                dataGridViewColumn is not null && !columnFound;)
            {
                if ((RightToLeftInternal && cx < data.X) || (!RightToLeftInternal && cx > data.Right))
                {
                    break;
                }

                if (dataGridViewColumn.Index == columnIndex)
                {
                    columnFound = true;
                }
                else
                {
                    if (RightToLeftInternal)
                    {
                        cx -= dataGridViewColumn.Thickness;
                    }
                    else
                    {
                        cx += dataGridViewColumn.Thickness;
                    }

                    if (dataGridViewColumn.Index == DisplayedBandsInfo.FirstDisplayedScrollingCol)
                    {
                        if (RightToLeftInternal)
                        {
                            cx += FirstDisplayedScrollingColumnHiddenWidth;
                        }
                        else
                        {
                            cx -= FirstDisplayedScrollingColumnHiddenWidth;
                        }
                    }

                    dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);
                }
            }
        }

        if (columnFound)
        {
            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
            int displayWidth, viewedColumnWidth = dataGridViewColumn.Thickness;
            if (dataGridViewColumn.Index == DisplayedBandsInfo.FirstDisplayedScrollingCol)
            {
                viewedColumnWidth -= FirstDisplayedScrollingColumnHiddenWidth;
            }

            displayWidth = cutOverflow
                && ((!RightToLeftInternal && cx + viewedColumnWidth > data.Right)
                    || (RightToLeftInternal && cx - viewedColumnWidth < data.X))
                    ? RightToLeftInternal ? cx - data.X : data.Right - cx
                    : viewedColumnWidth;

            Rectangle columnRect = RightToLeftInternal
                ? new Rectangle(cx - displayWidth, data.Y, displayWidth, data.Height)
                : new Rectangle(cx, data.Y, displayWidth, data.Height);

            if (_layout.ColumnHeadersVisible)
            {
                columnRect.Height += _layout.ColumnHeaders.Height;
                columnRect.Y -= _layout.ColumnHeaders.Height;
            }

            return columnRect;
        }

        return Rectangle.Empty;
    }

    // xColumnLeftEdge returns the left edge of the column when RightToLeft is false.
    // xColumnLeftEdge returns the right edge of the column when RightToLeft is true.
    private int GetColumnIndexFromX(int x, out int xColumnLeftEdge)
    {
        Rectangle data = _layout.Data;
        Debug.Assert(RightToLeftInternal || (x >= data.X - 1 && x < data.Right), "x must be inside the horizontal bounds of this.layout.Data");
        Debug.Assert(!RightToLeftInternal || (x >= data.X && x <= data.Right), "x must be inside the horizontal bounds of this.layout.Data");

        if (!RightToLeftInternal && x == data.X - 1)
        {
            x++;
        }
        else if (RightToLeftInternal && x == data.Right)
        {
            x--;
        }

        int cx = RightToLeftInternal ? data.Right - 1 : data.X;

        // first try to match x against a frozen column
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        while (dataGridViewColumn is not null
            && ((!RightToLeftInternal && cx < data.Right) || (RightToLeftInternal && cx >= data.X)))
        {
            if (RightToLeftInternal)
            {
                cx -= dataGridViewColumn.Thickness;
            }
            else
            {
                cx += dataGridViewColumn.Thickness;
            }

            if (!RightToLeftInternal && cx > x)
            {
                xColumnLeftEdge = cx - dataGridViewColumn.Thickness;
                return dataGridViewColumn.Index;
            }
            else if (RightToLeftInternal && cx < x)
            {
                xColumnLeftEdge = cx + dataGridViewColumn.Thickness;
                return dataGridViewColumn.Index;
            }

            dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen,
                DataGridViewElementStates.None);
        }

        if (RightToLeftInternal)
        {
            cx += FirstDisplayedScrollingColumnHiddenWidth;
        }
        else
        {
            cx -= FirstDisplayedScrollingColumnHiddenWidth;
        }

        // second try to match x against a scrolling column
        if (DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0)
        {
            dataGridViewColumn = Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol];
            while (dataGridViewColumn is not null
                && ((!RightToLeftInternal && cx < data.Right) || (RightToLeftInternal && cx >= data.X)))
            {
                Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen);
                if (RightToLeftInternal)
                {
                    cx -= dataGridViewColumn.Thickness;
                }
                else
                {
                    cx += dataGridViewColumn.Thickness;
                }

                if (!RightToLeftInternal && cx > x)
                {
                    xColumnLeftEdge = cx - dataGridViewColumn.Thickness;
                    return dataGridViewColumn.Index;
                }
                else if (RightToLeftInternal && cx < x)
                {
                    xColumnLeftEdge = cx + dataGridViewColumn.Thickness;
                    return dataGridViewColumn.Index;
                }

                dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.None);
            }
        }

        xColumnLeftEdge = -1;
        return -1;
    }

    private static int GetColumnScrollRate(int xOffset)
    {
        Debug.Assert(xOffset > 0);
        // Counting 20ms for executing the actual column scrolling
        if (xOffset <= 10)
        {
            return 480;     // Two columns per second
        }

        if (xOffset <= 15)
        {
            return 313;     // Three columns per second
        }

        if (xOffset <= 25)
        {
            return 180;     // Five columns per second
        }

        if (xOffset <= 35)
        {
            return 123;     // Seven columns per second
        }

        return Math.Max(1, 4000 / xOffset);
    }

    /// <summary>
    ///  Returns the coordinate of the left edge of the given column. Note that
    ///  the column does not need to be completely visible on the display area.
    ///  Value returned is not necessarily within layout.Data because of the
    ///  this.negOffset value, or because the column may start to the right of
    ///  data area, or behind the frozen area, or completely on the left of the control.
    ///  The right edge is returned in RightToLeft mode.
    /// </summary>
    internal int GetColumnXFromIndex(int index)
    {
        Debug.Assert(index < Columns.Count);
        Debug.Assert(Columns[index].Visible);

        int x = RightToLeftInternal ? _layout.Data.Right - 1 : _layout.Data.X;

        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        while (dataGridViewColumn is not null)
        {
            if (index == dataGridViewColumn.Index)
            {
                return x;
            }

            if (RightToLeftInternal)
            {
                x -= dataGridViewColumn.Thickness;
            }
            else
            {
                x += dataGridViewColumn.Thickness;
            }

            dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen,
                DataGridViewElementStates.None);
        }

        if (RightToLeftInternal)
        {
            x += FirstDisplayedScrollingColumnHiddenWidth;
        }
        else
        {
            x -= FirstDisplayedScrollingColumnHiddenWidth;
        }

        int xFirstVisibleScrollingCol = x;

        dataGridViewColumn = DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0
            ? Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol]
            : Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);

        Debug.Assert(dataGridViewColumn!.Visible && !dataGridViewColumn.Frozen);

        while (dataGridViewColumn is not null)
        {
            if (index == dataGridViewColumn.Index)
            {
                return x;
            }

            if (RightToLeftInternal)
            {
                x -= dataGridViewColumn.Thickness;
            }
            else
            {
                x += dataGridViewColumn.Thickness;
            }

            dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
        }

        // The column is completely hidden on the left/right of the dataGridView
        x = xFirstVisibleScrollingCol;
        dataGridViewColumn = Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol];
        dataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn,
            DataGridViewElementStates.Visible,
            DataGridViewElementStates.Frozen);
        while (dataGridViewColumn is not null)
        {
            if (RightToLeftInternal)
            {
                x += dataGridViewColumn.Thickness;
            }
            else
            {
                x -= dataGridViewColumn.Thickness;
            }

            if (index == dataGridViewColumn.Index)
            {
                return x;
            }

            dataGridViewColumn = Columns.GetPreviousColumn(dataGridViewColumn,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.Frozen);
        }

        Debug.Fail("Could not find column in GetColumnXFromIndex");
        return 0;
    }

    private int GetNegOffsetFromHorizontalOffset(int horizontalOffset)
    {
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
        while (dataGridViewColumn is not null && dataGridViewColumn.Thickness <= horizontalOffset)
        {
            horizontalOffset -= dataGridViewColumn.Thickness;
            dataGridViewColumn = Columns.GetNextColumn(dataGridViewColumn, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
        }

        return horizontalOffset;
    }

    private bool GetOutOfBoundCorrectedHitTestInfo(ref HitTestInfo hti, ref int mouseX, ref int mouseY, out int xOffset, out int yOffset)
    {
        xOffset = yOffset = 0;
        Rectangle rectScrollingArea = _layout.Data;
        int visibleRowsHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible);
        int frozenVisibleRowsHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        int fittingTrailingScrollingRowsHeight = ComputeHeightOfFittingTrailingScrollingRows(frozenVisibleRowsHeight);
        int trailingScrollingRowsHeight = ComputeHeightOfTrailingScrollingRows();
        int emptyBackgroundWidth = Math.Max(0, _layout.Data.Width - Columns.GetColumnsWidth(DataGridViewElementStates.Visible));
        int emptyBackgroundHeight = Math.Max(0, _layout.Data.Height - frozenVisibleRowsHeight - trailingScrollingRowsHeight);

        Debug.Assert(!_vertScrollBar.Enabled
            || !_vertScrollBar.Visible
            || _vertScrollBar.Maximum == visibleRowsHeight - frozenVisibleRowsHeight);

        if (_dataGridViewOper[OperationTrackRowSelect])
        {
            if (_layout.RowHeadersVisible)
            {
                // Include row headers
                rectScrollingArea = Rectangle.Union(rectScrollingArea, _layout.RowHeaders);
            }

            // Discard frozen rows
            DiscardZonesInScrollingArea(
                ref rectScrollingArea,
                emptyBackgroundWidth,
                emptyBackgroundHeight,
                frozenVisibleRowsHeight,
                discardFrozenColumns: false,
                discardFrozenRows: true);

            if (mouseY >= rectScrollingArea.Top && mouseY <= rectScrollingArea.Bottom)
            {
                // Mouse's Y is in-bound -- correct X value
                hti = HitTest(RightToLeftInternal ? rectScrollingArea.Right - 1 : rectScrollingArea.Left, mouseY);
                if (_ptAnchorCell.Y != -1
                    && (Rows.GetRowState(_ptAnchorCell.Y) & DataGridViewElementStates.Frozen) != 0
                    && _trackRowEdge != -1
                    && (Rows.GetRowState(_trackRowEdge) & DataGridViewElementStates.Frozen) != 0
                    && hti._row >= 0
                    && (Rows.GetRowState(hti._row) & DataGridViewElementStates.Frozen) == 0)
                {
                    // Anchor cell is in frozen row and target cell is in unfrozen row. Make sure no row is scrolled off.
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
                    int firstUnfrozenRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                    int firstColumnIndex;
                    if (hti._col >= 0)
                    {
                        firstColumnIndex = hti._col;
                    }
                    else
                    {
                        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                        firstColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
                    }

                    if (firstColumnIndex >= 0 && firstUnfrozenRowIndex >= 0)
                    {
                        if (!ScrollIntoView(firstColumnIndex, firstUnfrozenRowIndex, forCurrentCellChange: false))
                        {
                            return false;
                        }

                        hti = HitTest(RightToLeftInternal ? rectScrollingArea.Right : rectScrollingArea.Left, mouseY);
                    }
                }

                return true;
            }

            // Mouse's Y is outside of scrolling bands
            if (mouseY < rectScrollingArea.Top)
            {
                if (_ptAnchorCell.Y != -1
                    && ((Rows.GetRowState(_ptAnchorCell.Y) & DataGridViewElementStates.Frozen) == 0
                        || (_trackRowEdge != -1 && (Rows.GetRowState(_trackRowEdge) & DataGridViewElementStates.Frozen) == 0))
                    && VerticalScrollingOffset != 0)
                {
                    // Up scrolling is required because the anchor's row is unfrozen
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
                    yOffset = mouseY - rectScrollingArea.Top;   // yOffset strictly negative
                    mouseX = RightToLeftInternal ? rectScrollingArea.Right - 1 : rectScrollingArea.Left + 1;
                }
                else
                {
                    hti = HitTest(RightToLeftInternal ? rectScrollingArea.Right : rectScrollingArea.Left, mouseY);
                }
            }
            else
            {
                Debug.Assert(mouseY > rectScrollingArea.Bottom);
                if (DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0)
                {
                    if (VerticalScrollingOffset + Rows.SharedRow(DisplayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(DisplayedBandsInfo.FirstDisplayedScrollingRow) <=
                        visibleRowsHeight - frozenVisibleRowsHeight - fittingTrailingScrollingRowsHeight)
                    {
                        // Down scrolling is required
                        yOffset = mouseY - rectScrollingArea.Bottom;   // yOffset strictly positive
                        mouseX = RightToLeftInternal ? rectScrollingArea.Right - 1 : rectScrollingArea.Left + 1;
                    }
                }
            }

            return true;
        }

        if (_dataGridViewOper[OperationTrackColSelect])
        {
            if (_layout.ColumnHeadersVisible)
            {
                // Include column headers
                rectScrollingArea = Rectangle.Union(rectScrollingArea, _layout.ColumnHeaders);
            }

            // Discard frozen columns
            DiscardZonesInScrollingArea(
                ref rectScrollingArea,
                emptyBackgroundWidth,
                emptyBackgroundHeight,
                frozenVisibleRowsHeight,
                discardFrozenColumns: true,
                discardFrozenRows: false);

            if (mouseX >= rectScrollingArea.Left && mouseX <= rectScrollingArea.Right)
            {
                // Mouse's X is in-bound -- correct Y value
                hti = HitTest(mouseX, rectScrollingArea.Top);
                if (_ptAnchorCell.X != -1
                    && Columns[_ptAnchorCell.X].Frozen
                    && _trackColumnEdge != -1
                    && Columns[_trackColumnEdge].Frozen
                    && hti._col >= 0
                    && !Columns[hti._col].Frozen)
                {
                    // Anchor cell is in frozen column and target cell is in unfrozen column. Make sure no column is scrolled off.
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0);
                    int firstUnfrozenColumnIndex = Columns.GetFirstColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen)!.Index;
                    int firstRowIndex = hti._row >= 0 ? hti._row : Rows.GetFirstRow(DataGridViewElementStates.Visible);

                    if (firstRowIndex >= 0 && firstUnfrozenColumnIndex >= 0)
                    {
                        if (!ScrollIntoView(firstUnfrozenColumnIndex, firstRowIndex, forCurrentCellChange: false))
                        {
                            return false;
                        }

                        hti = HitTest(mouseX, rectScrollingArea.Top);
                    }
                }

                return true;
            }

            // Mouse's X is outside of scrolling bands
            if ((!RightToLeftInternal && mouseX < rectScrollingArea.Left) || (RightToLeftInternal && mouseX > rectScrollingArea.Right))
            {
                if (_ptAnchorCell.X != -1
                    && (!Columns[_ptAnchorCell.X].Frozen || (_trackColumnEdge != -1 && !Columns[_trackColumnEdge].Frozen))
                    && DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0
                    && (FirstDisplayedScrollingColumnHiddenWidth > 0
                        || Columns.GetPreviousColumn(
                            Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol],
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.Frozen) is not null))
                {
                    // xOffset strictly negative
                    if (RightToLeftInternal)
                    {
                        // Right scrolling is required
                        xOffset = rectScrollingArea.Right - mouseX;
                    }
                    else
                    {
                        // Left scrolling is required
                        xOffset = mouseX - rectScrollingArea.Left;
                    }

                    mouseY = rectScrollingArea.Top + 1;
                }
                else
                {
                    hti = HitTest(mouseX, rectScrollingArea.Top);
                }
            }
            else
            {
                Debug.Assert((!RightToLeftInternal && mouseX > rectScrollingArea.Right) || (RightToLeftInternal && mouseX < rectScrollingArea.Left));
                if (DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0)
                {
                    if (DisplayedBandsInfo.LastTotallyDisplayedScrollingCol != -1
                        && Columns.GetNextColumn(
                            Columns[DisplayedBandsInfo.LastTotallyDisplayedScrollingCol],
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.None) is null)
                    {
                        // No more columns to scroll
                        return true;
                    }

                    DataGridViewColumn? newFirstVisibleScrollingCol = Columns.GetNextColumn(
                        Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol],
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);

                    int newColOffset = 0;
                    for (DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.Frozen);
                        dataGridViewColumn != newFirstVisibleScrollingCol;
                        dataGridViewColumn = Columns.GetNextColumn(
                            dataGridViewColumn,
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.None))
                    {
                        newColOffset += dataGridViewColumn!.Thickness;
                    }

                    if (HorizontalOffset != newColOffset)
                    {
                        // xOffset strictly positive
                        if (RightToLeftInternal)
                        {
                            // Left scrolling is required
                            xOffset = rectScrollingArea.Left - mouseX;
                        }
                        else
                        {
                            // Right scrolling is required
                            xOffset = mouseX - rectScrollingArea.Right;
                        }

                        mouseY = rectScrollingArea.Top + 1;
                    }
                }
            }

            return true;
        }

        if (_dataGridViewOper[OperationTrackCellSelect])
        {
            bool recomputeHitTestInfo = false;

            // Discard frozen columns and rows
            DiscardZonesInScrollingArea(
                ref rectScrollingArea,
                emptyBackgroundWidth,
                emptyBackgroundHeight,
                frozenVisibleRowsHeight,
                discardFrozenColumns: true,
                discardFrozenRows: true);

            if (mouseY < rectScrollingArea.Top)
            {
                // Mouse's Y is above scrolling bands
                if (((_ptAnchorCell.Y != -1 && (Rows.GetRowState(_ptAnchorCell.Y) & DataGridViewElementStates.Frozen) == 0)
                    || (_ptCurrentCell.Y != -1 && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0))
                    && VerticalScrollingOffset != 0)
                {
                    // Up scrolling is required - the anchor's row is unfrozen
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
                    yOffset = mouseY - rectScrollingArea.Top;   // yOffset strictly negative
                }
                else
                {
                    // Correct mouse's Y - no scrolling can be performed
                    if (mouseY < _layout.Data.Top)
                    {
                        mouseY = _layout.Data.Top + 1;
                        recomputeHitTestInfo = true;
                    }
                }
            }
            else if (mouseY > rectScrollingArea.Bottom)
            {
                // Mouse's Y is below scrolling bands
                if (DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0)
                {
                    if (VerticalScrollingOffset + Rows.SharedRow(DisplayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(DisplayedBandsInfo.FirstDisplayedScrollingRow) <=
                        visibleRowsHeight - frozenVisibleRowsHeight - fittingTrailingScrollingRowsHeight)
                    {
                        // Down scrolling is required
                        yOffset = mouseY - rectScrollingArea.Bottom;   // yOffset strictly positive
                    }
                    else
                    {
                        // Correct mouse's Y - no scrolling can be performed
                        mouseY = rectScrollingArea.Bottom - 1;
                        recomputeHitTestInfo = true;
                    }
                }
                else
                {
                    // Correct mouse's Y - no scrolling can be performed
                    mouseY = rectScrollingArea.Bottom - 1;
                    recomputeHitTestInfo = true;
                }
            }
#if DEBUG
            else
            {
                // Mouse's Y is in-bound
                Debug.Assert(mouseY >= rectScrollingArea.Top && mouseY <= rectScrollingArea.Bottom);
            }
#endif
            if ((!RightToLeftInternal && mouseX < rectScrollingArea.Left) || (RightToLeftInternal && mouseX > rectScrollingArea.Right))
            {
                // Mouse's X is on the left of scrolling bands (LTR)
                if (((_ptAnchorCell.X != -1 && !Columns[_ptAnchorCell.X].Frozen) || (_ptCurrentCell.X != -1 && !Columns[_ptCurrentCell.X].Frozen))
                    && DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0
                    && (FirstDisplayedScrollingColumnHiddenWidth > 0
                        || Columns.GetPreviousColumn(
                            Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol],
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.Frozen) is not null))
                {
                    // xOffset strictly negative
                    if (RightToLeftInternal)
                    {
                        // Right scrolling is required - anchor's column is unfrozen
                        xOffset = rectScrollingArea.Right - mouseX;
                    }
                    else
                    {
                        // Left scrolling is required - anchor's column is unfrozen
                        xOffset = mouseX - rectScrollingArea.Left;
                    }
                }
                else
                {
                    // Correct mouse's X - no scrolling can be performed
                    if (!RightToLeftInternal && mouseX < _layout.Data.Left)
                    {
                        mouseX = _layout.Data.Left + 1;
                        recomputeHitTestInfo = true;
                    }
                    else if (RightToLeftInternal && mouseX > _layout.Data.Right)
                    {
                        mouseX = _layout.Data.Right - 1;
                        recomputeHitTestInfo = true;
                    }
                }
            }
            else if ((!RightToLeftInternal && mouseX > rectScrollingArea.Right) || (RightToLeftInternal && mouseX < rectScrollingArea.Left))
            {
                // Mouse's X is on the right of scrolling bands (LTR)
                if (DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0
                    && (DisplayedBandsInfo.LastTotallyDisplayedScrollingCol == -1
                        || Columns.GetNextColumn(
                            Columns[DisplayedBandsInfo.LastTotallyDisplayedScrollingCol],
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.None) is not null))
                {
                    DataGridViewColumn? newFirstVisibleScrollingCol = Columns.GetNextColumn(
                        Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol],
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);
                    int newColOffset = 0;
                    for (DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.Frozen);
                        dataGridViewColumn != newFirstVisibleScrollingCol;
                        dataGridViewColumn = Columns.GetNextColumn(
                            dataGridViewColumn,
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.None))
                    {
                        newColOffset += dataGridViewColumn!.Thickness;
                    }

                    if (HorizontalOffset != newColOffset)
                    {
                        // xOffset strictly positive
                        if (RightToLeftInternal)
                        {
                            // Left scrolling is required
                            xOffset = rectScrollingArea.Left - mouseX;
                        }
                        else
                        {
                            // Right scrolling is required
                            xOffset = mouseX - rectScrollingArea.Right;
                        }
                    }
                    else
                    {
                        // Correct mouse's X - no scrolling can be performed
                        mouseX = RightToLeftInternal ? rectScrollingArea.Left + 1 : rectScrollingArea.Right - 1;

                        recomputeHitTestInfo = true;
                    }
                }
                else
                {
                    // Correct mouse's X - no scrolling can be performed
                    mouseX = RightToLeftInternal ? rectScrollingArea.Left + 1 : rectScrollingArea.Right - 1;
                    recomputeHitTestInfo = true;
                }
            }
#if DEBUG
            else
            {
                // Mouse's X is in-bound
                Debug.Assert(mouseX >= rectScrollingArea.Left && mouseX <= rectScrollingArea.Right);
            }
#endif
            if (recomputeHitTestInfo)
            {
                hti = HitTest(mouseX, mouseY);
            }
        }

        return true;
    }

    internal override Size GetPreferredSizeCore(Size proposedConstraints)
    {
        int bordersAndPaddingWidth = 2 * (BorderWidth + Padding.Size.Width);
        int bordersAndPaddingHeight = 2 * (BorderWidth + Padding.Size.Height);

        bool allowHorizScrollbar = _scrollBars is ScrollBars.Both or ScrollBars.Horizontal;
        bool allowVertScrollbar = _scrollBars is ScrollBars.Both or ScrollBars.Vertical;

        int minimumWidth = 16 + bordersAndPaddingWidth;
        if (allowVertScrollbar)
        {
            minimumWidth += _vertScrollBar.Width;
        }

        if (RowHeadersVisible)
        {
            minimumWidth += RowHeadersWidth;
        }

        int preferredWidth = Math.Min(minimumWidth + Columns.GetColumnsWidth(DataGridViewElementStates.Visible), proposedConstraints.Width);
        if (preferredWidth < minimumWidth)
        {
            preferredWidth = minimumWidth;
        }

        int minimumHeight = 16 + bordersAndPaddingHeight;
        if (allowHorizScrollbar)
        {
            minimumHeight += _horizScrollBar.Height;
        }

        if (ColumnHeadersVisible)
        {
            minimumHeight += ColumnHeadersHeight;
        }

        int preferredHeight = Math.Min(minimumHeight + Rows.GetRowsHeight(DataGridViewElementStates.Visible), proposedConstraints.Height);
        if (preferredHeight < minimumHeight)
        {
            preferredHeight = minimumHeight;
        }

        return new Size(preferredWidth, preferredHeight);
    }

    // Rectangle returned includes the potential row header
    public Rectangle GetRowDisplayRectangle(int rowIndex, bool cutOverflow)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rowIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        return GetRowDisplayRectanglePrivate(rowIndex, cutOverflow);
    }

    private Rectangle GetRowDisplayRectanglePrivate(int rowIndex, bool cutOverflow)
    {
        Debug.Assert(rowIndex >= 0 && rowIndex < Rows.Count);

        if ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) == 0)
        {
            return Rectangle.Empty;
        }

        Rectangle data = _layout.Data;
        int cy = data.Y;
        bool rowFound = false;
        int indexTmp;
        for (indexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            indexTmp != -1 && !rowFound;)
        {
            if (cy > data.Bottom)
            {
                break;
            }

            if (indexTmp == rowIndex)
            {
                rowFound = true;
            }
            else
            {
                cy += Rows.SharedRow(indexTmp).GetHeight(indexTmp);
                indexTmp = Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            }
        }

        if (!rowFound && DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0)
        {
            for (indexTmp = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                indexTmp != -1 && !rowFound;)
            {
                if (cy > data.Bottom)
                {
                    break;
                }

                if (indexTmp == rowIndex)
                {
                    rowFound = true;
                }
                else
                {
                    cy += Rows.SharedRow(indexTmp).GetHeight(indexTmp);
                    indexTmp = Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible);
                }
            }
        }

        if (rowFound)
        {
            int displayHeight = cutOverflow && cy + Rows.SharedRow(indexTmp).GetHeight(indexTmp) > data.Bottom
                ? data.Bottom - cy
                : Rows.SharedRow(indexTmp).GetHeight(indexTmp);

            Rectangle rowRect = new(data.X, cy, data.Width, displayHeight);

            if (_layout.RowHeadersVisible)
            {
                rowRect.Width += _layout.RowHeaders.Width;
                if (!RightToLeftInternal)
                {
                    rowRect.X -= _layout.RowHeaders.Width;
                }
            }

            return rowRect;
        }

        return Rectangle.Empty;
    }

    private int GetRowIndexFromY(int y, out int yRowTopEdge)
    {
        Rectangle data = _layout.Data;
        Debug.Assert(y >= data.Y - 1 && y < data.Bottom, "y must be inside the vertical bounds of the data area.");

        if (y == data.Y - 1)
        {
            y++;
        }

        int rowHeight;
        int cy = data.Y;

        // first try to match y against a frozen rows
        int indexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        while (indexTmp != -1 && cy < data.Bottom)
        {
            rowHeight = Rows.SharedRow(indexTmp).GetHeight(indexTmp);
            cy += rowHeight;
            if (cy > y)
            {
                yRowTopEdge = cy - rowHeight;
                return indexTmp;
            }

            indexTmp = Rows.GetNextRow(indexTmp,
                DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        }

        // second try to match y against a scrolling row
        if (DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0)
        {
            indexTmp = DisplayedBandsInfo.FirstDisplayedScrollingRow;
            Debug.Assert((Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0
                && (Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0);

            while (indexTmp != -1 && cy < data.Bottom)
            {
                rowHeight = Rows.SharedRow(indexTmp).GetHeight(indexTmp);
                cy += rowHeight;
                if (cy > y)
                {
                    yRowTopEdge = cy - rowHeight;
                    return indexTmp;
                }

                indexTmp = Rows.GetNextRow(indexTmp,
                    DataGridViewElementStates.Visible);
            }
        }

        yRowTopEdge = -1;
        return -1;
    }

    private static int GetRowScrollRate(int yOffset)
    {
        Debug.Assert(yOffset > 0);
        // Counting 10ms for executing the actual row scrolling
        if (yOffset <= 10)
        {
            return 90;      // Ten rows per second
        }

        if (yOffset <= 15)
        {
            return 57;     // Fifteen rows per second
        }

        if (yOffset <= 25)
        {
            return 30;     // Twenty-five rows per second
        }

        if (yOffset <= 35)
        {
            return 18;     // Thirty-five rows per second
        }

        return Math.Max(1, 600 / yOffset);
    }

    /// <summary>
    ///  Returns the coordinate of the upper edge of the given row. Note that
    ///  the row does not need to be completely visible on the display area.
    ///  Value returned is not necessarily within layout.Data because the row
    ///  may start below the data area.
    /// </summary>
    internal int GetRowYFromIndex(int index)
    {
        Debug.Assert(index >= 0 && index < Rows.Count);
        Debug.Assert((Rows.GetRowState(index) & DataGridViewElementStates.Visible) != 0);

        int y = _layout.Data.Y;

        int indexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        while (indexTmp != -1)
        {
            if (index == indexTmp)
            {
                return y;
            }

            y += Rows.SharedRow(indexTmp).GetHeight(indexTmp);
            indexTmp = Rows.GetNextRow(indexTmp,
                DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        }

        int yFirstVisibleScrollingRow = y;

        if (DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0)
        {
            if (index >= DisplayedBandsInfo.FirstDisplayedScrollingRow)
            {
                // index is part of the scrolling rows below the frozen rows
                indexTmp = DisplayedBandsInfo.FirstDisplayedScrollingRow;
            }
            else
            {
                // index is part of the rows hidden behind the frozen rows or on top of the control
                indexTmp = -1;
            }
        }
        else
        {
            // frozen rows cover all the rows real-estate. Look for index starting at the first visible non-frozen row.
            indexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
            Debug.Assert(indexTmp != -1);
        }

        if (indexTmp != -1)
        {
            Debug.Assert((Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0
                && (Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0);

            while (indexTmp != -1)
            {
                if (index == indexTmp)
                {
                    return y;
                }

                y += Rows.SharedRow(indexTmp).GetHeight(indexTmp);
                indexTmp = Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible);
            }
        }

        // Row is completely hidden behind frozen rows or on top of control
        y = yFirstVisibleScrollingRow;
        Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow != -1);
        indexTmp = Rows.GetPreviousRow(DisplayedBandsInfo.FirstDisplayedScrollingRow,
            DataGridViewElementStates.Visible,
            DataGridViewElementStates.Frozen);
        while (indexTmp != -1)
        {
            y -= Rows.SharedRow(indexTmp).GetHeight(indexTmp);
            if (index == indexTmp)
            {
                return y;
            }

            indexTmp = Rows.GetPreviousRow(indexTmp,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.Frozen);
        }

        Debug.Fail("Could not find row in GetRowYFromIndex");
        return 0;
    }

    private bool GetTabKeyEffective(bool shift, bool ctrl) => StandardTab
        ? ctrl && !((!shift && (!VisibleCellExists || CurrentCellIsLastVisibleCell))
            || (shift && (!VisibleCellExists || CurrentCellIsFirstVisibleCell)))
        : !ctrl && !((!shift && (!VisibleCellExists || CurrentCellIsLastVisibleCell))
            || (shift && (!VisibleCellExists || CurrentCellIsFirstVisibleCell)));

    public HitTestInfo HitTest(int x, int y)
    {
        HitTestInfo hti = new();

        if (!_layout.Inside.Contains(x, y))
        {
            return hti;
        }

        if (_horizScrollBar is not null && _horizScrollBar.Visible && _horizScrollBar.Bounds.Contains(x, y))
        {
            hti._type = DataGridViewHitTestType.HorizontalScrollBar;
            return hti;
        }

        if (_vertScrollBar is not null && _vertScrollBar.Visible && _vertScrollBar.Bounds.Contains(x, y))
        {
            hti._type = DataGridViewHitTestType.VerticalScrollBar;
            return hti;
        }

        if (_layout.TopLeftHeader.Contains(x, y))
        {
            hti._type = DataGridViewHitTestType.TopLeftHeader;
            hti._typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeader;
            hti._colStart = RightToLeftInternal ? _layout.TopLeftHeader.Right - 1 : _layout.TopLeftHeader.Left;

            hti._rowStart = _layout.TopLeftHeader.Top;
            if ((!RightToLeftInternal && _layout.TopLeftHeader.Right - x < ColumnSizingHotZone)
                || (RightToLeftInternal && x - _layout.TopLeftHeader.Left < ColumnSizingHotZone))
            {
                // hti.edge = DataGridViewHitTestTypeCloseEdge.Right;
                if (RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing)
                {
                    hti._typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft;
                    hti._mouseBarOffset = RightToLeftInternal
                        ? _layout.TopLeftHeader.Left - x - 1
                        : _layout.TopLeftHeader.Right - x - 1;
                }
            }
            else if (_layout.TopLeftHeader.Top + _layout.TopLeftHeader.Height - y < RowSizingHotZone)
            {
                // hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom;
                if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing)
                {
                    hti._typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop;
                    hti._mouseBarOffset = _layout.TopLeftHeader.Top + _layout.TopLeftHeader.Height - y - 1;
                }
            }

            return hti;
        }

        // check for column resize / insertion
        if (_layout.ColumnHeaders.Contains(x, y))
        {
            // this is actually the right edge in RTL mode
            hti._col = GetColumnIndexFromX(x, out int xColumnLeftEdge);
            if (hti._col < 0)
            {
                return HitTestInfo.Nowhere;
            }

            Debug.Assert(xColumnLeftEdge == GetColumnXFromIndex(hti._col));
            hti._type = DataGridViewHitTestType.ColumnHeader;
            hti._typeInternal = DataGridViewHitTestTypeInternal.ColumnHeader;
            hti._rowStart = _layout.ColumnHeaders.Top;
            hti._colStart = xColumnLeftEdge;
            int columnWidth = Columns[hti._col].Thickness;
            if ((!RightToLeftInternal && xColumnLeftEdge + columnWidth - x < ColumnSizingHotZone)
                || (RightToLeftInternal && x - xColumnLeftEdge + columnWidth < ColumnSizingHotZone))
            {
                // hti.edge = DataGridViewHitTestTypeCloseEdge.Right;
                hti._mouseBarOffset = RightToLeftInternal
                    ? xColumnLeftEdge - columnWidth - x + 1
                    : xColumnLeftEdge + columnWidth - x - 1;

                DataGridViewColumn dataGridViewColumn = Columns[hti._col];
                hti._typeInternal = dataGridViewColumn.Resizable == DataGridViewTriState.True
                    && (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None
                        || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                        ? DataGridViewHitTestTypeInternal.ColumnResizeRight
                        : DataGridViewHitTestTypeInternal.ColumnHeaderRight;
            }
            else if ((!RightToLeftInternal && x - xColumnLeftEdge < ColumnSizingHotZone)
                || (RightToLeftInternal && xColumnLeftEdge - x < ColumnSizingHotZone))
            {
                // hti.edge = DataGridViewHitTestTypeCloseEdge.Left;
                DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(
                    Columns[hti._col],
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.None);

                if (dataGridViewColumn is not null)
                {
                    hti._adjacentCol = dataGridViewColumn.Index;
                    hti._mouseBarOffset = RightToLeftInternal ? xColumnLeftEdge - x + 1 : xColumnLeftEdge - x - 1;

                    hti._typeInternal = dataGridViewColumn.Resizable == DataGridViewTriState.True
                        && (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None
                            || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                            ? DataGridViewHitTestTypeInternal.ColumnResizeLeft
                            : DataGridViewHitTestTypeInternal.ColumnHeaderLeft;
                }
                else
                {
                    if (RowHeadersVisible && RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing)
                    {
                        hti._typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight;
                        hti._mouseBarOffset = RightToLeftInternal ? xColumnLeftEdge - x : xColumnLeftEdge - x - 1;
                    }
                    else
                    {
                        hti._typeInternal = DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft;
                    }
                }
            }
            else if (_layout.ColumnHeaders.Bottom - y < RowSizingHotZone)
            {
                // hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom;
                if (/*!this.RowHeadersVisible &&*/ ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing)
                {
                    hti._typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom;
                    hti._mouseBarOffset = _layout.ColumnHeaders.Bottom - y - 1;
                }
            }
        }

        // check for row resize
        if (_layout.RowHeaders.Contains(x, y))
        {
            hti._row = GetRowIndexFromY(y, out int yRowTopEdge);
            if (hti._row < 0)
            {
                return HitTestInfo.Nowhere;
            }

            Debug.Assert(yRowTopEdge == GetRowYFromIndex(hti._row));
            hti._type = DataGridViewHitTestType.RowHeader;
            hti._typeInternal = DataGridViewHitTestTypeInternal.RowHeader;
            hti._rowStart = yRowTopEdge;
            hti._colStart = RightToLeftInternal ? _layout.RowHeaders.Right - 1 : _layout.RowHeaders.Left;

            int rowHeight = Rows.SharedRow(hti._row).GetHeight(hti._row);
            if (yRowTopEdge + rowHeight - y < RowSizingHotZone)
            {
                // hti.edge = DataGridViewHitTestTypeCloseEdge.Bottom;

                if (RowIsResizable(hti._row) && AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None)
                {
                    hti._typeInternal = DataGridViewHitTestTypeInternal.RowResizeBottom;
                    hti._mouseBarOffset = yRowTopEdge + rowHeight - y - 1;
                }
            }
            else if (y - yRowTopEdge < RowSizingHotZone)
            {
                // hti.edge = DataGridViewHitTestTypeCloseEdge.Top;
                int indexTmp = -1;
                if (hti._row != DisplayedBandsInfo.FirstDisplayedScrollingRow || DisplayedBandsInfo.NumDisplayedFrozenRows > 0)
                {
                    indexTmp = Rows.GetPreviousRow(hti._row, DataGridViewElementStates.Visible);
                }

                if (indexTmp != -1)
                {
                    if (RowIsResizable(indexTmp) && AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None)
                    {
                        hti._typeInternal = DataGridViewHitTestTypeInternal.RowResizeTop;
                        hti._adjacentRow = indexTmp;
                        hti._mouseBarOffset = yRowTopEdge - y - 1;
                    }
                }
                else
                {
                    if (ColumnHeadersVisible && ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing)
                    {
                        hti._typeInternal = DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom;
                        hti._mouseBarOffset = yRowTopEdge - y - 1;
                    }
                }
            }
            else if ((!RightToLeftInternal && _layout.RowHeaders.Right - x < ColumnSizingHotZone)
                || (RightToLeftInternal && x - _layout.RowHeaders.Left < ColumnSizingHotZone))
            {
                // hti.edge = DataGridViewHitTestTypeCloseEdge.Right;
                if (RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing)
                {
                    hti._typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeRight;
                    hti._mouseBarOffset = RightToLeftInternal
                        ? _layout.RowHeaders.Left - x - 1
                        : _layout.RowHeaders.Right - x - 1;
                }
            }
        }

        if (_layout.Data.Contains(x, y))
        {
            hti._col = GetColumnIndexFromX(x, out int xColumnLeftEdge);
            hti._row = GetRowIndexFromY(y, out int yRowTopEdge);
            if (hti._col < 0 || hti._row < 0)
            {
                return HitTestInfo.Nowhere;
            }

            Debug.Assert(xColumnLeftEdge == GetColumnXFromIndex(hti._col));
            Debug.Assert(yRowTopEdge == GetRowYFromIndex(hti._row));
            hti._type = DataGridViewHitTestType.Cell;
            hti._typeInternal = DataGridViewHitTestTypeInternal.Cell;
            hti._rowStart = yRowTopEdge;
            hti._colStart = xColumnLeftEdge;
            if (!ColumnHeadersVisible)
            {
                int columnWidth = Columns[hti._col].Thickness;
                if ((!RightToLeftInternal && xColumnLeftEdge + columnWidth - x < ColumnSizingHotZone)
                    || (RightToLeftInternal && x - xColumnLeftEdge + columnWidth < ColumnSizingHotZone))
                {
                    hti._mouseBarOffset = RightToLeftInternal
                        ? xColumnLeftEdge - columnWidth - x + 1
                        : xColumnLeftEdge + columnWidth - x - 1;

                    DataGridViewColumn dataGridViewColumn = Columns[hti._col];
                    if (dataGridViewColumn.Resizable == DataGridViewTriState.True
                        && (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None
                            || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill))
                    {
                        hti._typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeRight;
                    }

                    return hti;
                }
                else if ((!RightToLeftInternal && x - xColumnLeftEdge < ColumnSizingHotZone)
                    || (RightToLeftInternal && xColumnLeftEdge - x < ColumnSizingHotZone))
                {
                    DataGridViewColumn? dataGridViewColumn = null;
                    if (hti._col != DisplayedBandsInfo.FirstDisplayedScrollingCol || DisplayedBandsInfo.LastTotallyDisplayedScrollingCol >= 0)
                    {
                        dataGridViewColumn = Columns.GetPreviousColumn(Columns[hti._col],
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.None);
                    }

                    if (dataGridViewColumn is not null)
                    {
                        hti._adjacentCol = dataGridViewColumn.Index;
                        hti._mouseBarOffset = RightToLeftInternal ? xColumnLeftEdge - x + 1 : xColumnLeftEdge - x - 1;

                        if (dataGridViewColumn.Resizable == DataGridViewTriState.True
                            && (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None
                                || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill))
                        {
                            hti._typeInternal = DataGridViewHitTestTypeInternal.ColumnResizeLeft;
                        }

                        return hti;
                    }
                    else
                    {
                        if (RowHeadersVisible && RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing)
                        {
                            hti._typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeLeft;
                            hti._mouseBarOffset = RightToLeftInternal ? xColumnLeftEdge - x : xColumnLeftEdge - x - 1;

                            return hti;
                        }
                    }
                }
            }
            else if ((!RightToLeftInternal && x - xColumnLeftEdge < ColumnSizingHotZone)
                || (RightToLeftInternal && xColumnLeftEdge - x < ColumnSizingHotZone))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                if (hti._col == dataGridViewColumn.Index
                    && RowHeadersVisible
                    && RowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing)
                {
                    hti._typeInternal = DataGridViewHitTestTypeInternal.RowHeadersResizeLeft;
                    hti._mouseBarOffset = RightToLeftInternal ? xColumnLeftEdge - x : xColumnLeftEdge - x - 1;

                    return hti;
                }
            }

            if (!RowHeadersVisible)
            {
                int rowHeight = Rows.SharedRow(hti._row).GetHeight(hti._row);
                if (yRowTopEdge + rowHeight - y < RowSizingHotZone)
                {
                    if (RowIsResizable(hti._row) && AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None)
                    {
                        hti._typeInternal = DataGridViewHitTestTypeInternal.RowResizeBottom;
                        hti._mouseBarOffset = yRowTopEdge + rowHeight - y - 1;
                    }
                }
                else if (y - yRowTopEdge < RowSizingHotZone)
                {
                    int indexTmp = -1;
                    if (hti._row != DisplayedBandsInfo.FirstDisplayedScrollingRow || DisplayedBandsInfo.NumDisplayedFrozenRows > 0)
                    {
                        indexTmp = Rows.GetPreviousRow(hti._row,
                            DataGridViewElementStates.Visible);
                    }

                    if (indexTmp != -1)
                    {
                        if (RowIsResizable(indexTmp) && AutoSizeRowsMode == DataGridViewAutoSizeRowsMode.None)
                        {
                            hti._typeInternal = DataGridViewHitTestTypeInternal.RowResizeTop;
                            hti._adjacentRow = indexTmp;
                            hti._mouseBarOffset = yRowTopEdge - y - 1;
                        }
                    }
                    else
                    {
                        if (ColumnHeadersVisible && ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing)
                        {
                            hti._typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop;
                            hti._mouseBarOffset = yRowTopEdge - y - 1;
                        }
                    }
                }
            }
            else if (y - yRowTopEdge < RowSizingHotZone)
            {
                int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                Debug.Assert(rowIndex >= 0);
                if (hti._row == rowIndex
                    && ColumnHeadersVisible
                    && ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing)
                {
                    hti._typeInternal = DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop;
                    hti._mouseBarOffset = yRowTopEdge - y - 1;
                }
            }
        }

        return hti;
    }

    private void HorizScrollTimer_Tick(object? sender, EventArgs e)
    {
        BeginInvoke(new MethodInvoker(HorizScrollTimerHandler));
    }

    private void HorizScrollTimerHandler()
    {
        Debug.Assert(_dataGridViewOper[OperationTrackColSelect] || _dataGridViewOper[OperationTrackCellSelect]);

        Point ptMouse = PointToClient(MousePosition);
        HitTestInfo hti = HitTest(ptMouse.X, ptMouse.Y);
        int mouseX = ptMouse.X, mouseY = ptMouse.Y;
        if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out int xOffset, out int yOffset))
        {
            if (xOffset != 0)
            {
                int absXOffset = Math.Abs(xOffset), normOffset = xOffset / absXOffset;
                ScrollColumns(normOffset);
                Debug.Assert(_horizScrollTimer is not null);
                _horizScrollTimer.Interval = GetColumnScrollRate(absXOffset);
                if (_dataGridViewOper[OperationTrackColSelect])
                {
                    hti = HitTest(ptMouse.X + (RightToLeftInternal ? 1 : -1) * (xOffset + normOffset), mouseY);
                    if (hti._col >= 0)
                    {
                        OnColumnSelectMouseMove(hti);
                    }
                }
                else if (_dataGridViewOper[OperationTrackCellSelect])
                {
                    hti = yOffset != 0
                        ? HitTest(
                            ptMouse.X + (RightToLeftInternal ? 1 : -1) * (xOffset + normOffset),
                            ptMouse.Y - yOffset - (yOffset / Math.Abs(yOffset)))
                        : HitTest(ptMouse.X + (RightToLeftInternal ? 1 : -1) * (xOffset + normOffset), mouseY);

                    if (hti._col >= 0 && hti._row >= 0)
                    {
                        OnCellSelectMouseMove(hti);
                    }
                }
            }
            else
            {
                if (_dataGridViewOper[OperationTrackColSelect] && hti._col >= 0)
                {
                    OnColumnSelectMouseMove(hti);
                }
                else if (_dataGridViewOper[OperationTrackCellSelect] && hti._col >= 0 && hti._row >= 0)
                {
                    OnCellSelectMouseMove(hti);
                }

                HorizScrollTimer.Enabled = false;
            }
        }
    }

    // Returns true for success, returns false when the OnDataError event cancels the operation.
    private bool InitializeEditingCellValue(ref DataGridViewCellStyle dataGridViewCellStyle, ref DataGridViewCell dataGridViewCell)
    {
        DataGridViewDataErrorEventArgs? dgvdee = null;
        // Save unedited value so we can restore it later if parsing of new value fails
        _uneditedFormattedValue = dataGridViewCell.GetFormattedValue(_ptCurrentCell.Y, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting);
        _dataGridViewState1[State1_IgnoringEditingChanges] = true;
        try
        {
            IDataGridViewEditingCell dataGridViewEditingCell = (IDataGridViewEditingCell)dataGridViewCell;
            object? currentFormattedValue = dataGridViewEditingCell.GetEditingCellFormattedValue(DataGridViewDataErrorContexts.Formatting);
            if ((currentFormattedValue is null && _uneditedFormattedValue is not null)
                || (currentFormattedValue is not null && _uneditedFormattedValue is null)
                || (currentFormattedValue is not null && !_uneditedFormattedValue!.Equals(currentFormattedValue)))
            {
                Debug.Assert(_ptCurrentCell.X == dataGridViewCell.ColumnIndex);
                dataGridViewCell = Rows[_ptCurrentCell.Y].Cells[_ptCurrentCell.X]; // un-share the edited cell
                dataGridViewEditingCell = (IDataGridViewEditingCell)dataGridViewCell;
                dataGridViewEditingCell.EditingCellFormattedValue = _uneditedFormattedValue;
                dataGridViewEditingCell.EditingCellValueChanged = false;
            }
        }
        catch (Exception exception) when (!exception.IsCriticalException())
        {
            dgvdee = new(
                exception,
                _ptCurrentCell.X,
                _ptCurrentCell.Y,
                DataGridViewDataErrorContexts.InitialValueRestoration);
            OnDataErrorInternal(dgvdee);
        }
        finally
        {
            _dataGridViewState1[State1_IgnoringEditingChanges] = false;
        }

        return dgvdee is null || (dgvdee.ThrowException ? throw dgvdee.Exception : !dgvdee.Cancel);
    }

    // Returns true for success, returns false when the OnDataError event cancels the operation.
    private bool InitializeEditingControlValue(ref DataGridViewCellStyle dataGridViewCellStyle, DataGridViewCell dataGridViewCell)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        Debug.Assert(EditingControl is not null);

        DataGridViewDataErrorEventArgs? dgvdee = null;
        object? initialFormattedValue = dataGridViewCell.GetFormattedValue(_ptCurrentCell.Y, ref dataGridViewCellStyle, DataGridViewDataErrorContexts.Formatting);
        _dataGridViewState1[State1_EditingControlChanging] = true;
        _dataGridViewState1[State1_IgnoringEditingChanges] = true;
        try
        {
            dataGridViewCell.InitializeEditingControl(_ptCurrentCell.Y, initialFormattedValue, dataGridViewCellStyle);
            ((IDataGridViewEditingControl)EditingControl).EditingControlValueChanged = false;
        }
        catch (Exception exception) when (!exception.IsCriticalException())
        {
            dgvdee = new(
                exception,
                _ptCurrentCell.X,
                _ptCurrentCell.Y,
                DataGridViewDataErrorContexts.InitialValueRestoration);
            OnDataErrorInternal(dgvdee);
        }
        finally
        {
            _dataGridViewState1[State1_EditingControlChanging] = false;
            _dataGridViewState1[State1_IgnoringEditingChanges] = false;
        }

        if (dgvdee is not null)
        {
            return dgvdee.ThrowException ? throw dgvdee.Exception : !dgvdee.Cancel;
        }

        // Save unedited value so we can restore it later if parsing of new value fails
        _uneditedFormattedValue = initialFormattedValue;
        return true;
    }

    public void InvalidateCell(DataGridViewCell dataGridViewCell)
    {
        ArgumentNullException.ThrowIfNull(dataGridViewCell);

        if (dataGridViewCell.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_CellDoesNotBelongToDataGridView);
        }

        InvalidateCellPrivate(dataGridViewCell);
    }

    private void InvalidateCellPrivate(DataGridViewCell dataGridViewCell)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        Debug.Assert(dataGridViewCell.DataGridView == this);
        InvalidateCell(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex);
    }

    public void InvalidateCell(int columnIndex, int rowIndex)
    {
        ArgumentOutOfRangeException.ThrowIfLessThan(columnIndex, -1);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);

        ArgumentOutOfRangeException.ThrowIfLessThan(rowIndex, -1);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        InvalidateCellPrivate(columnIndex, rowIndex);
    }

    private void InvalidateCellPrivate(int columnIndex, int rowIndex)
    {
        if (IsHandleCreated)
        {
            Rectangle cellDisplayRect = GetCellAdjustedDisplayRectangle(columnIndex, rowIndex, true);
            if (!cellDisplayRect.IsEmpty)
            {
                Invalidate(cellDisplayRect);
            }
        }
    }

    /// <summary>
    ///  Invalidate the painting region for the column specified.
    /// </summary>
    public void InvalidateColumn(int columnIndex)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(columnIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);

        InvalidateColumnInternal(columnIndex);
    }

    internal void InvalidateColumnInternal(int columnIndex)
    {
        Debug.Assert(columnIndex >= 0 && columnIndex < Columns.Count);

        if (IsHandleCreated)
        {
            Rectangle columnDisplayRect = GetColumnDisplayRectanglePrivate(columnIndex, true);
            if (!columnDisplayRect.IsEmpty)
            {
                Invalidate(columnDisplayRect);
            }
        }
    }

    private void InvalidateData()
    {
        if (IsHandleCreated)
        {
            Invalidate(_layout.Data);
        }
    }

    /// <summary>
    ///  Invalidates the scrollable area of the DataGridView.
    /// </summary>
    private void InvalidateInside()
    {
        if (IsHandleCreated)
        {
            Invalidate(_layout.Inside);
        }
    }

    /// <summary>
    ///  Invalidate the painting region for the row specified.
    /// </summary>
    public void InvalidateRow(int rowIndex)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rowIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        InvalidateRowPrivate(rowIndex);
    }

    private void InvalidateRowPrivate(int rowIndex)
    {
        Debug.Assert(rowIndex >= 0 && rowIndex < Rows.Count);

        if (IsHandleCreated)
        {
            Rectangle rowDisplayRect = GetRowDisplayRectanglePrivate(rowIndex, true);
            if (!rowDisplayRect.IsEmpty)
            {
                Invalidate(rowDisplayRect);
            }
        }
    }

    private void InvalidateRowHeights()
    {
        Rows.InvalidateCachedRowsHeights();
        if (IsHandleCreated)
        {
            PerformLayoutPrivate(
                useRowShortcut: false,
                computeVisibleRows: false,
                invalidInAdjustFillingColumns: false,
                repositionEditingControl: false);
            Invalidate();
        }
    }

    private void InvalidateRows(int lo, int hi)
    {
        Debug.Assert(lo <= hi);
        Debug.Assert(lo < Rows.Count);
        Debug.Assert(hi < Rows.Count);

        if (Rows.GetRowCount(DataGridViewElementStates.Visible) == 0)
        {
            return;
        }

        Rectangle rowDisplayRect, data;
        int top, bottom;

        data = _layout.Data;

        // If "lo" is not visible, then get the next visible row
        if ((Rows.GetRowState(lo) & DataGridViewElementStates.Visible) == 0)
        {
            lo = Rows.GetNextRow(lo, DataGridViewElementStates.Visible);
        }

        if (lo == -1)
        {
            // there are no visible rows below "lo" so there is nothing to invalidate.
            return;
        }

        // If "hi" is not visible, then get the previous visible row
        if ((Rows.GetRowState(hi) & DataGridViewElementStates.Visible) == 0)
        {
            hi = Rows.GetPreviousRow(hi, DataGridViewElementStates.Visible);
        }

        Debug.Assert(lo <= hi);
        Debug.Assert(lo > -1);

        rowDisplayRect = GetRowDisplayRectangle(lo, cutOverflow: true);

        if (rowDisplayRect.IsEmpty)
        {
            // The top row is offscreen
            if ((Rows.GetRowState(lo) & DataGridViewElementStates.Frozen) != 0)
            {
                // "lo" is a frozen row which is offscreen.
                // This means that "lo" and any other row below it are offscreen.
                return;
            }
            else if (DisplayedBandsInfo.NumDisplayedScrollingRows == 0)
            {
                // "lo" and all the rows below are scrolling rows but no scrolling rows are displayed.
                return;
            }
            else if (lo >= DisplayedBandsInfo.FirstDisplayedScrollingRow
                && Rows.GetRowCount(
                    DataGridViewElementStates.Visible,
                    DisplayedBandsInfo.FirstDisplayedScrollingRow,
                    lo) >= DisplayedBandsInfo.NumDisplayedScrollingRows)
            {
                // "lo" is a scrolling row whose coordinates are below the last visible row.
                return;
            }
            else
            {
                // "lo" is a scrolling row "behind" frozen rows.
                // Start invalidating at the top of the first displayed scrolling row.
                top = GetRowDisplayRectangle(DisplayedBandsInfo.FirstDisplayedScrollingRow, cutOverflow: true).Top;
            }
        }
        else
        {
            top = rowDisplayRect.Top;
        }

        rowDisplayRect = GetRowDisplayRectangle(hi, cutOverflow: true);

        if (rowDisplayRect.IsEmpty)
        {
            // The bottom row is offscreen.
            if ((Rows.GetRowState(hi) & DataGridViewElementStates.Frozen) == DataGridViewElementStates.Frozen)
            {
                // "hi" is a frozen row offscreen and "lo" is a frozen row on screen.
                // Invalidate all the way to the bottom
                bottom = data.Bottom;
            }
            else if (hi > DisplayedBandsInfo.FirstDisplayedScrollingRow)
            {
                // "hi" is a scrolling row offscreen which is beyond the firstDisplayedScrollingRow
                // Invalidate all the way to the bottom again.
                bottom = data.Bottom;
            }
            else if (Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) == 0)
            {
                // "hi" is a scrolling row above the first displayed scrolling row and there are no frozen rows.
                // There is nothing to invalidate.
                return;
            }
            else
            {
                // "hi" is a scrolling row which is "behind" the frozen rows.
                // Invalidate all the way to the bottom of the frozen rows
                // Compute the bottom of the last displayed frozen row.
                // There may be invisible rows between the frozen rows.
                bottom = 0;
                for (int i = 0; i < DisplayedBandsInfo.NumDisplayedFrozenRows;)
                {
                    if ((Rows.GetRowState(i) & DataGridViewElementStates.Visible) == 0)
                    {
                        continue;
                    }

                    if (i == DisplayedBandsInfo.NumDisplayedFrozenRows - 1)
                    {
                        bottom = GetRowDisplayRectangle(i, cutOverflow: true).Bottom;
                        break;
                    }

                    i++;
                }

                if (bottom <= top)
                {
                    // In this case both "lo" and "hi" are two scrolling rows behind the frozen rows.
                    // Nothing to invalidate.
                    return;
                }
            }
        }
        else
        {
            bottom = rowDisplayRect.Bottom;
        }

        Invalidate(new Rectangle(data.X, top, data.Width, bottom - top));
    }

    private void InvalidateScrollBars()
    {
        // invalidate the horizontal and the vertical scrollbars
        // note that the scrollbars can be null - this happens when
        // the control has been disposed.
        if (_horizScrollBar is not null && _horizScrollBar.Visible)
        {
            _horizScrollBar.Invalidate();
        }

        if (_vertScrollBar is not null && _vertScrollBar.Visible)
        {
            _vertScrollBar.Invalidate();
        }
    }

    private bool IsColumnOutOfBounds(int columnIndex)
    {
        return columnIndex >= Columns.Count || columnIndex == -1;
    }

    private bool IsInnerCellOutOfBounds(int columnIndex, int rowIndex)
    {
        return columnIndex >= Columns.Count || rowIndex >= Rows.Count || columnIndex == -1 || rowIndex == -1;
    }

    private bool IsRowOutOfBounds(int rowIndex)
    {
        return rowIndex >= Rows.Count || rowIndex == -1;
    }

    protected override bool IsInputChar(char charCode)
    {
        if (EditingControl is not null && _dataGridViewState1[State1_ForwardCharMessage])
        {
            // Do not process key press in ProcessDialogChar.
            return true;
        }
        else
        {
            return base.IsInputChar(charCode);
        }
    }

    protected override bool IsInputKey(Keys keyData)
    {
        if ((keyData & Keys.Alt) == Keys.Alt)
        {
            return false;
        }

        switch (keyData & Keys.KeyCode)
        {
            case Keys.Escape:
                {
                    return IsEscapeKeyEffective;
                }

            case Keys.Tab:
                {
                    return GetTabKeyEffective((keyData & Keys.Shift) == Keys.Shift, (keyData & Keys.Control) == Keys.Control);
                }

            case Keys.A:
                {
                    if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Control)
                    {
                        return true;
                    }

                    break;
                }

            case Keys.C:
            case Keys.Insert:
                {
                    if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Control)
                    {
                        return true;
                    }

                    break;
                }

            case Keys.Space:
                {
                    if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift
                        && (SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect
                            || SelectionMode == DataGridViewSelectionMode.RowHeaderSelect)
                        && _ptCurrentCell.X != -1)
                    {
                        return true;
                    }

                    break;
                }

            case Keys.Up:
            case Keys.Down:
            case Keys.Left:
            case Keys.Right:
            case Keys.Home:
            case Keys.End:
            case Keys.Next:
            case Keys.Prior:
            case Keys.Enter:
            case Keys.Delete:
            case Keys.D0:
            case Keys.NumPad0:
            case Keys.F2:
            case Keys.F3:
                {
                    return true;
                }
        }

        return base.IsInputKey(keyData);
    }

    /// <summary>
    ///  Determines if Scrollbars should be visible, updates their bounds and the bounds of all
    ///  other regions in the dataGridView's Layout.
    /// </summary>
    private void LayoutScrollBars()
    {
        using SuspendLayoutScope scope = new(this, performLayout: false);

        // Scrollbars are a tricky issue. We need to see if we can cram our columns and rows in without scrollbars
        // and if they don't fit, we make scrollbars visible and then fixup our regions for the data.
        bool allowHorizScrollbar = ((_scrollBars == ScrollBars.Both) || (_scrollBars == ScrollBars.Horizontal))
            && _dataGridViewState2[State2_AllowHorizontalScrollbar];
        bool allowVertScrollbar = _scrollBars is ScrollBars.Both or ScrollBars.Vertical;
        bool needHorizScrollbarWithoutVertScrollbar = false;
        bool needHorizScrollbar = false;
        bool needVertScrollbar = false;
        bool rightToLeftInternal = RightToLeftInternal;
        int oldfirstDisplayedScrollingRow;

        int totalVisibleColCount = Columns.GetColumnCount(DataGridViewElementStates.Visible);
        int totalVisibleRowCount = Rows.GetRowCount(DataGridViewElementStates.Visible);
        int totalVisibleWidth = Columns.GetColumnsWidth(DataGridViewElementStates.Visible);
        int totalVisibleFrozenWidth = Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);

        // Expensive call - dataGridView could have a mode where no row is resizable which would result in better perf
        int totalVisibleHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible);
        int totalVisibleFrozenHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);

        int horizScrollBarHeight = _horizScrollBar.Height = SystemInformation.HorizontalScrollBarHeight;
        int vertScrollBarWidth = _vertScrollBar.Width = SystemInformation.VerticalScrollBarWidth;

        if (allowHorizScrollbar
            && totalVisibleWidth > _layout.Data.Width
            && totalVisibleFrozenWidth < _layout.Data.Width
            && horizScrollBarHeight <= _layout.Data.Height)
        {
            int oldDataHeight = _layout.Data.Height;
            _layout.Data.Height -= horizScrollBarHeight;
            Debug.Assert(_layout.Data.Height >= 0);
            needHorizScrollbarWithoutVertScrollbar = needHorizScrollbar = true;
            if (totalVisibleWidth - _layout.Data.Width <= vertScrollBarWidth
                || _layout.Data.Width - totalVisibleFrozenWidth <= vertScrollBarWidth)
            {
                // Would we still need a horizontal scrollbar if there were a vertical one?
                oldfirstDisplayedScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                ComputeVisibleRows();
                if (DisplayedBandsInfo.NumTotallyDisplayedFrozenRows == Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)
                    && DisplayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)
                    && (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight)))
                {
                    needHorizScrollbar = totalVisibleFrozenWidth < _layout.Data.Width - vertScrollBarWidth;
                }

                DisplayedBandsInfo.FirstDisplayedScrollingRow = oldfirstDisplayedScrollingRow;
            }

            if (needHorizScrollbar)
            {
                if (_layout.RowHeadersVisible)
                {
                    _layout.RowHeaders.Height -= horizScrollBarHeight;
                    Debug.Assert(_layout.RowHeaders.Height >= 0);
                }
            }
            else
            {
                // Restore old data height because turns out a horizontal scroll bar wouldn't make sense
                _layout.Data.Height = oldDataHeight;
            }
        }

        oldfirstDisplayedScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;

        ComputeVisibleRows();
        if (allowVertScrollbar
            && DisplayedBandsInfo.NumTotallyDisplayedFrozenRows == Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)
            && DisplayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount - Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)
            && (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight))
            && _layout.Data.Height > totalVisibleFrozenHeight
            && vertScrollBarWidth <= _layout.Data.Width)
        {
            _layout.Data.Width -= vertScrollBarWidth;
            Debug.Assert(_layout.Data.Width >= 0);
            if (rightToLeftInternal)
            {
                _layout.Data.X += vertScrollBarWidth;
            }

            if (_layout.ColumnHeadersVisible)
            {
                _layout.ColumnHeaders.Width -= vertScrollBarWidth;
                Debug.Assert(_layout.ColumnHeaders.Width >= 0);
                if (rightToLeftInternal)
                {
                    _layout.ColumnHeaders.X += vertScrollBarWidth;
                }
            }

            needVertScrollbar = true;
        }

        DisplayedBandsInfo.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn();

        // Compute the number of visible columns only after setting up the vertical scroll bar.
        ComputeVisibleColumns();

        if (allowHorizScrollbar
            && needVertScrollbar && !needHorizScrollbar
            && totalVisibleWidth > _layout.Data.Width && totalVisibleFrozenWidth < _layout.Data.Width
            && horizScrollBarHeight <= _layout.Data.Height)
        {
            DisplayedBandsInfo.FirstDisplayedScrollingRow = oldfirstDisplayedScrollingRow;
            if (_layout.ColumnHeadersVisible)
            {
                _layout.ColumnHeaders.Width += vertScrollBarWidth;
                if (rightToLeftInternal)
                {
                    _layout.ColumnHeaders.X -= vertScrollBarWidth;
                }
            }

            _layout.Data.Width += vertScrollBarWidth;
            if (rightToLeftInternal)
            {
                _layout.Data.X -= vertScrollBarWidth;
            }

            _layout.Data.Height -= horizScrollBarHeight;
            Debug.Assert(_layout.Data.Height >= 0);
            needVertScrollbar = false;

            ComputeVisibleRows();
            if (DisplayedBandsInfo.NumTotallyDisplayedFrozenRows == Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen)
                && DisplayedBandsInfo.NumTotallyDisplayedScrollingRows != totalVisibleRowCount
                && (totalVisibleHeight - totalVisibleFrozenHeight != ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight))
                && _layout.Data.Height > totalVisibleFrozenHeight
                && vertScrollBarWidth <= _layout.Data.Width)
            {
                _layout.Data.Width -= vertScrollBarWidth;
                Debug.Assert(_layout.Data.Width >= 0);
                if (rightToLeftInternal)
                {
                    _layout.Data.X += vertScrollBarWidth;
                }

                if (_layout.ColumnHeadersVisible)
                {
                    _layout.ColumnHeaders.Width -= vertScrollBarWidth;
                    Debug.Assert(_layout.ColumnHeaders.Width >= 0);
                    if (rightToLeftInternal)
                    {
                        _layout.ColumnHeaders.X += vertScrollBarWidth;
                    }
                }

                needVertScrollbar = true;
            }

            needHorizScrollbar = needVertScrollbar || needHorizScrollbarWithoutVertScrollbar;
        }

        _layout.ResizeBoxRect = default;
        if (needVertScrollbar && needHorizScrollbar)
        {
            _layout.ResizeBoxRect = new Rectangle(
                rightToLeftInternal ? _layout.Data.X - _vertScrollBar.Width : _layout.Data.Right,
                _layout.Data.Bottom,
                _vertScrollBar.Width,
                _horizScrollBar.Height);
        }

        if (needHorizScrollbar && totalVisibleColCount > 0)
        {
            int widthNotVisible = totalVisibleWidth - _layout.Data.Width;

            _horizScrollBar.Minimum = 0;
            _horizScrollBar.Maximum = totalVisibleWidth - totalVisibleFrozenWidth;
            Debug.Assert(_horizScrollBar.Maximum > 0);
            _horizScrollBar.SmallChange = 1;
            _horizScrollBar.LargeChange = Math.Max(totalVisibleWidth - totalVisibleFrozenWidth - widthNotVisible, 0);
            _horizScrollBar.Enabled = Enabled;
            _horizScrollBar.Bounds = new Rectangle(
                rightToLeftInternal ? _layout.Inside.X + _layout.ResizeBoxRect.Width : _layout.Inside.X,
                _layout.Data.Bottom,
                _layout.Inside.Width - _layout.ResizeBoxRect.Width,
                _horizScrollBar.Height);
            _horizScrollBar.Visible = true;
            _horizScrollBar.Invalidate();
        }
        else
        {
            _horizScrollBar.Visible = false;
            HorizontalOffset = 0;

            _horizScrollBar.Enabled = false;
            _horizScrollBar.Minimum = 0;
            _horizScrollBar.Maximum = 1;
            _horizScrollBar.SmallChange = 1;
            _horizScrollBar.LargeChange = 1;
            _horizScrollBar.Value = 0;
        }

        if (needVertScrollbar)
        {
            int vertScrollBarTop = _layout.Data.Y;
            int vertScrollBarHeight = _layout.Data.Height;
            if (_layout.ColumnHeadersVisible)
            {
                vertScrollBarTop = _layout.ColumnHeaders.Y;
                vertScrollBarHeight += _layout.ColumnHeaders.Height;
            }
            else if (SingleHorizontalBorderAdded)
            {
                vertScrollBarTop--;
                vertScrollBarHeight++;
            }

            _vertScrollBar.Minimum = 0;
            _vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight;
            Debug.Assert(_vertScrollBar.Maximum > 0);
            _vertScrollBar.Value = ComputeHeightOfScrolledOffRows();
            _vertScrollBar.LargeChange = _layout.Data.Height - totalVisibleFrozenHeight;
            _vertScrollBar.Bounds = new Rectangle(
                rightToLeftInternal ? _layout.Data.X - _vertScrollBar.Width : _layout.Data.Right,
                vertScrollBarTop,
                _vertScrollBar.Width,
                vertScrollBarHeight);
            _vertScrollBar.Enabled = Enabled;
            _vertScrollBar.Visible = true;
            _vertScrollBar.Invalidate();

            VerticalScrollingOffset = _vertScrollBar.Value;
        }
        else
        {
            _vertScrollBar.Visible = false;
            VerticalScrollingOffset = ComputeHeightOfScrolledOffRows();

            _vertScrollBar.Enabled = false;
            _vertScrollBar.Minimum = 0;
            _vertScrollBar.Maximum = 1;
            _vertScrollBar.LargeChange = 1;
            _vertScrollBar.Value = 0;
        }
    }

    private void MakeFirstDisplayedCellCurrentCell(bool includeNewRow)
    {
        // No current cell - try to set the first displayed cell to be the current one.
        Point firstDisplayedCellAddress = FirstDisplayedCellAddress;
        if (firstDisplayedCellAddress.X != -1
            && (includeNewRow || !AllowUserToAddRowsInternal || firstDisplayedCellAddress.Y != Rows.Count - 1))
        {
            bool success = SetAndSelectCurrentCellAddress(
                firstDisplayedCellAddress.X,
                firstDisplayedCellAddress.Y,
                setAnchorCellAddress: true,
                validateCurrentCell: false,
                throughMouseClick: false,
                clearSelection: true,
                forceCurrentCellSelection: false);
            Debug.Assert(success);
        }
    }

    private static DataGridViewAutoSizeRowMode MapAutoSizeRowsModeToRowMode(DataGridViewAutoSizeRowsMode autoSizeRowsMode)
    {
        switch (autoSizeRowsMode)
        {
            case DataGridViewAutoSizeRowsMode.AllHeaders:
                return DataGridViewAutoSizeRowMode.RowHeader;
            case DataGridViewAutoSizeRowsMode.DisplayedHeaders:
                return DataGridViewAutoSizeRowMode.RowHeader;
            case DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders:
                return DataGridViewAutoSizeRowMode.AllCellsExceptHeader;
            case DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders:
                return DataGridViewAutoSizeRowMode.AllCellsExceptHeader;
            case DataGridViewAutoSizeRowsMode.AllCells:
                return DataGridViewAutoSizeRowMode.AllCells;
            case DataGridViewAutoSizeRowsMode.DisplayedCells:
                return DataGridViewAutoSizeRowMode.AllCells;
            default:
                Debug.Fail("Unexpected autoSizeRowsMode value in MapAutoSizeRowsModeToRowMode");
                return DataGridViewAutoSizeRowMode.RowHeader;
        }
    }

    private void MoveColumnHeadersOrRowResize(MouseEventArgs e)
    {
        _lastRowSplitBar = _currentRowSplitBar;
        _currentRowSplitBar = e.Y;
        Rectangle lastSplitBarRect = CalcRowResizeFeedbackRect(_lastRowSplitBar);

        if (EditingControl is not null
            && _editingPanel is not null
            && !_dataGridViewState1[State1_EditingControlHidden]
            && _editingPanel.Bounds.IntersectsWith(lastSplitBarRect))
        {
            _editingPanel.Invalidate();
            _editingPanel.Update();
            EditingControl.Invalidate();
            EditingControl.Update();
        }

        Invalidate(lastSplitBarRect);
        Invalidate(CalcRowResizeFeedbackRect(_currentRowSplitBar));
    }

    private void MapDataGridViewColumnToDataBoundField(DataGridViewColumn dataGridViewColumn)
    {
        Debug.Assert(DataSource is not null, "this method should only be called when we have a data connection");
        Debug.Assert(dataGridViewColumn.DataPropertyName.Length != 0, "this method should be called only for columns which have DataPropertyName set");
        DataGridViewDataConnection? conn = DataConnection;

        int boundColumnIndex = ((conn is null) ? -1 : conn.BoundColumnIndex(dataGridViewColumn.DataPropertyName));
        if (boundColumnIndex != -1)
        {
            dataGridViewColumn.IsDataBoundInternal = true;
            dataGridViewColumn.BoundColumnIndex = boundColumnIndex;
            dataGridViewColumn.BoundColumnConverter = conn!.BoundColumnConverter(boundColumnIndex);
            dataGridViewColumn.ValueType = conn.BoundColumnValueType(boundColumnIndex);
            dataGridViewColumn.ReadOnly = conn.DataFieldIsReadOnly(dataGridViewColumn.BoundColumnIndex) || dataGridViewColumn.ReadOnly;
            InvalidateColumnInternal(dataGridViewColumn.Index);

            // Set the Sorting information on the data grid view according to the new DataPropertyName.
            // RefreshColumns() has its own routine for setting the Sorting information so don't do this step
            // if we are in RefreshColumns();
            if (dataGridViewColumn.SortMode != DataGridViewColumnSortMode.NotSortable
                && !_dataGridViewOper[OperationInRefreshColumns])
            {
                dataGridViewColumn.HeaderCell.SortGlyphDirection = conn.BoundColumnSortOrder(boundColumnIndex);
                if (SortedColumn is null && dataGridViewColumn.HeaderCell.SortGlyphDirection != SortOrder.None)
                {
                    SortedColumn = dataGridViewColumn;
                    SortOrder = dataGridViewColumn.HeaderCell.SortGlyphDirection;
                    // no need to sort because the back end is already sorted....
                }
            }
        }
        else
        {
            dataGridViewColumn.IsDataBoundInternal = false;
            dataGridViewColumn.BoundColumnIndex = -1;
            dataGridViewColumn.BoundColumnConverter = null;
            InvalidateColumnInternal(dataGridViewColumn.Index);
        }
    }

    private void MoveColumnRelocation(MouseEventArgs e, HitTestInfo hti)
    {
        _lastHeaderShadow = e.X;
        _dataGridViewState2[State2_ShowColumnRelocationInsertion] = ColumnRelocationTarget(e, hti, out _trackColumnEdge);
        Invalidate(Rectangle.Union(_layout.TopLeftHeader, _layout.ColumnHeaders));
    }

    private void MoveRowHeadersOrColumnResize(int x)
    {
        _lastColSplitBar = _currentColSplitBar;
        _currentColSplitBar = x;
        Rectangle lastSplitBarRect = CalcColResizeFeedbackRect(_lastColSplitBar);

        if (EditingControl is not null
            && _editingPanel is not null
            && !_dataGridViewState1[State1_EditingControlHidden]
            && _editingPanel.Bounds.IntersectsWith(lastSplitBarRect))
        {
            _editingPanel.Invalidate();
            _editingPanel.Update();
            EditingControl.Invalidate();
            EditingControl.Update();
        }

        Invalidate(lastSplitBarRect);
        Invalidate(CalcColResizeFeedbackRect(_currentColSplitBar));
    }

    public virtual void NotifyCurrentCellDirty(bool dirty)
    {
        if (!_dataGridViewState1[State1_IgnoringEditingChanges])
        {
            // autosizing has no effect since edited value hasn't been committed
            // and autosizing code only looks at committed values.

            IsCurrentCellDirtyInternal = dirty;
            if (dirty && EditingControl is not null && ((IDataGridViewEditingControl)EditingControl).RepositionEditingControlOnValueChange)
            {
                PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
            }
        }
    }

    internal void OnAddedColumn(DataGridViewColumn dataGridViewColumn)
    {
        Debug.Assert(dataGridViewColumn.Index >= 0);
        Debug.Assert(dataGridViewColumn.DataGridView == this);

        if (dataGridViewColumn.DisplayIndex == -1 || dataGridViewColumn.DisplayIndex >= Columns.Count)
        {
            // Developer did not assign a DisplayIndex or picked a large number.
            // Choose the Index as the DisplayIndex.
            dataGridViewColumn.DisplayIndexInternal = dataGridViewColumn.Index;
            Columns.InvalidateCachedColumnsOrder();
        }

        CorrectColumnDisplayIndexesAfterInsertion(dataGridViewColumn);

        if (dataGridViewColumn.HasHeaderCell)
        {
            dataGridViewColumn.HeaderCell.DataGridView = this;
        }

        AdjustExpandingRows(dataGridViewColumn.Index, fixedWidth: false);
        DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode;
        Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet);
        bool fixedColumnWidth = autoSizeColumnMode is DataGridViewAutoSizeColumnMode.None or DataGridViewAutoSizeColumnMode.Fill;

        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
        {
            AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, fixedRowHeadersWidth: true, fixedColumnWidth);
        }

        if (!fixedColumnWidth)
        {
            // This is the first time the column auto sizes. Save current column width for later reuse.
            dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness;
            AutoResizeColumnInternal(
                dataGridViewColumn.Index,
                (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode,
                fixedHeight: true);

            if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
            {
                // Second round of column headers autosizing
                AutoResizeColumnHeadersHeight(
                    dataGridViewColumn.Index,
                    fixedRowHeadersWidth: true,
                    fixedColumnWidth: true);
            }
        }

        // Raise the ColumnAdded event
        OnColumnAdded(new DataGridViewColumnEventArgs(dataGridViewColumn));
    }

    internal void OnAddedRow_PreNotification(int rowIndex)
    {
        Debug.Assert(rowIndex >= 0);

        if (AllowUserToAddRowsInternal && NewRowIndex == -1)
        {
            // The added row is necessarily the 'new row'
            // Set the this.newRowIndex variable as early as possible.
            Debug.Assert(rowIndex == Rows.Count - 1);
            NewRowIndex = rowIndex;
        }

        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);

#if DEBUG
        DataGridViewRow dataGridViewRowDebug = Rows.SharedRow(rowIndex);
        foreach (DataGridViewCell dataGridViewCell in dataGridViewRowDebug.Cells)
        {
            Debug.Assert(!dataGridViewCell.Selected);
            Debug.Assert(dataGridViewRowDebug.Index != -1 || !dataGridViewCell.HasValue);
        }
#endif

        // Update this.individualReadOnlyCells
        if ((rowState & DataGridViewElementStates.ReadOnly) == 0 && !ReadOnly)
        {
            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
            foreach (DataGridViewCell dataGridViewCell in dataGridViewRow.Cells)
            {
                Debug.Assert(dataGridViewCell.OwningColumn is not null);
                if (!dataGridViewCell.OwningColumn.ReadOnly && IsSharedCellReadOnly(dataGridViewCell, rowIndex))
                {
                    _individualReadOnlyCells.Add(dataGridViewCell);
                }
            }
        }
    }

    internal void OnAddedRow_PostNotification(int rowIndex)
    {
        Debug.Assert(rowIndex >= 0);

        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);

        if ((rowState & DataGridViewElementStates.Visible) != 0)
        {
            bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0;

            DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode;

            bool autoSizeRow = false;
            // Auto size row if needed
            if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None
                && !((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed))
            {
                // this call may un-share the row.
                int rowHeight = Rows.SharedRow(rowIndex).GetHeight(rowIndex);
                Rows.SharedRow(rowIndex).CachedThickness = rowHeight;
                AutoResizeRowInternal(
                    rowIndex,
                    MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode),
                    fixedWidth: false,
                    internalAutosizing: true);
                autoSizeRow = true;
            }

            // Auto size columns also if needed
            DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows;
            if (rowDisplayed)
            {
                autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows;
            }

            bool columnAutoSized;
            if (Rows.GetRowCount(DataGridViewElementStates.Visible) > 1)
            {
                // Columns can only expand, and not collapse.
                columnAutoSized = AdjustExpandingColumns(autoSizeColumnCriteriaFilter, rowIndex);
            }
            else
            {
                columnAutoSized = AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, fixedHeight: true);
            }

            bool fixedColumnHeadersHeight = ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            bool rowHeadersAutoSize = _rowHeadersWidthSizeMode is not DataGridViewRowHeadersWidthSizeMode.EnableResizing
                and not DataGridViewRowHeadersWidthSizeMode.DisableResizing;

            if (!rowHeadersAutoSize && !columnAutoSized)
            {
                // No need to autosize the column headers when the row headers and columns don't change.
                fixedColumnHeadersHeight = true;
            }

            // Auto size row headers
            if (rowHeadersAutoSize)
            {
                AutoResizeRowHeadersWidth(
                    rowIndex,
                    _rowHeadersWidthSizeMode,
                    fixedColumnHeadersHeight,
                    fixedRowHeight: true);
            }

            // Auto size column headers
            if (!fixedColumnHeadersHeight)
            {
                AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
            }

            if (autoSizeRow)
            {
                // Second round of row autosizing
                AutoResizeRowInternal(
                    rowIndex,
                    MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode),
                    fixedWidth: true,
                    internalAutosizing: true);
            }

            if (rowHeadersAutoSize && !fixedColumnHeadersHeight)
            {
                // Second round of row headers autosizing
                AutoResizeRowHeadersWidth(
                    rowIndex,
                    _rowHeadersWidthSizeMode,
                    fixedColumnHeadersHeight: true,
                    fixedRowHeight: true);
            }
        }
    }

    internal void OnAddedRows_PreNotification(DataGridViewRow[] dataGridViewRows)
    {
        // Note: no row can be added that breaks the frozen row packing on the top

        foreach (DataGridViewRow dataGridViewRow in dataGridViewRows)
        {
            OnAddedRow_PreNotification(dataGridViewRow.Index);
        }
    }

    internal void OnAddedRows_PostNotification(DataGridViewRow[] dataGridViewRows)
    {
        foreach (DataGridViewRow dataGridViewRow in dataGridViewRows)
        {
            OnAddedRow_PostNotification(dataGridViewRow.Index);
        }
    }

    internal void OnAddingColumn(DataGridViewColumn dataGridViewColumn)
    {
        // throw an exception if the column to be added breaks the rules
        ArgumentNullException.ThrowIfNull(dataGridViewColumn);

        if (dataGridViewColumn.DataGridView is not null)
        {
            throw new InvalidOperationException(SR.DataGridView_ColumnAlreadyBelongsToDataGridView);
        }

        if (!InInitialization
            && dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic
            && (SelectionMode == DataGridViewSelectionMode.FullColumnSelect || SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect))
        {
            throw new InvalidOperationException(string.Format(SR.DataGridViewColumn_SortModeAndSelectionModeClash, DataGridViewColumnSortMode.Automatic.ToString(), SelectionMode.ToString()));
        }

        if (dataGridViewColumn.Visible)
        {
            // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used

            // Make sure the column does not autosize based only on header while column headers are invisible
            if (!ColumnHeadersVisible
                && (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader
                    || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet
                        && AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader)))
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddAutoSizedColumn);
            }

            // Make sure the column is not frozen and auto fills
            if (dataGridViewColumn.Frozen
                && (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill
                    || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet
                        && AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill)))
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddAutoFillColumn);
            }

            // UsedFillWeight values need to be updated
            _dataGridViewState2[State2_UsedFillWeightsDirty] = true;
        }

        // Make sure the sum of the column weights does not exceed ushort.MaxValue
        float weightSum = Columns.GetColumnsFillWeight(DataGridViewElementStates.None) + dataGridViewColumn.FillWeight;
        if (weightSum > ushort.MaxValue)
        {
            throw new InvalidOperationException(string.Format(SR.DataGridView_WeightSumCannotExceedLongMaxValue, ushort.MaxValue));
        }

        // check for correctness of frozen state - throws exception if state is incorrect.
        CorrectColumnFrozenState(dataGridViewColumn, Columns.Count);

        // prepare the existing rows by appending cells of correct type
        if (Rows.Count > 0)
        {
            // Only require a default cell type when there are rows to fill
            if (dataGridViewColumn.CellType is null)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddUntypedColumn);
            }

            if (dataGridViewColumn.CellTemplate!.DefaultNewRowValue is not null && NewRowIndex != -1)
            {
                // New row needs to be un-shared before addition of new cell with a Value is not null
                // (Accessing the indexer has this side-effect)
                _ = Rows[NewRowIndex];
            }

            int newColumnCount = Columns.Count + 1;

            try
            {
                for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
                {
                    DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                    if (dataGridViewRow.Cells.Count < newColumnCount)
                    {
                        DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone();
                        dataGridViewRow.Cells.AddInternal(dataGridViewCellNew);
                        if (rowIndex == NewRowIndex)
                        {
                            dataGridViewCellNew.SetValueInternal(rowIndex, dataGridViewCellNew.DefaultNewRowValue);
                        }

                        dataGridViewCellNew.DataGridView = this;
                        dataGridViewCellNew.OwningRow = dataGridViewRow;
                        dataGridViewCellNew.OwningColumn = dataGridViewColumn;

                        KeyboardToolTipStateMachine.Instance.Hook(dataGridViewCellNew, KeyboardToolTip);
                    }
                }
            }
            catch
            {
                for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
                {
                    DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                    if (dataGridViewRow.Cells.Count == newColumnCount)
                    {
                        dataGridViewRow.Cells.RemoveAtInternal(newColumnCount - 1);
                    }
                    else
                    {
                        Debug.Assert(dataGridViewRow.Cells.Count < newColumnCount);
                        break;
                    }
                }

                throw;
            }
        }
    }

    internal void OnAddingColumns(DataGridViewColumn[] dataGridViewColumns)
    {
        // Make sure the sum of the column weights does not exceed ushort.MaxValue
        float weightSum = Columns.GetColumnsFillWeight(DataGridViewElementStates.None);
        Debug.Assert(weightSum <= ushort.MaxValue);

        // throw an exception if any of the columns to be added breaks the rules
        Debug.Assert(dataGridViewColumns is not null);
        foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns)
        {
            if (dataGridViewColumn is null)
            {
                throw new InvalidOperationException(SR.DataGridView_AtLeastOneColumnIsNull);
            }

            if (dataGridViewColumn.DataGridView is not null)
            {
                throw new InvalidOperationException(SR.DataGridView_ColumnAlreadyBelongsToDataGridView);
            }

            // Only require a default cell type when there are rows to fill
            if (Rows.Count > 0 && dataGridViewColumn.CellType is null)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddUntypedColumn);
            }

            if (!InInitialization
                && dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic
                && (SelectionMode == DataGridViewSelectionMode.FullColumnSelect || SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect))
            {
                throw new InvalidOperationException(string.Format(
                    SR.DataGridViewColumn_SortModeAndSelectionModeClash,
                    DataGridViewColumnSortMode.Automatic.ToString(),
                    SelectionMode.ToString()));
            }

            if (dataGridViewColumn.Visible)
            {
                // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used

                // Make sure the column does not autosize based only on header while column headers are invisible
                if (!ColumnHeadersVisible
                    && (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader
                        || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet
                            && AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader)))
                {
                    throw new InvalidOperationException(SR.DataGridView_CannotAddAutoSizedColumn);
                }

                // Make sure the column is not frozen and auto fills
                if (dataGridViewColumn.Frozen
                    && (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill
                        || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet
                            && AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill)))
                {
                    throw new InvalidOperationException(SR.DataGridView_CannotAddAutoFillColumn);
                }

                // UsedFillWeight values need to be updated
                _dataGridViewState2[State2_UsedFillWeightsDirty] = true;
            }

            weightSum += dataGridViewColumn.FillWeight;
            if (weightSum > ushort.MaxValue)
            {
                throw new InvalidOperationException(string.Format(SR.DataGridView_WeightSumCannotExceedLongMaxValue, ushort.MaxValue));
            }
        }

        Debug.Assert(weightSum <= ushort.MaxValue);

        // make sure no two columns are identical
        int columnCount = dataGridViewColumns.Length;
        for (int column1 = 0; column1 < columnCount - 1; column1++)
        {
            for (int column2 = column1 + 1; column2 < columnCount; column2++)
            {
                if (dataGridViewColumns[column1] == dataGridViewColumns[column2])
                {
                    throw new InvalidOperationException(SR.DataGridView_CannotAddIdenticalColumns);
                }
            }
        }

        // check for correctness of frozen states - throws exception if any state is incorrect.
        CorrectColumnFrozenStates(dataGridViewColumns);

        // prepare the existing rows by appending cells of correct type
        if (Rows.Count > 0)
        {
            foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns)
            {
                Debug.Assert(dataGridViewColumn.CellType is not null);
                if (dataGridViewColumn.CellTemplate!.DefaultNewRowValue is not null && NewRowIndex != -1)
                {
                    // New row needs to be un-shared before addition of new cell with a Value is not null
                    // (Accessing the indexer has this side-effect)
                    _ = Rows[NewRowIndex];
                    break;
                }
            }

            int previousColumnCount = Columns.Count;
            int addedColumnCount = 0;

            try
            {
                foreach (DataGridViewColumn dataGridViewColumn in dataGridViewColumns)
                {
                    addedColumnCount++;
                    Debug.Assert(dataGridViewColumn.CellType is not null);
                    for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
                    {
                        DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                        if (dataGridViewRow.Cells.Count < previousColumnCount + addedColumnCount)
                        {
                            DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate!.Clone();
                            int indexCell = dataGridViewRow.Cells.AddInternal(dataGridViewCellNew);
                            if (rowIndex == NewRowIndex)
                            {
                                dataGridViewCellNew.Value = dataGridViewCellNew.DefaultNewRowValue;
                            }

                            dataGridViewCellNew.DataGridView = this;
                            dataGridViewCellNew.OwningRow = dataGridViewRow;
                            dataGridViewCellNew.OwningColumn = dataGridViewColumn;
                        }
                    }
                }
            }
            catch
            {
                for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
                {
                    DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                    while (dataGridViewRow.Cells.Count > previousColumnCount)
                    {
                        dataGridViewRow.Cells.RemoveAtInternal(dataGridViewRow.Cells.Count - 1);
                    }
                }

                throw;
            }
        }
    }

    internal void OnAddingRow(DataGridViewRow dataGridViewRow, DataGridViewElementStates rowState, bool checkFrozenState)
    {
        // Note dataGridViewRow.DataGridView is not null for duplication of shared rows.

        // throw an exception if the row to be added breaks the rules
        ArgumentNullException.ThrowIfNull(dataGridViewRow);

        // !Do not check for dataGridViewRow.Selected flag. Caller does it instead!
        // !Do not check for dataGridViewRow.DataGridView is not null. Caller does it instead!

        if (checkFrozenState)
        {
            // check for correctness of frozen state - throws exception if state is incorrect.
            CorrectRowFrozenState(dataGridViewRow, rowState, Rows.Count);
        }

        if (ReadOnly && dataGridViewRow.DataGridView is null && dataGridViewRow.ReadOnly)
        {
            // Clear the superfluous flag since the whole dataGridView is read-only
            dataGridViewRow.ReadOnly = false;
        }

        int columnIndex = 0;
        foreach (DataGridViewColumn dataGridViewColumn in Columns)
        {
            DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex];
            if ((ReadOnly || dataGridViewColumn.ReadOnly) && dataGridViewCell.StateIncludes(DataGridViewElementStates.ReadOnly))
            {
                // Clear superfluous flag since the whole dataGridView or column is ReadOnly
                dataGridViewCell.ReadOnlyInternal = false;
            }

            KeyboardToolTipStateMachine.Instance.Hook(dataGridViewCell, KeyboardToolTip);

            columnIndex++;
        }
    }

    internal void OnAddingRows(DataGridViewRow[] dataGridViewRows, bool checkFrozenStates)
    {
        // throw an exception if any of the rows to be added breaks the rules
        Debug.Assert(dataGridViewRows is not null);

        foreach (DataGridViewRow dataGridViewRow in dataGridViewRows)
        {
            if (dataGridViewRow is null)
            {
                throw new InvalidOperationException(SR.DataGridView_AtLeastOneRowIsNull);
            }

            if (dataGridViewRow.DataGridView is not null)
            {
                throw new InvalidOperationException(SR.DataGridView_RowAlreadyBelongsToDataGridView);
            }

            if (dataGridViewRow.Selected)
            {
                throw new InvalidOperationException(SR.DataGridViewRowCollection_CannotAddOrInsertSelectedRow);
            }

            if (dataGridViewRow.Cells.Count > Columns.Count)
            {
                throw new InvalidOperationException(SR.DataGridViewRowCollection_TooManyCells);
            }
        }

        // make sure no two rows are identical
        int rowCount = dataGridViewRows.Length;
        for (int row1 = 0; row1 < rowCount - 1; row1++)
        {
            for (int row2 = row1 + 1; row2 < rowCount; row2++)
            {
                if (dataGridViewRows[row1] == dataGridViewRows[row2])
                {
                    throw new InvalidOperationException(SR.DataGridView_CannotAddIdenticalRows);
                }
            }
        }

        if (checkFrozenStates)
        {
            Debug.Assert(!AllowUserToAddRowsInternal);
            CorrectRowFrozenStates(dataGridViewRows, rowIndexInserted: Rows.Count);
        }

        foreach (DataGridViewRow dataGridViewRow in dataGridViewRows)
        {
            CompleteCellsCollection(dataGridViewRow);
            OnAddingRow(dataGridViewRow, dataGridViewRow.State, checkFrozenState: false);
        }
    }

    internal void OnAdvancedBorderStyleChanged(DataGridViewAdvancedBorderStyle dgvabs)
    {
        if (!_dataGridViewOper[OperationInBorderStyleChange])
        {
            if (dgvabs == AdvancedCellBorderStyle)
            {
                OnCellBorderStyleChanged(EventArgs.Empty);
            }
            else if (dgvabs == AdvancedColumnHeadersBorderStyle)
            {
                OnColumnHeadersBorderStyleChanged(EventArgs.Empty);
            }
            else if (dgvabs == AdvancedRowHeadersBorderStyle)
            {
                OnRowHeadersBorderStyleChanged(EventArgs.Empty);
            }
        }
    }

    protected virtual void OnAllowUserToAddRowsChanged(EventArgs e)
    {
        PushAllowUserToAddRows();
        GetEvent<EventHandler>(s_allowUserToAddRowsChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnAllowUserToDeleteRowsChanged(EventArgs e) =>
        GetEvent<EventHandler>(s_allowUserToDeleteRowsChangedEvent)?.Invoke(this, e);

    protected virtual void OnAllowUserToOrderColumnsChanged(EventArgs e) =>
        GetEvent<EventHandler>(s_allowUserToOrderColumnsChangedEvent)?.Invoke(this, e);

    protected virtual void OnAllowUserToResizeColumnsChanged(EventArgs e) =>
        GetEvent<EventHandler>(s_allowUserToResizeColumnsChangedEvent)?.Invoke(this, e);

    protected virtual void OnAllowUserToResizeRowsChanged(EventArgs e) =>
        GetEvent<EventHandler>(s_allowUserToResizeRowsChangedEvent)?.Invoke(this, e);

    protected virtual void OnAlternatingRowsDefaultCellStyleChanged(EventArgs e)
    {
        if (e is DataGridViewCellStyleChangedEventArgs dgvcsce && !dgvcsce.ChangeAffectsPreferredSize)
        {
            InvalidateData();
        }
        else
        {
            OnRowsGlobalAutoSize();
            if (EditingControl is not null)
            {
                PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
            }
        }

        GetEvent<EventHandler>(s_alternatingRowsDefaultCellStyleChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnAutoGenerateColumnsChanged(EventArgs e)
    {
        if (AutoGenerateColumns && DataSource is not null)
        {
            // Refresh the list of columns and the rows
            RefreshColumnsAndRows();
        }

        GetEvent<EventHandler>(s_autoGenerateColumnsChangedEvent)?.Invoke(this, e);
    }

    internal void OnAutoSizeColumnModeChanged(DataGridViewColumn dataGridViewColumn, DataGridViewAutoSizeColumnMode previousInheritedMode)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        DataGridViewAutoSizeColumnModeEventArgs dgvascme = new(dataGridViewColumn, previousInheritedMode);
        OnAutoSizeColumnModeChanged(dgvascme);
    }

    protected virtual void OnAutoSizeColumnModeChanged(DataGridViewAutoSizeColumnModeEventArgs e)
    {
        DataGridViewColumn? dataGridViewColumn = e.Column
            ?? throw new InvalidOperationException(string.Format(SR.InvalidNullArgument, "e.Column"));

        DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode;
        Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet);

        DataGridViewAutoSizeColumnMode previousInheritedMode = e.PreviousMode;
        bool previousInheritedModeAutoSized = previousInheritedMode is not DataGridViewAutoSizeColumnMode.Fill
            and not DataGridViewAutoSizeColumnMode.None
            and not DataGridViewAutoSizeColumnMode.NotSet;

        if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill
            || previousInheritedMode == DataGridViewAutoSizeColumnMode.Fill)
        {
            // UsedFillWeight values need to be updated
            _dataGridViewState2[State2_UsedFillWeightsDirty] = true;
        }

        bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0;

        if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None)
        {
            if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill)
            {
                if (!previousInheritedModeAutoSized)
                {
                    // Save current column width for later reuse
                    dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness;
                }

                AutoResizeColumnInternal(
                    dataGridViewColumn.Index,
                    (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode,
                    fixedHeight);
            }
        }
        else if (dataGridViewColumn.Thickness != dataGridViewColumn.CachedThickness && previousInheritedModeAutoSized)
        {
            // Restoring cached column width
            dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness);
        }

        // Auto fill columns if needed
        PerformLayoutPrivate(
            useRowShortcut: false,
            computeVisibleRows: true,
            invalidInAdjustFillingColumns: true,
            repositionEditingControl: false);

        // Autosize rows and column headers if needed
        if (!fixedHeight)
        {
            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
            if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
            {
                AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, fixedRowHeadersWidth: true, fixedColumnWidth: true);
            }

            // Column gets autosized with 1 degree of freedom this time.
            if (autoSizeColumnMode is not DataGridViewAutoSizeColumnMode.None and not DataGridViewAutoSizeColumnMode.Fill)
            {
                AutoResizeColumnInternal(
                    dataGridViewColumn.Index,
                    (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode,
                    fixedHeight: true);
            }
        }

        GetEvent<DataGridViewAutoSizeColumnModeEventHandler>(s_autosizeColumnModeChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnAutoSizeColumnsModeChanged(DataGridViewAutoSizeColumnsModeEventArgs e)
    {
        DataGridViewAutoSizeColumnMode[] previousModes = e.PreviousModes;

        ArgumentNullException.ThrowIfNull(previousModes, nameof(e.PreviousModes));

        if (previousModes.Length != Columns.Count)
        {
            throw new ArgumentException(SR.DataGridView_PreviousModesHasWrongLength);
        }

        foreach (DataGridViewColumn dataGridViewColumn in Columns)
        {
            if (dataGridViewColumn.Visible)
            {
                DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode;
                Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet);
                DataGridViewAutoSizeColumnMode previousInheritedMode = previousModes[dataGridViewColumn.Index];
                bool previousInheritedModeAutoSized = previousInheritedMode is not DataGridViewAutoSizeColumnMode.Fill
                    and not DataGridViewAutoSizeColumnMode.None
                    and not DataGridViewAutoSizeColumnMode.NotSet;

                if (autoSizeColumnMode == DataGridViewAutoSizeColumnMode.Fill || previousInheritedMode == DataGridViewAutoSizeColumnMode.Fill)
                {
                    // UsedFillWeight values need to be updated
                    _dataGridViewState2[State2_UsedFillWeightsDirty] = true;
                }

                if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None)
                {
                    if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill)
                    {
                        if (!previousInheritedModeAutoSized)
                        {
                            // Save current column width for later reuse
                            dataGridViewColumn.CachedThickness = dataGridViewColumn.Thickness;
                        }

                        AutoResizeColumnInternal(
                            dataGridViewColumn.Index,
                            (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode,
                            fixedHeight: (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0);
                    }
                }
                else if (dataGridViewColumn.Thickness != dataGridViewColumn.CachedThickness && previousInheritedModeAutoSized)
                {
                    // Restoring cached column width
                    dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness);
                }
            }
        }

        // Auto fill columns if needed
        PerformLayoutPrivate(
            useRowShortcut: false,
            computeVisibleRows: true,
            invalidInAdjustFillingColumns: true,
            repositionEditingControl: false);

        // Autosize rows and column headers if needed
        if ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0)
        {
            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
            if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
            {
                AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
            }

            // Second pass of column autosizing with 1 degree of freedom
            foreach (DataGridViewColumn dataGridViewColumn in Columns)
            {
                DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode;
                Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet);

                if (autoSizeColumnMode is not DataGridViewAutoSizeColumnMode.None and not DataGridViewAutoSizeColumnMode.Fill)
                {
                    AutoResizeColumnInternal(
                        dataGridViewColumn.Index,
                        (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode,
                        fixedHeight: true);
                }
            }
        }

        GetEvent<DataGridViewAutoSizeColumnsModeEventHandler>(s_autosizeColumnsModeChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnAutoSizeRowsModeChanged(DataGridViewAutoSizeModeEventArgs e)
    {
        if (_autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None)
        {
            // restore cached rows thickness
            RestoreRowsCachedThickness();
        }
        else
        {
            if (!e.PreviousModeAutoSized)
            {
                // Save the rows thickness for later reuse
                // Note that only visible rows are affected, contrary to columns in OnAutoSizeColumnsModeChanged where all columns are affected.
                for (int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                    rowIndex != -1;
                    rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible))
                {
                    // this call may un-share the row.
                    int rowHeight = Rows.SharedRow(rowIndex).GetHeight(rowIndex);
                    Rows.SharedRow(rowIndex).CachedThickness = rowHeight;
                }
            }

            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
        }

        GetEvent<DataGridViewAutoSizeModeEventHandler>(s_autosizeRowsModeChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnBackgroundColorChanged(EventArgs e)
    {
        InvalidateInside();
        GetEvent<EventHandler>(s_backgroundColorChangedEvent)?.Invoke(this, e);
    }

    internal void OnBandContextMenuStripChanged(DataGridViewBand dataGridViewBand)
    {
        if (dataGridViewBand is DataGridViewColumn dataGridViewColumn)
        {
            DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
            OnColumnContextMenuStripChanged(dgvce);
        }
        else
        {
            DataGridViewRowEventArgs dgvre = new((DataGridViewRow)dataGridViewBand);
            OnRowContextMenuStripChanged(dgvre);
        }
    }

    internal void OnBandDefaultCellStyleChanged(DataGridViewBand dataGridViewBand)
    {
        if (dataGridViewBand is DataGridViewColumn dataGridViewColumn)
        {
            DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
            OnColumnDefaultCellStyleChanged(dgvce);
        }
        else
        {
            DataGridViewRowEventArgs dgvre = new((DataGridViewRow)dataGridViewBand);
            OnRowDefaultCellStyleChanged(dgvre);
        }
    }

    internal void OnBandDividerThicknessChanged(DataGridViewBand dataGridViewBand)
    {
        if (dataGridViewBand is DataGridViewColumn dataGridViewColumn)
        {
            DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
            OnColumnDividerWidthChanged(dgvce);
        }
        else
        {
            DataGridViewRowEventArgs dgvre = new((DataGridViewRow)dataGridViewBand);
            OnRowDividerHeightChanged(dgvre);
        }
    }

    internal void OnBandHeaderCellChanged(DataGridViewBand dataGridViewBand)
    {
        if (dataGridViewBand is DataGridViewColumn dataGridViewColumn)
        {
            DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
            OnColumnHeaderCellChanged(dgvce);
        }
        else
        {
            DataGridViewRowEventArgs dgvre = new((DataGridViewRow)dataGridViewBand);
            OnRowHeaderCellChanged(dgvre);
        }
    }

    internal void OnBandMinimumThicknessChanged(DataGridViewBand dataGridViewBand)
    {
        if (dataGridViewBand is DataGridViewColumn dataGridViewColumn)
        {
            DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
            OnColumnMinimumWidthChanged(dgvce);
        }
        else
        {
            DataGridViewRowEventArgs dgvre = new((DataGridViewRow)dataGridViewBand);
            OnRowMinimumHeightChanged(dgvre);
        }
    }

    internal void OnBandThicknessChanged(DataGridViewBand dataGridViewBand)
    {
        if (dataGridViewBand is DataGridViewColumn dataGridViewColumn)
        {
            DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
            OnColumnWidthChanged(dgvce);
        }
        else
        {
            DataGridViewRowEventArgs dgvre = new((DataGridViewRow)dataGridViewBand);
            OnRowHeightChanged(dgvre);
        }
    }

    internal void OnBandThicknessChanging()
    {
        if (InAdjustFillingColumns)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAlterAutoFillColumnParameter);
        }
    }

    protected override void OnBindingContextChanged(EventArgs e)
    {
        if (_dataGridViewState2[State2_InBindingContextChanged])
        {
            return;
        }

        _dataGridViewState2[State2_InBindingContextChanged] = true;
        try
        {
            if (DataConnection is not null)
            {
                CurrentCell = null;
                try
                {
                    DataConnection.SetDataConnection(DataSource, DataMember);
                }
                catch (ArgumentException)
                {
                    if (DesignMode)
                    {
                        // If the DataMember became invalid at DesignTime then set it to String.Empty,
                        // regenerate the column collection and DO NOT send BindingContextChanged event.
                        DataMember = string.Empty;
                        RefreshColumnsAndRows();
                        return;
                    }
                    else
                    {
                        throw;
                    }
                }

                RefreshColumnsAndRows();
                base.OnBindingContextChanged(e);
                if (DataConnection.CurrencyManager is not null)
                {
                    OnDataBindingComplete(ListChangedType.Reset);
                }
            }
            else
            {
                base.OnBindingContextChanged(e);
            }
        }
        finally
        {
            _dataGridViewState2[State2_InBindingContextChanged] = false;
        }
    }

    protected virtual void OnBorderStyleChanged(EventArgs e) =>
        GetEvent<EventHandler>(s_borderStyleChangedEvent)?.Invoke(this, e);

    protected virtual void OnCancelRowEdit(QuestionEventArgs e)
    {
        if (GetEvent<QuestionEventHandler>(s_cancelRowEditEvent) is { } eh)
        {
            eh(this, e);
            CorrectFocus(onlyIfGridHasFocus: true);
        }
    }

    private void CheckEventArgsIndexesUpperBounds(IDataGridViewCellEventArgs e)
    {
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(e.ColumnIndex, Columns.Count, "e.ColumnIndex");
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(e.RowIndex, Rows.Count, "e.RowIndex");
    }

    private static void CheckEventArgsIndexesNotNegative(DataGridViewCellValueEventArgs e)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(e.ColumnIndex, "e.ColumnIndex");
        ArgumentOutOfRangeException.ThrowIfNegative(e.RowIndex, "e.RowIndex");
    }

    protected virtual void OnCellBeginEdit(DataGridViewCellCancelEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellCancelEventHandler>(s_cellBeginEditEvent)?.Invoke(this, e);
    }

    protected virtual void OnCellBorderStyleChanged(EventArgs e)
    {
        PerformLayoutPrivate(
            useRowShortcut: false,
            computeVisibleRows: false,
            invalidInAdjustFillingColumns: true,
            repositionEditingControl: false);

        Invalidate();
        GetEvent<EventHandler>(s_cellBorderStyleChangedEvent)?.Invoke(this, e);
    }

    internal void OnCellClickInternal(DataGridViewCellEventArgs e)
    {
        OnCellClick(e);
    }

    protected virtual void OnCellClick(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.ClickUnsharesRowInternal(e))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnClickInternal(e);
        }
        else
        {
            dataGridViewCell.OnClickInternal(e);
        }

        GetEvent<DataGridViewCellEventHandler>(s_cellClickEvent)?.Invoke(this, e);
    }

    internal void OnCellCommonChange(int columnIndex, int rowIndex)
    {
        if (columnIndex == -1)
        {
            // row or topleft header characteristic has changed
            OnRowHeaderGlobalAutoSize(rowIndex);
        }
        else
        {
            if (rowIndex == -1)
            {
                // column header characteristic has changed
                OnColumnHeaderGlobalAutoSize(columnIndex);
            }
            else
            {
                // regular cell characteristic changed
                InvalidateCellPrivate(columnIndex, rowIndex);

                bool rowDisplayed = false;
                if (rowIndex != -1)
                {
                    rowDisplayed = (Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0;
                }

                DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)Columns[columnIndex].InheritedAutoSizeMode;
                bool autoSizeColumn = (autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.AllRows) != 0;
                if (rowDisplayed)
                {
                    autoSizeColumn |= (autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows) != 0;
                }

                bool autoSizeRow = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0;
                if (autoSizeRow)
                {
                    AutoResizeRowInternal(
                        rowIndex,
                        MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode),
                        fixedWidth: !autoSizeColumn,
                        internalAutosizing: true);
                }

                if (autoSizeColumn)
                {
                    AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight: true);
                    if (autoSizeRow)
                    {
                        // Second round of row autosizing with 1 degree of freedom.
                        AutoResizeRowInternal(
                            rowIndex,
                            MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode),
                            fixedWidth: true,
                            internalAutosizing: true);
                    }
                }
            }
        }
    }

    internal void OnCellContentClickInternal(DataGridViewCellEventArgs e)
    {
        OnCellContentClick(e);
    }

    protected virtual void OnCellContentClick(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.ContentClickUnsharesRowInternal(e))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnContentClickInternal(e);
        }
        else
        {
            dataGridViewCell.OnContentClickInternal(e);
        }

        GetEvent<DataGridViewCellEventHandler>(s_cellContentClickEvent)?.Invoke(this, e);
    }

    internal void OnCellContentDoubleClickInternal(DataGridViewCellEventArgs e)
    {
        OnCellContentDoubleClick(e);
    }

    protected virtual void OnCellContentDoubleClick(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.ContentDoubleClickUnsharesRowInternal(e))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnContentDoubleClickInternal(e);
        }
        else
        {
            dataGridViewCell.OnContentDoubleClickInternal(e);
        }

        GetEvent<DataGridViewCellEventHandler>(s_cellContentDoubleClickEvent)?.Invoke(this, e);
    }

    internal void OnCellContextMenuStripChanged(DataGridViewCell dataGridViewCell)
    {
        DataGridViewCellEventArgs dgvce = new(dataGridViewCell);
        OnCellContextMenuStripChanged(dgvce);
    }

    protected virtual void OnCellContextMenuStripChanged(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellEventHandler>(s_cellContextMenuStripChangedEvent)?.Invoke(this, e);
    }

    internal ContextMenuStrip? OnCellContextMenuStripNeeded(int columnIndex, int rowIndex, ContextMenuStrip? contextMenuStrip)
    {
        DataGridViewCellContextMenuStripNeededEventArgs dgvccmsne = new(columnIndex, rowIndex, contextMenuStrip);
        OnCellContextMenuStripNeeded(dgvccmsne);
        return dgvccmsne.ContextMenuStrip;
    }

    protected virtual void OnCellContextMenuStripNeeded(DataGridViewCellContextMenuStripNeededEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellContextMenuStripNeededEventHandler>(s_cellContextMenuStripNeededEvent)?.Invoke(this, e);
    }

    protected virtual void OnCellDoubleClick(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.DoubleClickUnsharesRowInternal(e))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnDoubleClickInternal(e);
        }
        else
        {
            dataGridViewCell.OnDoubleClickInternal(e);
        }

        GetEvent<DataGridViewCellEventHandler>(s_cellDoubleClickEvent)?.Invoke(this, e);
    }

    protected virtual void OnCellEndEdit(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellEventHandler>(s_cellEndEditEvent)?.Invoke(this, e);
    }

    internal void OnCellEnter(ref DataGridViewCell? dataGridViewCell, int columnIndex, int rowIndex)
    {
        OnCellEnter(new DataGridViewCellEventArgs(columnIndex, rowIndex));
        if (dataGridViewCell is not null)
        {
            if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
            {
                dataGridViewCell = null;
            }
            else
            {
                Debug.Assert(rowIndex < Rows.Count && columnIndex < Columns.Count);
                dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
            }
        }
    }

    protected virtual void OnCellEnter(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        try
        {
            _noDimensionChangeCount++;
            GetEvent<DataGridViewCellEventHandler>(s_cellEnterEvent)?.Invoke(this, e);
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
        }
    }

    internal void OnCellErrorTextChanged(DataGridViewCell dataGridViewCell)
    {
        Debug.Assert(dataGridViewCell.RowIndex >= -1);
        Debug.Assert(dataGridViewCell.ColumnIndex >= -1);
        DataGridViewCellEventArgs dgvce = new(dataGridViewCell);
        OnCellErrorTextChanged(dgvce);
    }

    protected virtual void OnCellErrorTextChanged(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        UpdateCellErrorText(e.ColumnIndex, e.RowIndex);
        GetEvent<DataGridViewCellEventHandler>(s_cellErrorTextChangedEvent)?.Invoke(this, e);
    }

    internal string OnCellErrorTextNeeded(int columnIndex, int rowIndex, string errorText)
    {
        Debug.Assert(columnIndex >= 0);
        Debug.Assert(rowIndex >= 0);
        DataGridViewCellErrorTextNeededEventArgs dgvcetne = new(columnIndex, rowIndex, errorText);
        OnCellErrorTextNeeded(dgvcetne);
        return dgvcetne.ErrorText;
    }

    protected virtual void OnCellErrorTextNeeded(DataGridViewCellErrorTextNeededEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellErrorTextNeededEventHandler>(s_cellErrorTextNeededEvent)?.Invoke(this, e);
    }

    internal DataGridViewCellFormattingEventArgs OnCellFormatting(
        int columnIndex,
        int rowIndex,
        object? val,
        Type? formattedValueType,
        DataGridViewCellStyle cellStyle)
    {
        DataGridViewCellFormattingEventArgs dgvcfe = new(
            columnIndex,
            rowIndex,
            val,
            formattedValueType,
            cellStyle);

        OnCellFormatting(dgvcfe);
        return dgvcfe;
    }

    protected virtual void OnCellFormatting(DataGridViewCellFormattingEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellFormattingEventHandler>(s_cellFormattingEvent)?.Invoke(this, e);
    }

    internal void OnCellLeave(ref DataGridViewCell? dataGridViewCell, int columnIndex, int rowIndex)
    {
        OnCellLeave(new DataGridViewCellEventArgs(columnIndex, rowIndex));
        if (dataGridViewCell is not null)
        {
            if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
            {
                dataGridViewCell = null;
            }
            else
            {
                Debug.Assert(rowIndex < Rows.Count && columnIndex < Columns.Count);
                dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
            }
        }
    }

    protected virtual void OnCellLeave(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        try
        {
            _noDimensionChangeCount++;
            GetEvent<DataGridViewCellEventHandler>(s_cellLeaveEvent)?.Invoke(this, e);
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
        }
    }

    protected virtual void OnCellMouseClick(DataGridViewCellMouseEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.MouseClickUnsharesRowInternal(e))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseClickInternal(e);
        }
        else
        {
            dataGridViewCell.OnMouseClickInternal(e);
        }

        _dataGridViewState2[State2_NextMouseUpIsDouble] = false;
        GetEvent<DataGridViewCellMouseEventHandler>(s_cellMouseClickEvent)?.Invoke(this, e);
    }

    protected virtual void OnCellMouseDoubleClick(DataGridViewCellMouseEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.MouseDoubleClickUnsharesRowInternal(e))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseDoubleClickInternal(e);
        }
        else
        {
            dataGridViewCell.OnMouseDoubleClickInternal(e);
        }

        _dataGridViewState2[State2_NextMouseUpIsDouble] = true;
        GetEvent<DataGridViewCellMouseEventHandler>(s_cellMouseDoubleClickEvent)?.Invoke(this, e);
    }

    protected virtual void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);

        // Only left clicks for now
        Keys nModifier = ModifierKeys;
        bool isControlDown = (nModifier & Keys.Control) == Keys.Control && (nModifier & Keys.Alt) == 0;
        bool isShiftDown = (nModifier & Keys.Shift) == Keys.Shift;
        bool isAltDown = (nModifier & Keys.Alt) == Keys.Alt;

        Point ptGridCoord = ConvertCellToGridCoord(e.ColumnIndex, e.RowIndex, e.X, e.Y);

        HitTestInfo hti = HitTest(ptGridCoord.X, ptGridCoord.Y);

        if (!_dataGridViewState2[State2_MessageFromEditingCtrls] && e.Button == MouseButtons.Left)
        {
            Debug.Assert(hti.Type is not DataGridViewHitTestType.None
                and not DataGridViewHitTestType.HorizontalScrollBar
                and not DataGridViewHitTestType.VerticalScrollBar);
            Debug.Assert(_ptMouseDownCell.X == hti._col);
            Debug.Assert(_ptMouseDownCell.Y == hti._row);

            switch (hti._typeInternal)
            {
                // Check for column/row (headers) resize
                case DataGridViewHitTestTypeInternal.ColumnResizeLeft:
                case DataGridViewHitTestTypeInternal.ColumnResizeRight:
                case DataGridViewHitTestTypeInternal.RowResizeBottom:
                case DataGridViewHitTestTypeInternal.RowResizeTop:
                case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft:
                case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight:
                case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft:
                case DataGridViewHitTestTypeInternal.RowHeadersResizeRight:
                case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop:
                case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom:
                case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop:
                case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom:
                    {
                        _dataGridViewOper[OperationResizingOperationAboutToStart] = (e.Clicks == 1);
                        break;
                    }
            }
        }

        try
        {
            if (e.RowIndex >= 0 && dataGridViewCell.MouseDownUnsharesRowInternal(e))
            {
                // Accessing the indexer unshares the row
                _ = Rows[e.RowIndex];
                GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseDownInternal(e);
            }
            else
            {
                dataGridViewCell.OnMouseDownInternal(e);
            }

            GetEvent<DataGridViewCellMouseEventHandler>(s_cellMouseDownEvent)?.Invoke(this, e);

            if (!_dataGridViewState2[State2_MessageFromEditingCtrls] && e.Button == MouseButtons.Left)
            {
                switch (hti._typeInternal)
                {
                    // Check column resize
                    case DataGridViewHitTestTypeInternal.ColumnResizeLeft:
                    case DataGridViewHitTestTypeInternal.ColumnResizeRight:
                        {
                            int columnIndex = (hti._typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight) ? hti._col : hti._adjacentCol;
                            Debug.Assert(Columns[columnIndex].Resizable == DataGridViewTriState.True);
                            if (e.Clicks == 1)
                            {
                                BeginMouseColumnResize(ptGridCoord.X, hti._mouseBarOffset, columnIndex);
                            }

                            break;
                        }

                    // Check row resize
                    case DataGridViewHitTestTypeInternal.RowResizeBottom:
                    case DataGridViewHitTestTypeInternal.RowResizeTop:
                        {
                            int rowIndex = (hti._typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom) ? hti._row : hti._adjacentRow;
                            if (e.Clicks == 1)
                            {
                                BeginRowResize(ptGridCoord.Y, hti._mouseBarOffset, rowIndex);
                            }

                            break;
                        }

                    // Check for column header mouse down
                    case DataGridViewHitTestTypeInternal.ColumnHeader:
                    case DataGridViewHitTestTypeInternal.ColumnHeaderLeft:
                    case DataGridViewHitTestTypeInternal.ColumnHeaderRight:
                    case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft:
                        {
                            if (isAltDown && AllowUserToOrderColumns
                                && (SelectionMode == DataGridViewSelectionMode.FullColumnSelect
                                    || SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect))
                            {
                                BeginColumnRelocation(ptGridCoord.X, hti._col);
                            }
                            else
                            {
                                OnColumnHeaderMouseDown(hti, isShiftDown, isControlDown);
                            }

                            break;
                        }

                    // Check for row header mouse down
                    case DataGridViewHitTestTypeInternal.RowHeader:
                        {
                            OnRowHeaderMouseDown(hti, isShiftDown, isControlDown);
                            break;
                        }

                    // Check for cell mouse down
                    case DataGridViewHitTestTypeInternal.Cell:
                        {
                            OnCellMouseDown(hti, isShiftDown, isControlDown);
                            break;
                        }

                    // Check for top/left header mouse down
                    case DataGridViewHitTestTypeInternal.TopLeftHeader:
                        {
                            OnTopLeftHeaderMouseDown();
                            break;
                        }

                    // Check for row headers resize
                    case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft:
                    case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight:
                    case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft:
                    case DataGridViewHitTestTypeInternal.RowHeadersResizeRight:
                        {
                            if (e.Clicks == 1)
                            {
                                BeginRowHeadersResize(ptGridCoord.X, hti._mouseBarOffset);
                            }

                            break;
                        }

                    // Check for column headers resize
                    case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop:
                    case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom:
                    case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop:
                    case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom:
                        {
                            if (e.Clicks == 1)
                            {
                                BeginColumnHeadersResize(ptGridCoord.Y, hti._mouseBarOffset);
                            }

                            break;
                        }
                }

                // Make sure that there is a current cell after this mouse down event.
                if (_ptCurrentCell.X == -1)
                {
                    MakeFirstDisplayedCellCurrentCell(includeNewRow: true);
                }
            }
        }
        finally
        {
            _dataGridViewOper[OperationResizingOperationAboutToStart] = false;
        }
    }

    private void OnCellMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown)
    {
        Debug.Assert(hti.Type == DataGridViewHitTestType.Cell);

        // Only commit cell if the target cell is different from the current one.
        if (_ptCurrentCell.X >= 0 && (_ptCurrentCell.X != hti._col || _ptCurrentCell.Y != hti._row))
        {
            Point ptOriginalCurrentCell = _ptCurrentCell;
            if (!CommitEdit(
                DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit,
                forCurrentCellChange: _ptCurrentCell.X != hti._col || _ptCurrentCell.Y != hti._row,
                forCurrentRowChange: _ptCurrentCell.Y != hti._row))
            {
                // Return silently if validating/commit/abort failed
                return;
            }

            if (_ptCurrentCell != ptOriginalCurrentCell)
            {
                // Somehow the fact that the current cell was committed altered the current cell value.
                // To avoid unintentional multi-selections, we act as if Shift and Control keys were up.
                isShiftDown = isControlDown = false;
            }
        }

        if (hti._col >= Columns.Count)
        {
            DataGridViewColumn? dataGridViewLastVisibleColumn = Columns.GetLastColumn(
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
            if (_ptCurrentCell.X == -1 && dataGridViewLastVisibleColumn is not null)
            {
                // CurrentCell was reset because CommitEdit deleted column(s).
                // Since the user clicked on a cell, we don't want to end up
                // with no CurrentCell. We pick the last visible column in the grid.
                hti._col = dataGridViewLastVisibleColumn.Index;
            }
            else
            {
                return;
            }
        }

        if (hti._row >= Rows.Count)
        {
            int lastVisibleRowIndex = Rows.GetLastRow(DataGridViewElementStates.Visible);
            if (_ptCurrentCell.X == -1 && lastVisibleRowIndex != -1)
            {
                // CurrentCell was reset because CommitEdit deleted row(s).
                // Since the user clicked on a cell, we don't want to end up
                // with no CurrentCell. We pick the last visible row in the
                // grid which may be the 'new row'.
                hti._row = lastVisibleRowIndex;
            }
            else
            {
                return;
            }
        }

        bool select = true;
        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                    {
                        if (isControlDown
                            && IsSharedCellSelected(Rows.SharedRow(hti._row).Cells[hti._col], hti._row)
                            && (!isShiftDown || !MultiSelect))
                        {
                            select = false;
                        }

                        if (select)
                        {
                            if ((!MultiSelect || !isControlDown) && !(MultiSelect && isShiftDown))
                            {
                                Debug.Assert(MultiSelect || _individualSelectedCells.Count <= 1);
                                RemoveIndividuallySelectedCells(hti._col, hti._row);
                            }

                            if (MultiSelect)
                            {
                                if (_dataGridViewOper[OperationTrackMouseMoves])
                                {
                                    _dataGridViewOper[OperationTrackCellSelect] = true;
                                }

                                if (isShiftDown)
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        hti._col,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        hti._row);
                                }
                                else
                                {
                                    SetSelectedCellCore(hti._col, hti._row, selected: true);
                                }
                            }
                            else
                            {
                                SetSelectedCellCore(hti._col, hti._row, selected: true);
                            }
                        }
                        else
                        {
                            SetSelectedCellCore(hti._col, hti._row, selected: false);
                        }

                        bool success = SetCurrentCellAddressCore(
                            hti._col,
                            hti._row,
                            !isShiftDown,
                            validateCurrentCell: false,
                            throughMouseClick: true);
                        Debug.Assert(success);
                        break;
                    }

                case DataGridViewSelectionMode.FullColumnSelect:
                    {
                        if (isControlDown && Columns[hti._col].Selected)
                        {
                            select = false;
                        }

                        if (select)
                        {
                            bool selectColumnRange = false;
                            _trackColumn = hti._col;
                            _trackColumnEdge = -1;
                            if (MultiSelect && isShiftDown && _ptAnchorCell.X > -1 && Columns[_ptAnchorCell.X].Selected)
                            {
                                selectColumnRange = true;
                            }

                            if (!MultiSelect || !isControlDown || isShiftDown)
                            {
                                Debug.Assert(MultiSelect || _selectedBandIndexes.Count <= 1);
                                int bandIndex = 0;
                                bool switchedToBulkPaint = false;
                                if (_selectedBandIndexes.Count > BulkPaintThreshold)
                                {
                                    _inBulkPaintCount++;
                                    switchedToBulkPaint = true;
                                }

                                try
                                {
                                    while (bandIndex < _selectedBandIndexes.Count)
                                    {
                                        if (_selectedBandIndexes[bandIndex] != hti._col)
                                        {
                                            // deselect currently selected column
                                            SetSelectedColumnCore(_selectedBandIndexes[bandIndex], false);
                                        }
                                        else
                                        {
                                            bandIndex++;
                                        }
                                    }
                                }
                                finally
                                {
                                    if (switchedToBulkPaint)
                                    {
                                        ExitBulkPaint(-1, -1);
                                    }
                                }
                            }

                            if (MultiSelect && _dataGridViewOper[OperationTrackMouseMoves])
                            {
                                _dataGridViewOper[OperationTrackColSelect] = true;
                            }

                            if (selectColumnRange)
                            {
                                if (Columns.DisplayInOrder(_ptAnchorCell.X, hti._col))
                                {
                                    SelectColumnRange(_ptAnchorCell.X, hti._col, true);
                                }
                                else
                                {
                                    SelectColumnRange(hti._col, _ptAnchorCell.X, true);
                                }
                            }
                            else if (!_selectedBandIndexes.Contains(hti._col))
                            {
                                SetSelectedColumnCore(hti._col, true);
                            }
                        }
                        else
                        {
                            Debug.Assert(_selectedBandIndexes.Contains(hti._col));
                            SetSelectedColumnCore(hti._col, false);
                        }

                        bool success = SetCurrentCellAddressCore(
                            hti._col,
                            hti._row,
                            !isShiftDown,
                            validateCurrentCell: false,
                            throughMouseClick: true);
                        Debug.Assert(success);
                        break;
                    }

                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    {
                        if (isControlDown
                            && (Columns[hti._col].Selected || IsSharedCellSelected(Rows.SharedRow(hti._row).Cells[hti._col], hti._row))
                            && (!isShiftDown || !MultiSelect))
                        {
                            select = false;
                        }

                        if (select)
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                if (_selectedBandIndexes.Count > 0)
                                {
                                    SetSelectedColumnCore(_selectedBandIndexes.HeadInt, false);
                                }
                                else
                                {
                                    RemoveIndividuallySelectedCells();
                                }

                                SetSelectedCellCore(hti._col, hti._row, true);
                            }
                            else
                            {
                                if (!isControlDown && !isShiftDown)
                                {
                                    bool switchedToBulkPaint = false;
                                    if (_selectedBandIndexes.Count > BulkPaintThreshold)
                                    {
                                        _inBulkPaintCount++;
                                        switchedToBulkPaint = true;
                                    }

                                    try
                                    {
                                        while (_selectedBandIndexes.Count > 0)
                                        {
                                            SetSelectedColumnCore(_selectedBandIndexes.HeadInt, false);
                                        }

                                        RemoveIndividuallySelectedCells(hti._col, hti._row);
                                    }
                                    finally
                                    {
                                        if (switchedToBulkPaint)
                                        {
                                            ExitBulkPaint(-1, -1);
                                        }
                                    }
                                }

                                if (_dataGridViewOper[OperationTrackMouseMoves])
                                {
                                    _dataGridViewOper[OperationTrackCellSelect] = true;
                                }

                                if (isShiftDown)
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        hti._col,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        hti._row);
                                }
                                else
                                {
                                    SetSelectedCellCore(hti._col, hti._row, true);
                                }
                            }
                        }
                        else
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                if (_selectedBandIndexes.Count > 0)
                                {
                                    SetSelectedColumnCore(_selectedBandIndexes.HeadInt, false);
                                }
                                else
                                {
                                    SetSelectedCellCore(hti._col, hti._row, false);
                                }
                            }
                            else
                            {
                                SetSelectedCellCore(hti._col, hti._row, false);
                            }
                        }

                        bool success = SetCurrentCellAddressCore(
                            hti._col,
                            hti._row,
                            !isShiftDown,
                            validateCurrentCell: false,
                            throughMouseClick: true);
                        Debug.Assert(success);
                        break;
                    }

                case DataGridViewSelectionMode.FullRowSelect:
                    {
                        if (isControlDown && ((Rows.GetRowState(hti._row) & DataGridViewElementStates.Selected) != 0))
                        {
                            select = false;
                        }

                        if (select)
                        {
                            bool selectRowRange = false;
                            _trackRow = hti._row;
                            _trackRowEdge = -1;
                            if (MultiSelect
                                && isShiftDown
                                && _ptAnchorCell.Y > -1 && (Rows.GetRowState(_ptAnchorCell.Y) & DataGridViewElementStates.Selected) != 0)
                            {
                                selectRowRange = true;
                            }

                            if (!MultiSelect || !isControlDown || isShiftDown)
                            {
                                Debug.Assert(MultiSelect || _selectedBandIndexes.Count <= 1);
                                int bandIndex = 0;
                                bool switchedToBulkPaint = false;
                                if (_selectedBandIndexes.Count > BulkPaintThreshold)
                                {
                                    _inBulkPaintCount++;
                                    switchedToBulkPaint = true;
                                }

                                try
                                {
                                    while (bandIndex < _selectedBandIndexes.Count)
                                    {
                                        if (_selectedBandIndexes[bandIndex] != hti._row)
                                        {
                                            // deselect currently selected row
                                            SetSelectedRowCore(_selectedBandIndexes[bandIndex], false);
                                        }
                                        else
                                        {
                                            bandIndex++;
                                        }
                                    }
                                }
                                finally
                                {
                                    if (switchedToBulkPaint)
                                    {
                                        ExitBulkPaint(-1, -1);
                                    }
                                }
                            }

                            if (MultiSelect && _dataGridViewOper[OperationTrackMouseMoves])
                            {
                                _dataGridViewOper[OperationTrackRowSelect] = true;
                            }

                            if (selectRowRange)
                            {
                                if (hti._row >= _ptAnchorCell.Y)
                                {
                                    SelectRowRange(_ptAnchorCell.Y, hti._row, true);
                                }
                                else
                                {
                                    SelectRowRange(hti._row, _ptAnchorCell.Y, true);
                                }
                            }
                            else if ((Rows.GetRowState(hti._row) & DataGridViewElementStates.Selected) == 0)
                            {
                                Debug.Assert(_selectedBandIndexes.Contains(hti._row) ==
                                             ((Rows.GetRowState(hti._row) & DataGridViewElementStates.Selected) != 0));
                                SetSelectedRowCore(hti._row, true);
                            }
                        }
                        else
                        {
                            Debug.Assert(_selectedBandIndexes.Contains(hti._row));
                            SetSelectedRowCore(hti._row, false);
                        }

                        bool success = SetCurrentCellAddressCore(
                            hti._col,
                            hti._row,
                            !isShiftDown,
                            validateCurrentCell: false,
                            throughMouseClick: true);
                        Debug.Assert(success);
                        break;
                    }

                case DataGridViewSelectionMode.RowHeaderSelect:
                    {
                        if (isControlDown
                            && (((Rows.GetRowState(hti._row) & DataGridViewElementStates.Selected) != 0)
                                || IsSharedCellSelected(Rows.SharedRow(hti._row).Cells[hti._col], hti._row))
                            && (!isShiftDown || !MultiSelect))
                        {
                            select = false;
                        }

                        if (select)
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                if (_selectedBandIndexes.Count > 0)
                                {
                                    SetSelectedRowCore(_selectedBandIndexes.HeadInt, false);
                                }
                                else
                                {
                                    RemoveIndividuallySelectedCells();
                                }

                                SetSelectedCellCore(hti._col, hti._row, true);
                            }
                            else
                            {
                                if (!isControlDown && !isShiftDown)
                                {
                                    bool switchedToBulkPaint = false;
                                    if (_selectedBandIndexes.Count > BulkPaintThreshold)
                                    {
                                        _inBulkPaintCount++;
                                        switchedToBulkPaint = true;
                                    }

                                    try
                                    {
                                        while (_selectedBandIndexes.Count > 0)
                                        {
                                            SetSelectedRowCore(_selectedBandIndexes.HeadInt, false);
                                        }

                                        RemoveIndividuallySelectedCells(hti._col, hti._row);
                                    }
                                    finally
                                    {
                                        if (switchedToBulkPaint)
                                        {
                                            ExitBulkPaint(-1, -1);
                                        }
                                    }
                                }

                                if (_dataGridViewOper[OperationTrackMouseMoves])
                                {
                                    _dataGridViewOper[OperationTrackCellSelect] = true;
                                }

                                if (isShiftDown)
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        hti._col,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        hti._row);
                                }
                                else
                                {
                                    SetSelectedCellCore(hti._col, hti._row, selected: true);
                                }
                            }
                        }
                        else
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                if (_selectedBandIndexes.Count > 0)
                                {
                                    SetSelectedRowCore(_selectedBandIndexes.HeadInt, selected: false);
                                }
                                else
                                {
                                    SetSelectedCellCore(hti._col, hti._row, selected: false);
                                }
                            }
                            else
                            {
                                SetSelectedCellCore(hti._col, hti._row, false);
                            }
                        }

                        SetCurrentCellAddressCore(
                            hti._col,
                            hti._row,
                            !isShiftDown,
                            validateCurrentCell: false,
                            throughMouseClick: true);
                        break;
                    }
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }
    }

    protected virtual void OnCellMouseEnter(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        _ptMouseEnteredCell.X = e.ColumnIndex;
        _ptMouseEnteredCell.Y = e.RowIndex;

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.MouseEnterUnsharesRowInternal(e.RowIndex))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseEnterInternal(e.RowIndex);
        }
        else
        {
            dataGridViewCell.OnMouseEnterInternal(e.RowIndex);
        }

        GetEvent<DataGridViewCellEventHandler>(s_cellMouseEnterEvent)?.Invoke(this, e);
    }

    protected virtual void OnCellMouseLeave(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        _ptMouseEnteredCell.X = -2;
        _ptMouseEnteredCell.Y = -2;

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.MouseLeaveUnsharesRowInternal(e.RowIndex))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseLeaveInternal(e.RowIndex);
        }
        else
        {
            dataGridViewCell.OnMouseLeaveInternal(e.RowIndex);
        }

        GetEvent<DataGridViewCellEventHandler>(s_cellMouseLeaveEvent)?.Invoke(this, e);
    }

    protected virtual void OnCellMouseMove(DataGridViewCellMouseEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.MouseMoveUnsharesRowInternal(e))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseMoveInternal(e);
        }
        else
        {
            dataGridViewCell.OnMouseMoveInternal(e);
        }

        GetEvent<DataGridViewCellMouseEventHandler>(s_cellMouseMoveEvent)?.Invoke(this, e);

        if (!_dataGridViewState1[State1_ScrolledSinceMouseDown]
            && !IsMouseOperationActive()
            && AllowUserToOrderColumns
            && SelectionMode != DataGridViewSelectionMode.FullColumnSelect
            && SelectionMode != DataGridViewSelectionMode.ColumnHeaderSelect
            && e.Button == MouseButtons.Left
            && _ptMouseDownCell.Y == -1
            && _ptMouseDownCell.X >= 0
            && _ptMouseDownCell.X < Columns.Count)
        {
            Point ptGridCoord = ConvertCellToGridCoord(e.ColumnIndex, e.RowIndex, e.X, e.Y);

            HitTestInfo hti = HitTest(ptGridCoord.X, ptGridCoord.Y);

            Debug.Assert(hti.Type is not DataGridViewHitTestType.None
                and not DataGridViewHitTestType.HorizontalScrollBar
                and not DataGridViewHitTestType.VerticalScrollBar);

            switch (hti._typeInternal)
            {
                // Check for column header mouse down
                case DataGridViewHitTestTypeInternal.ColumnHeader:
                case DataGridViewHitTestTypeInternal.ColumnHeaderLeft:
                case DataGridViewHitTestTypeInternal.ColumnHeaderRight:
                case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft:
                    {
                        Debug.Assert(!_dataGridViewState2[State2_MessageFromEditingCtrls]);
                        if (Math.Abs(_ptMouseDownGridCoord.X - ptGridCoord.X) >= s_dragSize.Width
                            || Math.Abs(_ptMouseDownGridCoord.Y - ptGridCoord.Y) >= s_dragSize.Height)
                        {
                            BeginColumnRelocation(_ptMouseDownGridCoord.X, _ptMouseDownCell.X);
                        }

                        break;
                    }
            }
        }
    }

    protected virtual void OnCellMouseUp(DataGridViewCellMouseEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        DataGridViewCell dataGridViewCell = GetCellInternal(e.ColumnIndex, e.RowIndex);
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        if (e.RowIndex >= 0 && dataGridViewCell.MouseUpUnsharesRowInternal(e))
        {
            // Accessing the indexer unshares the row
            _ = Rows[e.RowIndex];
            GetCellInternal(e.ColumnIndex, e.RowIndex).OnMouseUpInternal(e);
        }
        else
        {
            dataGridViewCell.OnMouseUpInternal(e);
        }

        GetEvent<DataGridViewCellMouseEventHandler>(s_cellMouseUpEvent)?.Invoke(this, e);
    }

    protected internal virtual void OnCellPainting(DataGridViewCellPaintingEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellPaintingEventHandler>(s_cellPaintingEvent)?.Invoke(this, e);
    }

    internal DataGridViewCellParsingEventArgs OnCellParsing(
        int rowIndex,
        int columnIndex,
        object? formattedValue,
        Type? valueType,
        DataGridViewCellStyle? cellStyle)
    {
        DataGridViewCellParsingEventArgs dgvcpe = new(
            rowIndex,
            columnIndex,
            formattedValue,
            valueType,
            cellStyle);

        OnCellParsing(dgvcpe);
        return dgvcpe;
    }

    protected virtual void OnCellParsing(DataGridViewCellParsingEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellParsingEventHandler>(s_cellParsingEvent)?.Invoke(this, e);
    }

    private void OnCellSelectMouseMove(HitTestInfo hti)
    {
        Debug.Assert(MultiSelect);
        int oldEdgeColumnIndex = _ptCurrentCell.X;
        int oldEdgeRowIndex = _ptCurrentCell.Y;
        if ((hti._col != _ptCurrentCell.X || hti._row != _ptCurrentCell.Y)
            && !CommitEditForOperation(hti._col, hti._row, forCurrentCellChange: true))
        {
            // Return silently if validating/commit/abort failed
            return;
        }

        _noSelectionChangeCount++;
        try
        {
            if (_ptAnchorCell.X == -1 || IsInnerCellOutOfBounds(hti._col, hti._row))
            {
                return;
            }

            UpdateSelectedCellsBlock(
                _ptAnchorCell.X,
                ref oldEdgeColumnIndex,
                hti._col,
                _ptAnchorCell.Y,
                ref oldEdgeRowIndex,
                hti._row);

            if (hti._col != _ptCurrentCell.X || hti._row != _ptCurrentCell.Y)
            {
                bool success = SetCurrentCellAddressCore(
                    hti._col,
                    hti._row,
                    setAnchorCellAddress: false,
                    validateCurrentCell: false,
                    throughMouseClick: false);
                Debug.Assert(success);
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }
    }

    protected virtual void OnCellStateChanged(DataGridViewCellStateChangedEventArgs e)
    {
        // At this point we assume that only the Selected state has an influence on the rendering of the cell.
        // If there is a scenario where another state has an effect, then the dev will have to invalidate the cell by hand.
        DataGridViewCell dataGridViewCell = e.Cell;
        if (e.StateChanged == DataGridViewElementStates.Selected)
        {
            Debug.Assert(dataGridViewCell.RowIndex >= 0);
            if (_inBulkPaintCount == 0)
            {
                InvalidateCellPrivate(dataGridViewCell);
            }
        }

        GetEvent<DataGridViewCellStateChangedEventHandler>(s_cellStateChangedEvent)?.Invoke(this, e);

        if (e.StateChanged == DataGridViewElementStates.ReadOnly
            && _ptCurrentCell.X == dataGridViewCell.ColumnIndex
            && _ptCurrentCell.Y == dataGridViewCell.RowIndex
            && dataGridViewCell.RowIndex > -1
            && !_dataGridViewOper[OperationInReadOnlyChange])
        {
            VerifyImeRestrictedModeChanged();

            if (!dataGridViewCell.ReadOnly
                && ColumnEditable(_ptCurrentCell.X)
                && !IsCurrentCellInEditMode
                && (EditMode == DataGridViewEditMode.EditOnEnter
                    || (EditMode != DataGridViewEditMode.EditProgrammatically && CurrentCellInternal.EditType is null)))
            {
                // Current cell becomes read/write. Enter editing mode.
                BeginEditInternal(selectAll: true);
            }
        }
    }

    internal void OnCellStyleChanged(DataGridViewCell dataGridViewCell)
    {
        DataGridViewCellEventArgs dgvce = new(dataGridViewCell);
        OnCellStyleChanged(dgvce);
    }

    protected virtual void OnCellStyleChanged(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        OnCellCommonChange(e.ColumnIndex, e.RowIndex);
        GetEvent<DataGridViewCellEventHandler>(s_cellStyleChangedEvent)?.Invoke(this, e);
    }

    internal void OnCellStyleContentChanged(DataGridViewCellStyle dataGridViewCellStyle, DataGridViewCellStyle.DataGridViewCellStylePropertyInternal property)
    {
        Debug.Assert(dataGridViewCellStyle is not null);
        switch (property)
        {
            case DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.Font:
                if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.DataGridView) != 0 && _dataGridViewState1[State1_AmbientFont])
                {
                    _dataGridViewState1[State1_AmbientFont] = false;
                }

                if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.ColumnHeaders) != 0 && _dataGridViewState1[State1_AmbientColumnHeadersFont])
                {
                    _dataGridViewState1[State1_AmbientColumnHeadersFont] = false;
                }

                if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.RowHeaders) != 0 && _dataGridViewState1[State1_AmbientRowHeadersFont])
                {
                    _dataGridViewState1[State1_AmbientRowHeadersFont] = false;
                }

                break;

            case DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.ForeColor:
                if ((dataGridViewCellStyle.Scope & DataGridViewCellStyleScopes.DataGridView) != 0 && _dataGridViewState1[State1_AmbientForeColor])
                {
                    _dataGridViewState1[State1_AmbientForeColor] = false;
                }

                break;
        }

        DataGridViewCellStyleContentChangedEventArgs dgvcscce = new(
            dataGridViewCellStyle,
            changeAffectsPreferredSize: property is not DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.Color
                and not DataGridViewCellStyle.DataGridViewCellStylePropertyInternal.ForeColor);

        OnCellStyleContentChanged(dgvcscce);
    }

    protected virtual void OnCellStyleContentChanged(DataGridViewCellStyleContentChangedEventArgs e)
    {
        // We assume that when a color changes, it has no effect on the autosize of elements
        bool repositionEditingControl = false;

        if ((e.CellStyleScope & DataGridViewCellStyleScopes.Cell) == DataGridViewCellStyleScopes.Cell && (e.CellStyleScope & DataGridViewCellStyleScopes.DataGridView) == 0)
        {
            // Same processing as in OnDefaultCellStyleChanged
            if (e.ChangeAffectsPreferredSize)
            {
                repositionEditingControl = true;
                OnGlobalAutoSize();
            }
            else
            {
                Invalidate();
            }
        }

        if ((e.CellStyleScope & DataGridViewCellStyleScopes.Column) == DataGridViewCellStyleScopes.Column)
        {
            if (e.ChangeAffectsPreferredSize)
            {
                repositionEditingControl = true;
                OnColumnsGlobalAutoSize();
            }
            else
            {
                InvalidateData();
            }
        }

        if ((e.CellStyleScope & DataGridViewCellStyleScopes.Row) == DataGridViewCellStyleScopes.Row && (e.CellStyleScope & DataGridViewCellStyleScopes.Rows) == 0 && (e.CellStyleScope & DataGridViewCellStyleScopes.AlternatingRows) == 0)
        {
            // Same processing as in OnRowsDefaultCellStyleChanged
            InvalidateData();
            if (e.ChangeAffectsPreferredSize)
            {
                repositionEditingControl = true;
                // Autosize rows if needed
                if ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0)
                {
                    AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: false, internalAutosizing: true);
                }

                // Auto size columns also if needed
                // Impossible to figure out if DisplayedRows filter should be added or not. Adding it to be on the conservative side.
                AutoResizeAllVisibleColumnsInternal(
                    DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows,
                    fixedHeight: true);

                // Second round of rows autosizing
                if ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0)
                {
                    AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
                }
            }
        }

        if ((e.CellStyleScope & DataGridViewCellStyleScopes.DataGridView) == DataGridViewCellStyleScopes.DataGridView)
        {
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize;
            if (e.ChangeAffectsPreferredSize)
            {
                repositionEditingControl = false;
                // OnDefaultCellStyleChanged will reposition the editing control.
            }

            OnDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }

        if ((e.CellStyleScope & DataGridViewCellStyleScopes.ColumnHeaders) == DataGridViewCellStyleScopes.ColumnHeaders)
        {
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize;
            if (e.ChangeAffectsPreferredSize)
            {
                repositionEditingControl = false;
                // OnColumnHeadersDefaultCellStyleChanged will reposition the editing control.
            }

            OnColumnHeadersDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }

        if ((e.CellStyleScope & DataGridViewCellStyleScopes.RowHeaders) == DataGridViewCellStyleScopes.RowHeaders)
        {
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize;
            if (e.ChangeAffectsPreferredSize)
            {
                repositionEditingControl = false;
                // OnRowHeadersDefaultCellStyleChanged will reposition the editing control.
            }

            OnRowHeadersDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }

        if ((e.CellStyleScope & DataGridViewCellStyleScopes.Rows) == DataGridViewCellStyleScopes.Rows)
        {
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize;
            if (e.ChangeAffectsPreferredSize)
            {
                repositionEditingControl = false;
                // OnRowsDefaultCellStyleChanged will reposition the editing control.
            }

            OnRowsDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }

        if ((e.CellStyleScope & DataGridViewCellStyleScopes.AlternatingRows) == DataGridViewCellStyleScopes.AlternatingRows)
        {
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = e.ChangeAffectsPreferredSize;
            if (e.ChangeAffectsPreferredSize)
            {
                repositionEditingControl = false;
                // OnAlternatingRowsDefaultCellStyleChanged will reposition the editing control.
            }

            OnAlternatingRowsDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }

        if (repositionEditingControl && EditingControl is not null)
        {
            PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
        }

        GetEvent<DataGridViewCellStyleContentChangedEventHandler>(s_cellStyleContentChangedEvent)?.Invoke(this, e);
    }

    internal void OnCellToolTipTextChanged(DataGridViewCell dataGridViewCell)
    {
        DataGridViewCellEventArgs dgvce = new(dataGridViewCell);
        OnCellToolTipTextChanged(dgvce);
    }

    protected virtual void OnCellToolTipTextChanged(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellEventHandler>(s_cellTooltipTextChangedEvent)?.Invoke(this, e);
    }

    internal string? OnCellToolTipTextNeeded(int columnIndex, int rowIndex, string toolTipText)
    {
        DataGridViewCellToolTipTextNeededEventArgs dgvctttne = new(columnIndex, rowIndex, toolTipText);
        OnCellToolTipTextNeeded(dgvctttne);
        return dgvctttne.ToolTipText;
    }

    protected virtual void OnCellToolTipTextNeeded(DataGridViewCellToolTipTextNeededEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellToolTipTextNeededEventHandler>(s_cellTooltipTextNeededEvent)?.Invoke(this, e);
    }

    internal void OnCellValidated(ref DataGridViewCell? dataGridViewCell, int columnIndex, int rowIndex)
    {
        OnCellValidated(new DataGridViewCellEventArgs(columnIndex, rowIndex));
        if (dataGridViewCell is not null)
        {
            if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
            {
                dataGridViewCell = null;
            }
            else
            {
                Debug.Assert(rowIndex < Rows.Count && columnIndex < Columns.Count);
                dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
            }
        }
    }

    protected virtual void OnCellValidated(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        try
        {
            _noDimensionChangeCount++;
            if (GetEvent<DataGridViewCellEventHandler>(s_cellValidatedEvent) is { } eh)
            {
                eh(this, e);
                CorrectFocus(onlyIfGridHasFocus: true);
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
        }
    }

    internal bool OnCellValidating(
        ref DataGridViewCell? dataGridViewCell,
        int columnIndex,
        int rowIndex,
        DataGridViewDataErrorContexts context)
    {
        DataGridViewCell currentCell = dataGridViewCell ?? CurrentCellInternal;
        DataGridViewCellStyle dataGridViewCellStyle = currentCell.GetInheritedStyle(inheritedCellStyle: null, rowIndex, includeColors: false);
        object? val = currentCell.GetValueInternal(rowIndex);
        object? editedFormattedValue = currentCell.GetEditedFormattedValue(val, rowIndex, ref dataGridViewCellStyle, context);
        DataGridViewCellValidatingEventArgs dgvcfvce = new(columnIndex, rowIndex, editedFormattedValue);
        OnCellValidating(dgvcfvce);
        if (dataGridViewCell is not null)
        {
            if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
            {
                dataGridViewCell = null;
            }
            else
            {
                Debug.Assert(rowIndex < Rows.Count && columnIndex < Columns.Count);
                dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
            }
        }

        return dgvcfvce.Cancel;
    }

    protected virtual void OnCellValidating(DataGridViewCellValidatingEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);

        try
        {
            _noDimensionChangeCount++;
            _dataGridViewOper[OperationInCellValidating] = true;

            if (GetEvent<DataGridViewCellValidatingEventHandler>(s_cellValidatingEvent) is { } eh)
            {
                eh(this, e);
                CorrectFocus(onlyIfGridHasFocus: true);
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
            _dataGridViewOper[OperationInCellValidating] = false;
        }
    }

    internal void OnCellValueChangedInternal(DataGridViewCellEventArgs e)
    {
        // For now, same effect as if the cell style had changed.
        OnCellValueChanged(e);
    }

    protected virtual void OnCellValueChanged(DataGridViewCellEventArgs e)
    {
        CheckEventArgsIndexesUpperBounds(e);
        OnCellCommonChange(e.ColumnIndex, e.RowIndex);
        GetEvent<DataGridViewCellEventHandler>(s_cellValueChangedEvent)?.Invoke(this, e);
    }

    internal object? OnCellValueNeeded(int columnIndex, int rowIndex)
    {
        DataGridViewCellValueEventArgs dataGridViewCellValueEventArgs = CellValueEventArgs;
        dataGridViewCellValueEventArgs.SetProperties(columnIndex, rowIndex, value: null);
        OnCellValueNeeded(dataGridViewCellValueEventArgs);
        return dataGridViewCellValueEventArgs.Value;
    }

    protected virtual void OnCellValueNeeded(DataGridViewCellValueEventArgs e)
    {
        CheckEventArgsIndexesNotNegative(e);
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellValueEventHandler>(s_cellValueNeededEvent)?.Invoke(this, e);
    }

    internal void OnCellValuePushed(int columnIndex, int rowIndex, object? value)
    {
        DataGridViewCellValueEventArgs dataGridViewCellValueEventArgs = CellValueEventArgs;
        dataGridViewCellValueEventArgs.SetProperties(columnIndex, rowIndex, value);
        OnCellValuePushed(dataGridViewCellValueEventArgs);
    }

    protected virtual void OnCellValuePushed(DataGridViewCellValueEventArgs e)
    {
        CheckEventArgsIndexesNotNegative(e);
        CheckEventArgsIndexesUpperBounds(e);
        GetEvent<DataGridViewCellValueEventHandler>(s_cellValuePushedEvent)?.Invoke(this, e);
    }

    internal void OnClearedRows()
    {
        // Raise the RowStateChanged(Displayed->false) events
        foreach (DataGridViewRow dataGridViewRow in _lstRows)
        {
            if (dataGridViewRow.Displayed)
            {
                dataGridViewRow.Displayed = false;
                DataGridViewRowStateChangedEventArgs dgvrsce = new(dataGridViewRow, DataGridViewElementStates.Displayed);
                OnRowStateChanged(rowIndex: -1, dgvrsce);
            }
        }

        _lstRows.Clear();
    }

    internal void OnClearingColumns()
    {
        CurrentCell = null;

        // Rows need to be cleared first. There cannot be rows without also having columns.
        Rows.ClearInternal(recreateNewRow: false);

        // Reset sort related variables.
        SortedColumn = null;
        SortOrder = SortOrder.None;

        // selectedBandIndexes, individualSelectedCells & individualReadOnlyCells cleared in OnClearingRows.
    }

    internal void OnClearingRows()
    {
        // Build a list of displayed rows in order to be able to raise their RowStateChanged(Displayed->false) events later on
        _lstRows.Clear();
        int numDisplayedRows = DisplayedBandsInfo.NumDisplayedFrozenRows + DisplayedBandsInfo.NumDisplayedScrollingRows;
        if (numDisplayedRows > 0)
        {
            _lstRows.Capacity = numDisplayedRows;
            int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Displayed);
            while (numDisplayedRows > 0 && rowIndex != -1)
            {
                _lstRows.Add(Rows[rowIndex]);
                numDisplayedRows--;
                if (numDisplayedRows > 0)
                {
                    rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Displayed);
                }
            }
        }

        CurrentCell = null;

        NewRowIndex = -1;
        _dataGridViewState2[State2_RaiseSelectionChanged] = _selectedBandIndexes.Count > 0
            || _individualSelectedCells.Count > 0;
        _selectedBandIndexes.Clear();
        _selectedBandSnapshotIndexes?.Clear();

        _individualSelectedCells.Clear();
        _individualReadOnlyCells.Clear();
    }

    protected virtual void OnColumnAdded(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        GetEvent<DataGridViewColumnEventHandler>(s_columnAddedEvent)?.Invoke(this, e);
    }

    internal void OnColumnCollectionChanged_PreNotification(CollectionChangeEventArgs ccea)
    {
        // we need to map columns w/ DataPropertyName to bound columns
        if (DataSource is not null && !_dataGridViewOper[OperationInRefreshColumns])
        {
            if (ccea.Action == CollectionChangeAction.Add)
            {
                DataGridViewColumn dataGridViewColumn = (DataGridViewColumn)ccea.Element!;
                if (dataGridViewColumn.DataPropertyName.Length != 0)
                {
                    MapDataGridViewColumnToDataBoundField(dataGridViewColumn);
                }
            }
            else if (ccea.Action == CollectionChangeAction.Refresh)
            {
                for (int i = 0; i < Columns.Count; i++)
                {
                    if (Columns[i].DataPropertyName.Length != 0)
                    {
                        MapDataGridViewColumnToDataBoundField(Columns[i]);
                    }
                }
            }
        }

        ResetUIState(useRowShortcut: false, computeVisibleRows: false);
    }

    internal void OnColumnCollectionChanged_PostNotification(DataGridViewColumn? dataGridViewColumn)
    {
        if (Columns.Count != 0 && Rows.Count == 0)
        {
            if (DataSource is not null && !_dataGridViewOper[OperationInRefreshColumns])
            {
                // this will create the 'add new row' when AllowUserToAddRowsInternal == true
                RefreshRows(scrollIntoView: true);
            }
            else if (AllowUserToAddRowsInternal)
            {
                AddNewRow(createdByEditing: false);
            }
        }

        if (AutoSize && (dataGridViewColumn is null || dataGridViewColumn.Visible))
        {
            LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Columns);
        }
    }

    internal void OnColumnCommonChange(int columnIndex)
    {
        OnColumnGlobalAutoSize(columnIndex);
    }

    protected virtual void OnColumnContextMenuStripChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        GetEvent<DataGridViewColumnEventHandler>(s_columnContextMenuStripChangedEvent)?.Invoke(this, e);
    }

    internal void OnColumnDataPropertyNameChanged(DataGridViewColumn dataGridViewColumn)
    {
        OnColumnDataPropertyNameChanged(new DataGridViewColumnEventArgs(dataGridViewColumn));
    }

    protected virtual void OnColumnDataPropertyNameChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        // map the dataGridView column to some data field
        if (DataSource is not null && e.Column.DataPropertyName.Length != 0 && !_dataGridViewOper[OperationInRefreshColumns])
        {
            MapDataGridViewColumnToDataBoundField(e.Column);
        }
        else if (DataSource is not null && e.Column.DataPropertyName.Length == 0)
        {
            if (e.Column.IsDataBound)
            {
                e.Column.IsDataBoundInternal = false;
                e.Column.BoundColumnIndex = -1;
                e.Column.BoundColumnConverter = null;
                InvalidateColumnInternal(e.Column.Index);
            }
        }

        GetEvent<DataGridViewColumnEventHandler>(s_columnDataPropertyNameChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnColumnDefaultCellStyleChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        OnColumnGlobalAutoSize(e.Column.Index);
        GetEvent<DataGridViewColumnEventHandler>(s_columnDefaultCellStyleChangedEvent)?.Invoke(this, e);
    }

    internal void OnColumnDisplayIndexChanged(DataGridViewColumn dataGridViewColumn)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
        OnColumnDisplayIndexChanged(dgvce);
    }

    internal void OnColumnDisplayIndexChanging(DataGridViewColumn dataGridViewColumn, int newDisplayIndex)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(newDisplayIndex != dataGridViewColumn.DisplayIndex);

        if (_dataGridViewOper[OperationInDisplayIndexAdjustments])
        {
            // We are within columns display indexes adjustments. We do not allow changing display indexes while adjusting them.
            throw new InvalidOperationException(SR.DataGridView_CannotAlterDisplayIndexWithinAdjustments);
        }

        // Throws an exception if the requested move is illegal
        CorrectColumnFrozenStatesForMove(dataGridViewColumn, newDisplayIndex);

        try
        {
            _dataGridViewOper[OperationInDisplayIndexAdjustments] = true;

            // Move is legal - let's adjust the affected display indexes.
            if (newDisplayIndex < dataGridViewColumn.DisplayIndex)
            {
                // DisplayIndex decreases. All columns with newDisplayIndex <= DisplayIndex < dataGridViewColumn.DisplayIndex
                // get their DisplayIndex incremented.
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (newDisplayIndex <= dataGridViewColumnTmp.DisplayIndex && dataGridViewColumnTmp.DisplayIndex < dataGridViewColumn.DisplayIndex)
                    {
                        dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex + 1;
                        dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on
                    }
                }
            }
            else
            {
                // DisplayIndex increases. All columns with dataGridViewColumn.DisplayIndex < DisplayIndex <= newDisplayIndex
                // get their DisplayIndex incremented.
                foreach (DataGridViewColumn dataGridViewColumnTmp in Columns)
                {
                    if (dataGridViewColumn.DisplayIndex < dataGridViewColumnTmp.DisplayIndex && dataGridViewColumnTmp.DisplayIndex <= newDisplayIndex)
                    {
                        dataGridViewColumnTmp.DisplayIndexInternal = dataGridViewColumnTmp.DisplayIndex - 1;
                        dataGridViewColumnTmp.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on
                    }
                }
            }
        }
        finally
        {
            _dataGridViewOper[OperationInDisplayIndexAdjustments] = false;
        }

        // Note that displayIndex of moved column is updated by caller.
    }

    protected virtual void OnColumnDisplayIndexChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        Debug.Assert(_dataGridViewOper[OperationInDisplayIndexAdjustments]);
#if DEBUG
        Debug.Assert(Columns.VerifyColumnDisplayIndexes());
#endif

        GetEvent<DataGridViewColumnEventHandler>(s_columnDisplayIndexChangedEvent)?.Invoke(this, e);
    }

    internal void OnColumnDisplayIndexChanged_PreNotification()
    {
        Debug.Assert(_dataGridViewOper[OperationInDisplayIndexAdjustments]);

        // column.DisplayIndex changed - this may require a complete re-layout of the control
        Columns.InvalidateCachedColumnsOrder();

        PerformLayoutPrivate(
            useRowShortcut: false,
            computeVisibleRows: false,
            invalidInAdjustFillingColumns: true,
            repositionEditingControl: false);

        if (EditingControl is not null)
        {
            PositionEditingControl(true, true, false);
        }

        Invalidate(Rectangle.Union(_layout.ColumnHeaders, _layout.Data));
    }

    internal void OnColumnDisplayIndexChanged_PostNotification()
    {
        // Notifications for adjusted display indexes.
        FlushDisplayIndexChanged(raiseEvent: true);
    }

    protected virtual void OnColumnDividerDoubleClick(DataGridViewColumnDividerDoubleClickEventArgs e)
    {
        GetEvent<DataGridViewColumnDividerDoubleClickEventHandler>(s_columnDividerDoubleClickEvent)?.Invoke(this, e);

        if (!e.Handled && e.Button == MouseButtons.Left && e.ColumnIndex < Columns.Count)
        {
            if (e.ColumnIndex == -1)
            {
                AutoResizeRowHeadersWidth(
                    DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders,
                    fixedColumnHeadersHeight: true,
                    fixedRowsHeight: true);
            }
            else
            {
                DataGridViewAutoSizeColumnMode inheritedAutoSizeMode = Columns[e.ColumnIndex].InheritedAutoSizeMode;
                if (inheritedAutoSizeMode is DataGridViewAutoSizeColumnMode.None or DataGridViewAutoSizeColumnMode.Fill)
                {
                    AutoResizeColumnInternal(
                        e.ColumnIndex,
                        DataGridViewAutoSizeColumnCriteriaInternal.Header | DataGridViewAutoSizeColumnCriteriaInternal.AllRows,
                        fixedHeight: true);
                }
                else
                {
                    AutoResizeColumnInternal(
                        e.ColumnIndex,
                        (DataGridViewAutoSizeColumnCriteriaInternal)inheritedAutoSizeMode,
                        fixedHeight: true);
                }
            }
        }
    }

    protected virtual void OnColumnDividerWidthChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        OnColumnGlobalAutoSize(e.Column.Index);
        GetEvent<DataGridViewColumnEventHandler>(s_columnDividerWidthChangedEvent)?.Invoke(this, e);
    }

    internal void OnColumnFillWeightChanged(DataGridViewColumn dataGridViewColumn)
    {
        if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
        {
            // UsedFillWeight properties need to be re-evaluated
            _dataGridViewState2[State2_UsedFillWeightsDirty] = true;

            // Adjust filling columns based on new weight of this column
            PerformLayoutPrivate(
                useRowShortcut: false,
                computeVisibleRows: true,
                invalidInAdjustFillingColumns: false,
                repositionEditingControl: false);
        }
    }

    internal void OnColumnFillWeightChanging(DataGridViewColumn dataGridViewColumn, float fillWeight)
    {
        if (InAdjustFillingColumns)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotAlterAutoFillColumnParameter);
        }

        // Make sure the sum of the column weights does not exceed ushort.MaxValue
        float weightSum = Columns.GetColumnsFillWeight(DataGridViewElementStates.None) - dataGridViewColumn.FillWeight + fillWeight;
        if (weightSum > ushort.MaxValue)
        {
            throw new InvalidOperationException(string.Format(SR.DataGridView_WeightSumCannotExceedLongMaxValue, ushort.MaxValue));
        }
    }

    private void OnColumnGlobalAutoSize(int columnIndex)
    {
        Debug.Assert(columnIndex >= 0 && columnIndex < Columns.Count);

        if (!Columns[columnIndex].Visible)
        {
            return;
        }

        InvalidateColumnInternal(columnIndex);

        if (_noAutoSizeCount > 0)
        {
            return;
        }

        bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0;

        DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)Columns[columnIndex].InheritedAutoSizeMode;
        if (autoSizeColumnCriteriaInternal is not DataGridViewAutoSizeColumnCriteriaInternal.None
            and not DataGridViewAutoSizeColumnCriteriaInternal.Fill)
        {
            AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight);
        }

        // Autosize rows and column headers if needed
        if (!fixedHeight)
        {
            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
        }

        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
        {
            AutoResizeColumnHeadersHeight(columnIndex, fixedRowHeadersWidth: true, fixedColumnWidth: true);
        }

        if (!fixedHeight
            && autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.None
            && autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Fill)
        {
            // Second round of column autosizing with 1 degree of freedom
            AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight: true);
        }
    }

    protected virtual void OnColumnHeaderCellChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        OnColumnHeaderGlobalAutoSize(e.Column.Index);
        GetEvent<DataGridViewColumnEventHandler>(s_columnHeaderCellChangedEvent)?.Invoke(this, e);
    }

    private void OnColumnHeaderGlobalAutoSize(int columnIndex)
    {
        if (!ColumnHeadersVisible)
        {
            return;
        }

        InvalidateCellPrivate(columnIndex, -1);

        if (_noAutoSizeCount > 0)
        {
            return;
        }

        DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)Columns[columnIndex].InheritedAutoSizeMode;
        DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header;
        bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0;

        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
        {
            AutoResizeColumnHeadersHeight(columnIndex, fixedRowHeadersWidth: true, fixedColumnWidth);
        }

        if (!fixedColumnWidth)
        {
            Debug.Assert(autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.None);
            Debug.Assert(autoSizeColumnCriteriaInternal != DataGridViewAutoSizeColumnCriteriaInternal.Fill);
            bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0;
            AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight);
            if (!fixedHeight)
            {
                AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);

                // Second round of column autosizing with 1 degree of freedom
                AutoResizeColumnInternal(columnIndex, autoSizeColumnCriteriaInternal, fixedHeight: true);
            }

            if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
            {
                // Second round of column headers autosizing with 1 degree of freedom
                AutoResizeColumnHeadersHeight(columnIndex, fixedRowHeadersWidth: true, fixedColumnWidth: true);
            }
        }
    }

    protected virtual void OnColumnHeaderMouseClick(DataGridViewCellMouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left
            && SelectionMode != DataGridViewSelectionMode.FullColumnSelect
            && SelectionMode != DataGridViewSelectionMode.ColumnHeaderSelect)
        {
            DataGridViewColumn dataGridViewColumn = Columns[e.ColumnIndex];

            if (CanSort(dataGridViewColumn))
            {
                ListSortDirection direction = ListSortDirection.Ascending;

                if (SortedColumn == dataGridViewColumn)
                {
                    Debug.Assert(SortOrder != SortOrder.None);
                    if (SortOrder == SortOrder.Ascending)
                    {
                        direction = ListSortDirection.Descending;
                    }
                }

                if ((DataSource is null)
                    || (DataSource is not null
                        && (DataConnection!.List is IBindingList)
                        && ((IBindingList)DataConnection.List).SupportsSorting
                        && dataGridViewColumn.IsDataBound))
                {
                    Sort(dataGridViewColumn, direction);
                }
            }
        }

        GetEvent<DataGridViewCellMouseEventHandler>(s_columnHeaderMouseClickEvent)?.Invoke(this, e);
    }

    protected virtual void OnColumnHeaderMouseDoubleClick(DataGridViewCellMouseEventArgs e) =>
        GetEvent<DataGridViewCellMouseEventHandler>(s_columnHeaderMouseDoubleClickEvent)?.Invoke(this, e);

    private void OnColumnHeaderMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown)
    {
        Debug.Assert(hti.Type == DataGridViewHitTestType.ColumnHeader);
        _noSelectionChangeCount++;

        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.FullRowSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    break;

                case DataGridViewSelectionMode.FullColumnSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    {
                        bool select = true;
                        if (isControlDown && Columns[hti._col].Selected)
                        {
                            select = false;
                        }

                        if (select)
                        {
                            int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                            if (rowIndex > -1 && hti._col != _ptCurrentCell.X)
                            {
                                // Make sure we will be able to scroll into view
                                int oldCurrentCellX = _ptCurrentCell.X;
                                int oldCurrentCellY = _ptCurrentCell.Y;
                                if (!EndEdit(
                                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange,
                                    validateCell: DataGridViewValidateCellInternal.Always,
                                    fireCellLeave: true,
                                    fireCellEnter: true,
                                    fireRowLeave: rowIndex != _ptCurrentCell.Y,
                                    fireRowEnter: rowIndex != _ptCurrentCell.Y,
                                    fireLeave: false,
                                    keepFocus: EditMode != DataGridViewEditMode.EditOnEnter,
                                    resetCurrentCell: true,
                                    resetAnchorCell: false))
                                {
                                    // Just cancel operation silently instead of throwing InvalidOperationException
                                    return;
                                }

                                if (rowIndex != oldCurrentCellY && oldCurrentCellY != -1)
                                {
                                    DataGridViewCell? dataGridViewCellTmp = null;
                                    if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                    {
                                        return;
                                    }

                                    if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY))
                                    {
                                        // Row validation was cancelled
                                        if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                        {
                                            return;
                                        }

                                        OnRowEnter(
                                            ref dataGridViewCellTmp,
                                            oldCurrentCellX,
                                            oldCurrentCellY,
                                            canCreateNewRow: true,
                                            validationFailureOccurred: true);
                                        if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                        {
                                            return;
                                        }

                                        OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY);
                                        return;
                                    }

                                    if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                    {
                                        return;
                                    }

                                    OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY);
                                }
                            }

                            if (IsColumnOutOfBounds(hti._col))
                            {
                                return;
                            }

                            bool selectColumnRange = false;
                            _trackColumn = hti._col;
                            _trackColumnEdge = -1;
                            if (MultiSelect && isShiftDown && _ptAnchorCell.X > -1 && Columns[_ptAnchorCell.X].Selected)
                            {
                                selectColumnRange = true;
                            }

                            if (!MultiSelect || !isControlDown || isShiftDown)
                            {
                                Debug.Assert(MultiSelect || _selectedBandIndexes.Count <= 1);
                                int bandIndex = 0;
                                bool switchedToBulkPaint = false;
                                if (_selectedBandIndexes.Count > BulkPaintThreshold)
                                {
                                    _inBulkPaintCount++;
                                    switchedToBulkPaint = true;
                                }

                                try
                                {
                                    while (bandIndex < _selectedBandIndexes.Count)
                                    {
                                        if (_selectedBandIndexes[bandIndex] != hti._col)
                                        {
                                            // deselect currently selected column
                                            SetSelectedColumnCore(_selectedBandIndexes[bandIndex], false);
                                        }
                                        else
                                        {
                                            bandIndex++;
                                        }
                                    }

                                    if (SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)
                                    {
                                        RemoveIndividuallySelectedCells();
                                    }
                                    else
                                    {
                                        Debug.Assert(_individualSelectedCells.Count == 0);
                                    }
                                }
                                finally
                                {
                                    if (switchedToBulkPaint)
                                    {
                                        ExitBulkPaint(-1, -1);
                                    }
                                }
                            }

                            if (MultiSelect && _dataGridViewOper[OperationTrackMouseMoves])
                            {
                                _dataGridViewOper[OperationTrackColSelect] = true;
                            }

                            if (selectColumnRange)
                            {
                                if (Columns.DisplayInOrder(_ptAnchorCell.X, hti._col))
                                {
                                    SelectColumnRange(_ptAnchorCell.X, hti._col, true);
                                }
                                else
                                {
                                    SelectColumnRange(hti._col, _ptAnchorCell.X, true);
                                }
                            }
                            else if (!_selectedBandIndexes.Contains(hti._col))
                            {
                                SetSelectedColumnCore(hti._col, true);
                            }

                            // set current cell to the top most visible cell in the column
                            if (rowIndex != -1)
                            {
                                if (hti._col != _ptCurrentCell.X)
                                {
                                    if (IsInnerCellOutOfBounds(hti._col, rowIndex))
                                    {
                                        return;
                                    }

                                    bool success = ScrollIntoView(hti._col, rowIndex, forCurrentCellChange: false);
                                    Debug.Assert(success);
                                    if (IsInnerCellOutOfBounds(hti._col, rowIndex))
                                    {
                                        return;
                                    }

                                    success = SetCurrentCellAddressCore(
                                        hti._col,
                                        rowIndex,
                                        !isShiftDown,
                                        validateCurrentCell: false,
                                        throughMouseClick: true);
                                    Debug.Assert(success);
                                }
                                else if (_ptCurrentCell.X != -1)
                                {
                                    // Potentially have to give focus back to the current edited cell.
                                    bool success = SetCurrentCellAddressCore(
                                        _ptCurrentCell.X,
                                        _ptCurrentCell.Y,
                                        setAnchorCellAddress: false,
                                        validateCurrentCell: false,
                                        throughMouseClick: false);
                                    Debug.Assert(success);
                                }
                            }
                            else
                            {
                                Debug.Assert(CurrentCellAddress == new Point(-1, -1));
                            }
                        }
                        else
                        {
                            Debug.Assert(_selectedBandIndexes.Contains(hti._col));
                            SetSelectedColumnCore(hti._col, false);
                        }

                        break;
                    }
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }
    }

    protected virtual void OnColumnHeadersBorderStyleChanged(EventArgs e)
    {
        PerformLayoutPrivate(
            useRowShortcut: false,
            computeVisibleRows: false,
            invalidInAdjustFillingColumns: true,
            repositionEditingControl: false);

        Invalidate();
        GetEvent<EventHandler>(s_columnHeadersBorderStyleChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnColumnHeadersDefaultCellStyleChanged(EventArgs e)
    {
        if (ColumnHeadersVisible)
        {
            Invalidate(Rectangle.Union(_layout.TopLeftHeader, _layout.ColumnHeaders));

            if (e is not DataGridViewCellStyleChangedEventArgs dgvcsce || dgvcsce.ChangeAffectsPreferredSize)
            {
                OnColumnHeadersGlobalAutoSize();
                if (EditingControl is not null)
                {
                    PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
                }
            }
        }

        GetEvent<EventHandler>(s_columnHeadersDefaultCellStyleChangedEvent)?.Invoke(this, e);
    }

    private void OnColumnHeadersGlobalAutoSize()
    {
        if (_noAutoSizeCount > 0)
        {
            return;
        }

        bool fixedRowHeadersWidth = _rowHeadersWidthSizeMode is DataGridViewRowHeadersWidthSizeMode.EnableResizing
            or DataGridViewRowHeadersWidthSizeMode.DisableResizing;
        bool fixedColumnHeadersHeight = ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize;

        if (!fixedColumnHeadersHeight)
        {
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, fixedColumnsWidth: false);
        }

        if (!fixedRowHeadersWidth)
        {
            AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight: false);
        }

        // Autosize columns
        bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.Header, fixedHeight: false);

        if (!fixedRowHeadersWidth || columnAutoSized)
        {
            // Autosize rows
            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
        }

        if (!fixedColumnHeadersHeight)
        {
            // Second round of column headers autosizing
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
        }

        if (!fixedRowHeadersWidth)
        {
            // Second round of row headers autosizing
            AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight: true);
        }

        // Second round of columns autosizing
        AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.Header, fixedHeight: true);
    }

    protected virtual void OnColumnHeadersHeightChanged(EventArgs e)
    {
        if (EditingControl is not null)
        {
            PositionEditingControl(true, false, false);
        }

        UpdateMouseEnteredCell(hti: null, e: null);
        OnColumnHeadersGlobalAutoSize();
        GetEvent<EventHandler>(s_columnHeadersHeightChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnColumnHeadersHeightSizeModeChanged(DataGridViewAutoSizeModeEventArgs e)
    {
        if (_columnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
        {
            if (!e.PreviousModeAutoSized)
            {
                // Save current column headers height for later reuse
                _cachedColumnHeadersHeight = ColumnHeadersHeight;
            }

            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
        }
        else if (e.PreviousModeAutoSized)
        {
            ColumnHeadersHeight = _cachedColumnHeadersHeight;
        }

        GetEvent<DataGridViewAutoSizeModeEventHandler>(s_columnHeadersHeightSizeModeChangedEvent)?.Invoke(this, e);
    }

    internal void OnColumnHidden(DataGridViewColumn dataGridViewColumn)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        if (dataGridViewColumn.Displayed)
        {
            dataGridViewColumn.Displayed = false;
            DataGridViewColumnStateChangedEventArgs dgvrsce = new(dataGridViewColumn, DataGridViewElementStates.Displayed);
            OnColumnStateChanged(dgvrsce);
        }
    }

    internal void OnColumnMinimumWidthChanging(DataGridViewColumn dataGridViewColumn, int minimumWidth)
    {
        if (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && dataGridViewColumn.Width < minimumWidth)
        {
            // Force the filled column's width to be minimumWidth
            try
            {
                _dataGridViewState2[State2_UsedFillWeightsDirty] = true;
                dataGridViewColumn.DesiredMinimumWidth = minimumWidth;
                PerformLayoutPrivate(
                    useRowShortcut: false,
                    computeVisibleRows: true,
                    invalidInAdjustFillingColumns: true,
                    repositionEditingControl: false);
            }
            finally
            {
                dataGridViewColumn.DesiredMinimumWidth = 0;
            }

            Debug.Assert(dataGridViewColumn.Width == minimumWidth);
        }
    }

    protected virtual void OnColumnMinimumWidthChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        if (e.Column.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
        {
            // Column's width may adjust smaller
            PerformLayoutPrivate(
                useRowShortcut: false,
                computeVisibleRows: true,
                invalidInAdjustFillingColumns: false,
                repositionEditingControl: false);
        }

        GetEvent<DataGridViewColumnEventHandler>(s_columnMinimumWidthChangedEvent)?.Invoke(this, e);
    }

    internal void OnColumnNameChanged(DataGridViewColumn dataGridViewColumn)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
        OnColumnNameChanged(dgvce);
    }

    protected virtual void OnColumnNameChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        // Check if the column name is used as is in the column header
        DataGridViewColumn dataGridViewColumn = e.Column;

        if (dataGridViewColumn.HasHeaderCell
            && dataGridViewColumn.HeaderCell.Value is string stringValue
            && string.Compare(stringValue, dataGridViewColumn.Name, ignoreCase: false, CultureInfo.InvariantCulture) == 0)
        {
            InvalidateCellPrivate(dataGridViewColumn.Index, -1);

            DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)dataGridViewColumn.InheritedAutoSizeMode;
            DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header;
            bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0 || !ColumnHeadersVisible;
            if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
            {
                AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, fixedRowHeadersWidth: true, fixedColumnWidth);
            }

            if (!fixedColumnWidth)
            {
                bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0;
                AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, fixedHeight);
                if (!fixedHeight)
                {
                    AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
                    // Second round of column autosizing
                    AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, fixedHeight: true);
                }

                if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
                {
                    // Second round of column headers autosizing
                    AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, fixedRowHeadersWidth: true, fixedColumnWidth: true);
                }
            }
        }

        GetEvent<DataGridViewColumnEventHandler>(s_columnNameChangedEvent)?.Invoke(this, e);
    }

    internal void OnColumnRemoved(DataGridViewColumn dataGridViewColumn)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(dataGridViewColumn.DataGridView is null);
        OnColumnRemoved(new DataGridViewColumnEventArgs(dataGridViewColumn));
    }

    protected virtual void OnColumnRemoved(DataGridViewColumnEventArgs e) =>
        GetEvent<DataGridViewColumnEventHandler>(s_columnRemovedEvent)?.Invoke(this, e);

    private void OnColumnSelectMouseMove(HitTestInfo hti)
    {
        Debug.Assert(hti._col >= 0);
        Debug.Assert(MultiSelect);

        if (_ptCurrentCell.X != -1 && hti._col != _ptCurrentCell.X && !CommitEditForOperation(hti._col, _ptCurrentCell.Y, true))
        {
            // Return silently if validating/commit/abort failed
            return;
        }

        if (IsColumnOutOfBounds(hti._col))
        {
            return;
        }

        _noSelectionChangeCount++;
        try
        {
            if (_trackColumnEdge >= 0 && (Columns.DisplayInOrder(_trackColumn, _trackColumnEdge) || _trackColumnEdge == _trackColumn) && Columns.DisplayInOrder(_trackColumnEdge, hti._col))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[_trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(dataGridViewColumn.Index, hti._col, true);
                _trackColumnEdge = hti._col;
            }
            else if (_trackColumnEdge >= 0 && Columns.DisplayInOrder(_trackColumn, _trackColumnEdge) && Columns.DisplayInOrder(hti._col, _trackColumnEdge) && (Columns.DisplayInOrder(_trackColumn, hti._col) || hti._col == _trackColumn))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[hti._col], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(dataGridViewColumn.Index, _trackColumnEdge, false);
                _trackColumnEdge = hti._col;
            }
            else if (_trackColumnEdge == -1 && Columns.DisplayInOrder(_trackColumn, hti._col))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[_trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(dataGridViewColumn.Index, hti._col, true);
                _trackColumnEdge = hti._col;
            }
            else if (_trackColumnEdge >= 0 && (Columns.DisplayInOrder(_trackColumnEdge, _trackColumn) || _trackColumnEdge == _trackColumn) && Columns.DisplayInOrder(hti._col, _trackColumnEdge))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[_trackColumnEdge], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(hti._col, dataGridViewColumn.Index, true);
                _trackColumnEdge = hti._col;
            }
            else if (_trackColumnEdge >= 0 && Columns.DisplayInOrder(_trackColumnEdge, _trackColumn) && Columns.DisplayInOrder(_trackColumnEdge, hti._col) && (Columns.DisplayInOrder(hti._col, _trackColumn) || hti._col == _trackColumn))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[hti._col], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(_trackColumnEdge, dataGridViewColumn.Index, false);
                _trackColumnEdge = hti._col;
            }
            else if (_trackColumnEdge == -1 && Columns.DisplayInOrder(hti._col, _trackColumn))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[_trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(hti._col, dataGridViewColumn.Index, true);
                _trackColumnEdge = hti._col;
            }
            else if (_trackColumnEdge >= 0 && Columns.DisplayInOrder(_trackColumn, _trackColumnEdge) && Columns.DisplayInOrder(hti._col, _trackColumn))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[_trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(dataGridViewColumn.Index, _trackColumnEdge, false);
                dataGridViewColumn = Columns.GetPreviousColumn(Columns[_trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(hti._col, dataGridViewColumn.Index, true);
                _trackColumnEdge = hti._col;
            }
            else if (_trackColumnEdge >= 0 && Columns.DisplayInOrder(_trackColumn, hti._col) && Columns.DisplayInOrder(_trackColumnEdge, _trackColumn))
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[_trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(_trackColumnEdge, dataGridViewColumn.Index, false);
                dataGridViewColumn = Columns.GetNextColumn(Columns[_trackColumn], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectColumnRange(dataGridViewColumn.Index, hti._col, true);
                _trackColumnEdge = hti._col;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        if (_ptCurrentCell.X != -1 && hti._col != _ptCurrentCell.X)
        {
            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(hti._col))
            {
                return;
            }

            bool success = SetCurrentCellAddressCore(
                hti._col,
                _ptCurrentCell.Y,
                setAnchorCellAddress: false,
                validateCurrentCell: false,
                throughMouseClick: false);
            Debug.Assert(success);
        }
    }

    private void OnColumnsGlobalAutoSize()
    {
        InvalidateData();

        if (_noAutoSizeCount > 0)
        {
            return;
        }

        // Auto-size columns if needed
        bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0;
        bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, fixedHeight);
        // Autosize rows if needed
        if (!fixedHeight)
        {
            if (columnAutoSized)
            {
                AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
            }

            // Second round of columns autosizing
            AutoResizeAllVisibleColumnsInternal(DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows, fixedHeight: true);
        }
    }

    internal void OnColumnSortModeChanged(DataGridViewColumn dataGridViewColumn)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        DataGridViewColumnEventArgs dgvce = new(dataGridViewColumn);
        OnColumnSortModeChanged(dgvce);
    }

    protected virtual void OnColumnSortModeChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        DataGridViewColumn dataGridViewColumn = e.Column;

        if (dataGridViewColumn.HasHeaderCell)
        {
            if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.NotSortable
                || (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Programmatic && SortedColumn == dataGridViewColumn))
            {
                dataGridViewColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
                // This call will trigger OnSortGlyphDirectionChanged which in turn does
                // this.sortedColumn = null; and InvalidateCellPrivate(e.Column.Index, -1);
            }

            // Potential resizing of the column headers and/or affected column.
            DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaInternal = (DataGridViewAutoSizeColumnCriteriaInternal)dataGridViewColumn.InheritedAutoSizeMode;
            DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFiltered = autoSizeColumnCriteriaInternal & DataGridViewAutoSizeColumnCriteriaInternal.Header;
            bool fixedColumnWidth = autoSizeColumnCriteriaFiltered == 0 || !ColumnHeadersVisible;
            if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
            {
                AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, fixedRowHeadersWidth: true, fixedColumnWidth);
            }

            if (!fixedColumnWidth)
            {
                bool fixedHeight = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) == 0;
                AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, fixedHeight);
                if (!fixedHeight)
                {
                    AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
                    // Second round of column autosizing
                    AutoResizeColumnInternal(dataGridViewColumn.Index, autoSizeColumnCriteriaInternal, fixedHeight: true);
                }

                if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
                {
                    // Second round of column headers autosizing
                    AutoResizeColumnHeadersHeight(dataGridViewColumn.Index, fixedRowHeadersWidth: true, fixedColumnWidth: true);
                }
            }
        }

        GetEvent<DataGridViewColumnEventHandler>(s_columnSortModeChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnColumnStateChanged(DataGridViewColumnStateChangedEventArgs e)
    {
        // column.Frozen | .Visible changed - this may require a complete re-layout of the control
        DataGridViewColumn dataGridViewColumn = e.Column;
        switch (e.StateChanged)
        {
            // At this point we assume that only the Selected state has an influence on the rendering of the column.
            // If there is a customer scenario where another state has an influence, the dev must invalidate the column by hand.
            // case DataGridViewElementStates.ReadOnly:
            // case DataGridViewElementStates.Resizable:

            case DataGridViewElementStates.Selected:
                if (dataGridViewColumn.Visible && _inBulkPaintCount == 0)
                {
                    InvalidateColumnInternal(dataGridViewColumn.Index);
                }

                break;

            case DataGridViewElementStates.Frozen:
                if (dataGridViewColumn.Visible)
                {
                    if (dataGridViewColumn.Frozen)
                    {
                        // visible column became frozen
                        if (_horizontalOffset >= dataGridViewColumn.Thickness)
                        {
                            Debug.Assert(Columns.DisplayInOrder(dataGridViewColumn.Index, DisplayedBandsInfo.FirstDisplayedScrollingCol));
                            _horizontalOffset -= dataGridViewColumn.Thickness;
                        }
                        else
                        {
                            _horizontalOffset = FirstDisplayedScrollingColumnHiddenWidth = 0;
                        }
                    }
                    else
                    {
                        // column was unfrozen - make it the first visible scrolling column if there is room
                        _horizontalOffset = FirstDisplayedScrollingColumnHiddenWidth = 0;
                    }

                    if (_horizScrollBar.Enabled)
                    {
                        _horizScrollBar.Value = _horizontalOffset;
                    }

                    // UsedFillWeight values need to be updated
                    _dataGridViewState2[State2_UsedFillWeightsDirty] = true;

                    PerformLayoutPrivate(
                        useRowShortcut: false,
                        computeVisibleRows: false,
                        invalidInAdjustFillingColumns: true,
                        repositionEditingControl: true);
                    Invalidate();
                }

                break;

            case DataGridViewElementStates.Visible:
                if (!dataGridViewColumn.Visible && dataGridViewColumn.Displayed)
                {
                    // Displayed column becomes invisible. Turns off the Displayed state.
                    dataGridViewColumn.Displayed = false;
                }

                // UsedFillWeight values need to be updated
                _dataGridViewState2[State2_UsedFillWeightsDirty] = true;

                PerformLayoutPrivate(
                    useRowShortcut: false,
                    computeVisibleRows: false,
                    invalidInAdjustFillingColumns: true,
                    repositionEditingControl: true);

                bool autoSizeRows = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0
                    || ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) != 0
                        && RowHeadersVisible);
                bool autoSizeColumn = false;
                DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.InheritedAutoSizeMode;
                Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet);

                if (autoSizeColumnMode is not DataGridViewAutoSizeColumnMode.None
                    and not DataGridViewAutoSizeColumnMode.Fill)
                {
                    // Column auto size
                    int width = dataGridViewColumn.ThicknessInternal;
                    if (dataGridViewColumn.Visible)
                    {
                        // Cache column's width before potential autosizing occurs
                        dataGridViewColumn.CachedThickness = width;
                        AutoResizeColumnInternal(dataGridViewColumn.Index, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, fixedHeight: !autoSizeRows);
                        autoSizeColumn = true;
                    }
                    else if (width != dataGridViewColumn.CachedThickness)
                    {
                        // Columns that are made invisible in the collection take their non-autosized width
                        dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness);
                    }
                }

                if (autoSizeRows)
                {
                    if (dataGridViewColumn.Visible)
                    {
                        AdjustExpandingRows(dataGridViewColumn.Index, fixedWidth: true);
                    }
                    else
                    {
                        AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
                    }

                    if (autoSizeColumn)
                    {
                        // Second round of column autosizing
                        AutoResizeColumnInternal(dataGridViewColumn.Index, (DataGridViewAutoSizeColumnCriteriaInternal)autoSizeColumnMode, fixedHeight: true);
                    }
                }
                else
                {
                    Invalidate();
                }

                break;
        }

        GetEvent<DataGridViewColumnStateChangedEventHandler>(s_columnStateChangedEvent)?.Invoke(this, e);

        if (e.StateChanged == DataGridViewElementStates.ReadOnly
            && dataGridViewColumn.Index == _ptCurrentCell.X
            && !_dataGridViewOper[OperationInReadOnlyChange])
        {
            VerifyImeRestrictedModeChanged();

            if (!dataGridViewColumn.ReadOnly
                && ColumnEditable(_ptCurrentCell.X)
                && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.ReadOnly) == 0
                    && !IsCurrentCellInEditMode
                    && (EditMode == DataGridViewEditMode.EditOnEnter
                        || (EditMode != DataGridViewEditMode.EditProgrammatically && CurrentCellInternal.EditType is null)))
            {
                // Current column becomes read/write. Enter editing mode.
                BeginEditInternal(selectAll: true);
            }
        }
    }

    internal void OnColumnToolTipTextChanged(DataGridViewColumn dataGridViewColumn)
    {
        OnColumnToolTipTextChanged(new DataGridViewColumnEventArgs(dataGridViewColumn));
    }

    protected virtual void OnColumnToolTipTextChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        GetEvent<DataGridViewColumnEventHandler>(s_columnTooltipTextChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnColumnWidthChanged(DataGridViewColumnEventArgs e)
    {
        if (e.Column.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        Columns.InvalidateCachedColumnsWidths();

        // don't do any layout logic if the handle was not created already
        if (e.Column.Visible && IsHandleCreated)
        {
            PerformLayoutPrivate(
                useRowShortcut: false,
                computeVisibleRows: false,
                invalidInAdjustFillingColumns: false,
                repositionEditingControl: false);

            Rectangle rightArea = _layout.Data;
            if (_layout.ColumnHeadersVisible)
            {
                rightArea = Rectangle.Union(rightArea, _layout.ColumnHeaders);
            }
            else if (SingleHorizontalBorderAdded)
            {
                rightArea.Y--;
                rightArea.Height++;
            }

            if (rightArea.Width > 0 && rightArea.Height > 0)
            {
                int leftEdge = GetColumnXFromIndex(e.Column.Index);
                if (RightToLeftInternal)
                {
                    rightArea.Width -= rightArea.Right - leftEdge;
                }
                else
                {
                    rightArea.Width -= leftEdge - rightArea.X;
                    rightArea.X = leftEdge;
                }

                if (rightArea.Width > 0 && rightArea.Height > 0)
                {
                    Invalidate(rightArea);
                }
            }

            if (EditingControl is not null)
            {
                PositionEditingControl(_ptCurrentCell.X != e.Column.Index, true, false);
            }

            UpdateMouseEnteredCell(hti: null, e: null);

            if (AutoSize)
            {
                LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Columns);
            }
        }

        GetEvent<DataGridViewColumnEventHandler>(s_columnWidthChangedEvent)?.Invoke(this, e);

        if (!InAdjustFillingColumns)
        {
            // Autosize rows and column headers if needed
            OnColumnGlobalAutoSize(e.Column.Index);
        }
    }

    internal void OnCommonCellContentClick(int columnIndex, int rowIndex, bool doubleClick)
    {
        if (_ptMouseDownCell.X == -2
            || (_dataGridViewState2[State2_CellMouseDownInContentBounds]
                && _ptMouseDownCell.X == columnIndex
                && _ptMouseDownCell.Y == rowIndex
                && (_ptMouseDownCell.X == -1
                    || _ptMouseDownCell.Y == -1
                    || (columnIndex == _ptCurrentCell.X && rowIndex == _ptCurrentCell.Y))))
        {
            DataGridViewCellEventArgs dgvce = new(columnIndex, rowIndex);
            if (doubleClick)
            {
                OnCellContentDoubleClick(dgvce);
            }
            else
            {
                OnCellContentClick(dgvce);
            }
        }
    }

    protected virtual void OnCurrentCellChanged(EventArgs e)
    {
        VerifyImeRestrictedModeChanged();
        GetEvent<EventHandler>(s_currentCellChangedEvent)?.Invoke(this, e);

        if (CurrentCell is not null && (ShowCellToolTips || (ShowCellErrors && !string.IsNullOrEmpty(CurrentCell?.ErrorText))))
        {
            ActivateToolTip(activate: false, toolTipText: string.Empty, CurrentCell.ColumnIndex, CurrentCell.RowIndex);
            KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(CurrentCell);
        }
    }

    protected virtual void OnCurrentCellDirtyStateChanged(EventArgs e)
    {
        if (RowHeadersVisible && ShowEditingIcon)
        {
            // Force the pencil to appear in the row header
            InvalidateCellPrivate(-1, _ptCurrentCell.Y);
        }

        if (IsCurrentCellDirty && NewRowIndex == _ptCurrentCell.Y)
        {
            Debug.Assert(AllowUserToAddRowsInternal);
            // First time the 'new' row gets edited.
            // It becomes a regular row and a new 'new' row is appended.
            NewRowIndex = -1;
            AddNewRow(createdByEditing: true);
        }

        GetEvent<EventHandler>(s_currentCellDirtyStateChangedEvent)?.Invoke(this, e);
    }

    protected override void OnCursorChanged(EventArgs e)
    {
        base.OnCursorChanged(e);
        if (!_dataGridViewState2[State2_IgnoreCursorChange])
        {
            _oldCursor = Cursor;
        }
    }

    internal void OnDataBindingComplete(ListChangedType listChangedType) =>
        OnDataBindingComplete(new DataGridViewBindingCompleteEventArgs(listChangedType));

    protected virtual void OnDataBindingComplete(DataGridViewBindingCompleteEventArgs e) =>
        GetEvent<DataGridViewBindingCompleteEventHandler>(s_dataBindingCompleteEvent)?.Invoke(this, e);

    protected virtual void OnDataError(bool displayErrorDialogIfNoHandler, DataGridViewDataErrorEventArgs e)
    {
        if (!_dataGridViewOper[OperationInDispose] && !IsDisposed)
        {
            if (Events[s_dataErrorEvent] is not DataGridViewDataErrorEventHandler eh)
            {
                if (displayErrorDialogIfNoHandler)
                {
                    string errorText = e.Exception is null
                        ? SR.DataGridView_ErrorMessageText_NoException
                        : string.Format(SR.DataGridView_ErrorMessageText_WithException, e.Exception);

                    if (RightToLeftInternal)
                    {
                        MessageBox.Show(
                            errorText,
                            SR.DataGridView_ErrorMessageCaption,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error,
                            MessageBoxDefaultButton.Button1,
                            MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading);
                    }
                    else
                    {
                        MessageBox.Show(errorText, SR.DataGridView_ErrorMessageCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }

                    CorrectFocus(onlyIfGridHasFocus: true);
                }
            }
            else
            {
                eh(this, e);
                CorrectFocus(onlyIfGridHasFocus: true);
            }
        }
    }

    internal void OnDataErrorInternal(DataGridViewDataErrorEventArgs e) =>
        OnDataError(displayErrorDialogIfNoHandler: !DesignMode, e);

    internal void OnDataGridViewElementStateChanged(DataGridViewElement element, int index, DataGridViewElementStates elementState)
    {
        if (element is DataGridViewColumn dataGridViewColumn)
        {
            DataGridViewColumnStateChangedEventArgs dgvcsce = new(dataGridViewColumn, elementState);

            OnColumnStateChanged(dgvcsce);
        }
        else
        {
            if (element is DataGridViewRow dataGridViewRow)
            {
                if (Events[s_rowStateChangedEvent] is DataGridViewRowStateChangedEventHandler
                    && dataGridViewRow.DataGridView is not null && dataGridViewRow.Index == -1)
                {
                    dataGridViewRow = Rows[index];
                }

                DataGridViewRowStateChangedEventArgs dgvrsce = new(dataGridViewRow, elementState);

                OnRowStateChanged(dataGridViewRow.Index == -1 ? index : dataGridViewRow.Index, dgvrsce);
            }
            else
            {
                if (element is DataGridViewCell dataGridViewCell)
                {
                    DataGridViewCellStateChangedEventArgs dgvcsce = new(dataGridViewCell, elementState);

                    OnCellStateChanged(dgvcsce);
                }
            }
        }

        if ((elementState & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected)
        {
            if (_noSelectionChangeCount > 0)
            {
                _dataGridViewState2[State2_RaiseSelectionChanged] = true;
            }
            else
            {
                OnSelectionChanged(EventArgs.Empty);
            }
        }
    }

    internal void OnDataGridViewElementStateChanging(DataGridViewElement element, int index, DataGridViewElementStates elementState)
    {
        if (element is DataGridViewColumn dataGridViewColumn)
        {
            // column.Frozen | .Visible | .ReadOnly changing
            switch (elementState)
            {
                case DataGridViewElementStates.Frozen:
                case DataGridViewElementStates.Visible:
                    if (elementState == DataGridViewElementStates.Visible)
                    {
                        if (!dataGridViewColumn.Visible
                            && dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader
                            && !ColumnHeadersVisible)
                        {
                            throw new InvalidOperationException(SR.DataGridView_CannotMakeAutoSizedColumnVisible);
                        }
                        else if (!dataGridViewColumn.Visible
                            && dataGridViewColumn.Frozen
                            && dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                        {
                            // alternative: throw new InvalidOperationException(SR.DataGridView_CannotMakeAutoFillColumnVisible);
                            //              DataGridView_CannotMakeAutoFillColumnVisible=The column cannot be made visible
                            //              because its autosizing mode is Fill and it is frozen.
                            // Removing the Fill auto size mode when frozen column becomes visible (instead of throwing an exception)
                            dataGridViewColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
                        }
                        else if (dataGridViewColumn.Visible && _ptCurrentCell.X == dataGridViewColumn.Index)
                        {
                            // Column of the current cell is made invisible. Trying to reset the current cell. May throw an exception.
                            ResetCurrentCell();
                            // Microsoft: Should the current cell be set to some cell after the operation?
                        }
                    }

                    if (elementState == DataGridViewElementStates.Frozen
                        && !dataGridViewColumn.Frozen
                        && dataGridViewColumn.Visible
                        && dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill)
                    {
                        // Removing the Fill auto size mode when visible column becomes frozen (instead of throwing an exception)
                        dataGridViewColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
                    }

                    CorrectColumnFrozenStates(dataGridViewColumn, elementState == DataGridViewElementStates.Frozen);
                    break;

                case DataGridViewElementStates.ReadOnly:
                    if (_ptCurrentCell.X == dataGridViewColumn.Index
                        && IsCurrentCellInEditMode
                        && !dataGridViewColumn.ReadOnly
                        && !_dataGridViewOper[OperationInReadOnlyChange])
                    {
                        Debug.Assert(!ReadOnly);
                        // Column becomes read-only. Exit editing mode.
                        if (!EndEdit(
                            DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit,
                            validateCell: DataGridViewValidateCellInternal.Always,
                            fireCellLeave: false,
                            fireCellEnter: false,
                            fireRowLeave: false,
                            fireRowEnter: false,
                            fireLeave: false,
                            keepFocus: true,
                            resetCurrentCell: false,
                            resetAnchorCell: false))
                        {
                            throw new InvalidOperationException(SR.DataGridView_CommitFailedCannotCompleteOperation);
                        }
                    }

                    break;

                default:
                    Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging");
                    break;
            }
        }
        else
        {
            if (element is DataGridViewRow dataGridViewRow)
            {
                // row.Frozen | .Visible | .ReadOnly changing
                int rowIndex = ((dataGridViewRow.Index > -1) ? dataGridViewRow.Index : index);
                switch (elementState)
                {
                    case DataGridViewElementStates.Frozen:
                    case DataGridViewElementStates.Visible:
                        if (elementState == DataGridViewElementStates.Visible && _ptCurrentCell.Y == rowIndex)
                        {
                            Debug.Assert((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0);
                            // Row of the current cell is made invisible.
                            if (DataSource is not null)
                            {
                                Debug.Assert(DataConnection is not null);
                                Debug.Assert(DataConnection.CurrencyManager is not null);
                                Debug.Assert(DataConnection.CurrencyManager.Position == _ptCurrentCell.Y);
                                // the row associated with the currency manager's position cannot be made invisible.
                                throw new InvalidOperationException(SR.DataGridView_CurrencyManagerRowCannotBeInvisible);
                            }

                            // Trying to reset the current cell. May throw an exception.
                            ResetCurrentCell();
                            // Microsoft: Should the current cell be set to some cell after the operation?
                        }

                        CorrectRowFrozenStates(dataGridViewRow, rowIndex, elementState == DataGridViewElementStates.Frozen);
                        break;

                    case DataGridViewElementStates.ReadOnly:
                        if (_ptCurrentCell.Y == rowIndex
                            && (Rows.GetRowState(rowIndex) & DataGridViewElementStates.ReadOnly) == 0
                            && !ReadOnly
                            && IsCurrentCellInEditMode
                            && !_dataGridViewOper[OperationInReadOnlyChange])
                        {
                            // Row becomes read-only. Exit editing mode.
                            if (!EndEdit(
                                DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit,
                                validateCell: DataGridViewValidateCellInternal.Always,
                                fireCellLeave: false,
                                fireCellEnter: false,
                                fireRowLeave: false,
                                fireRowEnter: false,
                                fireLeave: false,
                                keepFocus: true,
                                resetCurrentCell: false,
                                resetAnchorCell: false))
                            {
                                throw new InvalidOperationException(SR.DataGridView_CommitFailedCannotCompleteOperation);
                            }
                        }

                        break;

                    default:
                        Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging");
                        break;
                }
            }
            else
            {
                if (element is DataGridViewCell dataGridViewCell)
                {
                    // cell.ReadOnly changing
                    switch (elementState)
                    {
                        case DataGridViewElementStates.ReadOnly:
                            if (_ptCurrentCell.X == dataGridViewCell.ColumnIndex
                                && _ptCurrentCell.Y == dataGridViewCell.RowIndex
                                && IsCurrentCellInEditMode
                                && !dataGridViewCell.ReadOnly
                                && !_dataGridViewOper[OperationInReadOnlyChange])
                            {
                                Debug.Assert(!Columns[dataGridViewCell.ColumnIndex].ReadOnly);
                                Debug.Assert(!Rows[dataGridViewCell.RowIndex].ReadOnly);
                                Debug.Assert(!ReadOnly);
                                // Current cell becomes read-only. Exit editing mode.
                                if (!EndEdit(
                                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit,
                                    validateCell: DataGridViewValidateCellInternal.Always,
                                    fireCellLeave: false,
                                    fireCellEnter: false,
                                    fireRowLeave: false,
                                    fireRowEnter: false,
                                    fireLeave: false,
                                    keepFocus: true,
                                    resetCurrentCell: false,
                                    resetAnchorCell: false))
                                {
                                    throw new InvalidOperationException(SR.DataGridView_CommitFailedCannotCompleteOperation);
                                }
                            }

                            break;

                        default:
                            Debug.Fail("Unexpected DataGridViewElementStates param in DataGridView.OnDataGridViewElementStateChanging");
                            break;
                    }
                }
                else
                {
                    Debug.Fail("Unexpected DataGridViewElement type in DataGridView.OnDataGridViewElementStateChanging");
                }
            }
        }
    }

    protected virtual void OnDataMemberChanged(EventArgs e)
    {
        RefreshColumnsAndRows();
        GetEvent<EventHandler>(s_dataMemberChangedEvent)?.Invoke(this, e);

        if (DataConnection is not null && DataConnection.CurrencyManager is not null)
        {
            OnDataBindingComplete(ListChangedType.Reset);
        }
    }

    protected virtual void OnDataSourceChanged(EventArgs e)
    {
        RefreshColumnsAndRows();
        InvalidateRowHeights();
        GetEvent<EventHandler>(s_dataSourceChangedEvent)?.Invoke(this, e);

        if (DataConnection is not null && DataConnection.CurrencyManager is not null)
        {
            OnDataBindingComplete(ListChangedType.Reset);
        }
    }

    /// <summary>
    ///  Refresh items when the DataSource is disposed.
    /// </summary>
    private void OnDataSourceDisposed(object? sender, EventArgs e) => DataSource = null;

    protected virtual void OnDefaultCellStyleChanged(EventArgs e)
    {
        if (e is DataGridViewCellStyleChangedEventArgs dgvcsce && !dgvcsce.ChangeAffectsPreferredSize)
        {
            Invalidate();
        }
        else
        {
            OnGlobalAutoSize();
            if (EditingControl is not null)
            {
                PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
            }
        }

        GetEvent<EventHandler>(s_defaultCellStyleChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnDefaultValuesNeeded(DataGridViewRowEventArgs e) =>
        GetEvent<DataGridViewRowEventHandler>(s_defaultValuesNeededEvent)?.Invoke(this, e);

    protected override void OnDoubleClick(EventArgs e)
    {
        base.OnDoubleClick(e);

        if (!_dataGridViewState2[State2_MessageFromEditingCtrls]
            && !_dataGridViewOper[OperationInDispose]
            && !IsDisposed)
        {
            if (e is MouseEventArgs me)
            {
                HitTestInfo hti = HitTest(me.X, me.Y);
                if (hti.Type != DataGridViewHitTestType.None
                    && hti.Type != DataGridViewHitTestType.HorizontalScrollBar
                    && hti.Type != DataGridViewHitTestType.VerticalScrollBar
                    && me.Button == MouseButtons.Left)
                {
                    OnCellDoubleClick(new DataGridViewCellEventArgs(hti._col, hti._row));
                }
            }
        }
    }

    protected virtual void OnEditingControlShowing(DataGridViewEditingControlShowingEventArgs e) =>
        GetEvent<DataGridViewEditingControlShowingEventHandler>(s_editingControlShowingEvent)?.Invoke(this, e);

    protected virtual void OnEditModeChanged(EventArgs e)
    {
        if (Focused && EditMode == DataGridViewEditMode.EditOnEnter && _ptCurrentCell.X > -1 && !IsCurrentCellInEditMode)
        {
            // New edit mode is EditOnEnter and there is an editable current cell, try to go to edit mode.
            BeginEditInternal(selectAll: true);
        }

        GetEvent<EventHandler>(s_editModeChangedEvent)?.Invoke(this, e);
    }

    protected override void OnEnabledChanged(EventArgs e)
    {
        base.OnEnabledChanged(e);
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        if (IsHandleCreated && Enabled)
        {
            if (_vertScrollBar is not null && _vertScrollBar.Visible)
            {
                int totalVisibleHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible);
                int totalVisibleFrozenHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                _vertScrollBar.Maximum = totalVisibleHeight - totalVisibleFrozenHeight;

                _vertScrollBar.Enabled = true;
            }

            if (_horizScrollBar is not null && _horizScrollBar.Visible)
            {
                _horizScrollBar.Enabled = true;
            }
        }
    }

    protected internal override void OnEnter(EventArgs e)
    {
        if (EditingControl is not null && EditingControl.ContainsFocus)
        {
            return;
        }

        base.OnEnter(e);

        // There are cases when a control is on the designer and it still receives the OnEnter event.
        // Guard against this.
        if (DesignMode)
        {
            return;
        }

        _dataGridViewState1[State1_LeavingWithTabKey] = false;

        if (_ptCurrentCell.X > -1)
        {
            DataGridViewCell? dataGridViewCell = null;

            // We're re-entering a row we already entered earlier. The first time we entered the row,
            // the DataGridView didn't have focus. This time it does. We don't want to recreate the new row a second time.
            OnRowEnter(
                ref dataGridViewCell,
                _ptCurrentCell.X,
                _ptCurrentCell.Y,
                canCreateNewRow: false,
                validationFailureOccurred: false);
            if (_ptCurrentCell.X == -1)
            {
                return;
            }

            OnCellEnter(ref dataGridViewCell, _ptCurrentCell.X, _ptCurrentCell.Y);

            // Force repainting of the current column's header cell to highlight it
            if (SelectionMode == DataGridViewSelectionMode.FullRowSelect)
            {
                InvalidateCellPrivate(_ptCurrentCell.X, -1);
            }
        }
        else if (!_dataGridViewOper[OperationInMouseDown])
        {
            // Focus is given to the DataGridView control via a the TAB key.
            MakeFirstDisplayedCellCurrentCell(includeNewRow: true);
        }

        if (_ptCurrentCell.X > -1 && !IsCurrentCellInEditMode)
        {
            if (EditMode == DataGridViewEditMode.EditOnEnter
                || (EditMode != DataGridViewEditMode.EditProgrammatically && CurrentCellInternal.EditType is null))
            {
                BeginEditInternal(selectAll: true);
                if (_ptCurrentCell.X > -1 && CurrentCellInternal.EditType is null && !_dataGridViewOper[OperationInMouseDown])
                {
                    // The current cell does not have an edit type so the data grid view did not put an edit control on top.
                    // We should invalidate the current cell so that the dataGridView repaints the focus around the current cell.
                    // But do that only if the dataGridView did not get the focus via mouse.
                    InvalidateCellPrivate(columnIndex: _ptCurrentCell.X, rowIndex: _ptCurrentCell.Y);
                }
            }
            else if (!_dataGridViewOper[OperationInMouseDown])
            {
                // When the focus is given to the DataGridView control via mouse
                // the dataGridView changes selection so it invalidates the current cell anyway
                //
                // In any other case Invalidate the current cell so the dataGridView repaints the focus around the current cell
                InvalidateCellPrivate(columnIndex: _ptCurrentCell.X, rowIndex: _ptCurrentCell.Y);
            }
        }

        // Draw focus rectangle around the grid
        if (IsGridFocusRectangleEnabled())
        {
            InvalidateRectangleEdges(GetGridFocusRectangle());
        }
    }

    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);

        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        // Change may be due to an ambient font change.
        if (_dataGridViewState1[State1_AmbientColumnHeadersFont] && ColumnHeadersDefaultCellStyle.Font != base.Font)
        {
            ColumnHeadersDefaultCellStyle.Font = base.Font;
            _dataGridViewState1[State1_AmbientColumnHeadersFont] = true;
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true;
            OnColumnHeadersDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }

        if (_dataGridViewState1[State1_AmbientRowHeadersFont] && RowHeadersDefaultCellStyle.Font != base.Font)
        {
            RowHeadersDefaultCellStyle.Font = base.Font;
            _dataGridViewState1[State1_AmbientRowHeadersFont] = true;
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true;
            OnRowHeadersDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }

        if (_dataGridViewState1[State1_AmbientFont] && DefaultCellStyle.Font != base.Font)
        {
            DefaultCellStyle.Font = base.Font;
            _dataGridViewState1[State1_AmbientFont] = true;
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = true;
            OnDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }
    }

    protected override void OnForeColorChanged(EventArgs e)
    {
        base.OnForeColorChanged(e);
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        // Change may be due to an ambient forecolor change.
        if (_dataGridViewState1[State1_AmbientForeColor] && DefaultCellStyle.ForeColor != base.ForeColor)
        {
            DefaultCellStyle.ForeColor = base.ForeColor;
            _dataGridViewState1[State1_AmbientForeColor] = true;
            CellStyleChangedEventArgs.ChangeAffectsPreferredSize = false;
            OnDefaultCellStyleChanged(CellStyleChangedEventArgs);
        }
    }

    private void OnGlobalAutoSize()
    {
        Invalidate();

        if (_noAutoSizeCount > 0)
        {
            return;
        }

        bool autoSizeRowHeaders = _rowHeadersWidthSizeMode is not DataGridViewRowHeadersWidthSizeMode.EnableResizing
            and not DataGridViewRowHeadersWidthSizeMode.DisableResizing;

        if (autoSizeRowHeaders)
        {
            AutoResizeRowHeadersWidth(
                _rowHeadersWidthSizeMode,
                fixedColumnHeadersHeight: ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize,
                fixedRowsHeight: _autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None);
        }

        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
        {
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: false);
        }

        if (_autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None)
        {
            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: false, internalAutosizing: true);
        }

        AutoResizeAllVisibleColumnsInternal(
            DataGridViewAutoSizeColumnCriteriaInternal.Header
                | DataGridViewAutoSizeColumnCriteriaInternal.AllRows
                | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows,
            fixedHeight: true);

        if (autoSizeRowHeaders
            && (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize || _autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None))
        {
            // Second round of row headers autosizing
            AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight: true);
        }

        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
        {
            // Second round of column headers autosizing
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
        }

        if (_autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None)
        {
            // Second round of rows autosizing
            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
        }
    }

    protected override void OnGotFocus(EventArgs e)
    {
        base.OnGotFocus(e);
        if (_ptCurrentCell.X != -1)
        {
            InvalidateCell(_ptCurrentCell.X, _ptCurrentCell.Y);
        }

        // Inform Accessibility that our current cell contains the focus.
        if (!_dataGridViewOper[OperationInCurrentCellChange]
            && (!_dataGridViewOper[OperationInEndEdit] || EditMode != DataGridViewEditMode.EditOnEnter)
            && (!_dataGridViewOper[OperationInMouseDown] || EditMode != DataGridViewEditMode.EditOnEnter)
            && _ptCurrentCell.X > -1)
        {
            // The name is misleading ( the current cell did not change ).
            // However, AccessibilityNotifyCurrentCellChanged is now a public method so we can't change its name
            // to better reflect its purpose.
            AccessibilityNotifyCurrentCellChanged(_ptCurrentCell);
            if (CurrentCell is not null && (ShowCellToolTips || (ShowCellErrors && !string.IsNullOrEmpty(CurrentCell.ErrorText))))
            {
                ActivateToolTip(activate: false, toolTipText: string.Empty, CurrentCell.ColumnIndex, CurrentCell.RowIndex);
                KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(CurrentCell);
            }
        }
    }

    protected virtual void OnGridColorChanged(EventArgs e)
    {
        InvalidateInside();
        GetEvent<EventHandler>(s_gridColorChangedEvent)?.Invoke(this, e);
    }

    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);

        if (_layout._dirty)
        {
            PerformLayoutPrivate(
                useRowShortcut: false,
                computeVisibleRows: true,
                invalidInAdjustFillingColumns: false,
                repositionEditingControl: false);
        }

        if (_ptCurrentCell.X == -1)
        {
            MakeFirstDisplayedCellCurrentCell(includeNewRow: false);
        }
        else
        {
            ScrollIntoView(_ptCurrentCell.X, _ptCurrentCell.Y, forCurrentCellChange: false);
        }

        // do the AutoSize work that was skipped during initialization
        if (_dataGridViewState2[State2_AutoSizedWithoutHandle])
        {
            _dataGridViewState2[State2_AutoSizedWithoutHandle] = false;
            OnGlobalAutoSize();
        }

        SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged;
    }

    protected override void OnHandleDestroyed(EventArgs e)
    {
        SystemEvents.UserPreferenceChanged -= OnUserPreferenceChanged;
        base.OnHandleDestroyed(e);
    }

    internal void OnInsertedColumn_PreNotification(DataGridViewColumn dataGridViewColumn)
    {
        // Fix the OldFirstDisplayedScrollingCol
        DisplayedBandsInfo.CorrectColumnIndexAfterInsertion(dataGridViewColumn.Index, 1);

        // Fix the Index of all following columns
        CorrectColumnIndexesAfterInsertion(dataGridViewColumn, 1);

        // Same effect as appending a column
        OnAddedColumn(dataGridViewColumn);
    }

    internal void OnInsertedColumn_PostNotification(Point newCurrentCell)
    {
        // Update current cell if needed
        if (newCurrentCell.X != -1)
        {
            Debug.Assert(_ptCurrentCell.X == -1);
            bool success = SetAndSelectCurrentCellAddress(
                newCurrentCell.X,
                newCurrentCell.Y,
                setAnchorCellAddress: true,
                validateCurrentCell: false,
                throughMouseClick: false,
                clearSelection: false,
                forceCurrentCellSelection: Columns.GetColumnCount(DataGridViewElementStates.Visible) == 1);
            Debug.Assert(success);
        }
    }

    internal void OnInsertedRow_PreNotification(int rowIndex, int insertionCount)
    {
        Debug.Assert(rowIndex >= 0);
        Debug.Assert(insertionCount > 0);

        // Fix the OldFirstDisplayedScrollingRow
        DisplayedBandsInfo.CorrectRowIndexAfterInsertion(rowIndex, insertionCount);

        // Fix the Index of all following rows
        CorrectRowIndexesAfterInsertion(rowIndex, insertionCount);

        // Next, same effect as adding a row
        OnAddedRow_PreNotification(rowIndex);
    }

    internal void OnInsertedRow_PostNotification(int rowIndex, Point newCurrentCell, bool lastInsertion)
    {
        Debug.Assert(rowIndex >= 0);

        // Same effect as adding a row
        OnAddedRow_PostNotification(rowIndex);

        // Update current cell if needed
        if (lastInsertion && newCurrentCell.Y != -1)
        {
            Debug.Assert(_ptCurrentCell.X == -1);
            bool success = SetAndSelectCurrentCellAddress(
                newCurrentCell.X,
                newCurrentCell.Y,
                setAnchorCellAddress: true,
                validateCurrentCell: false,
                throughMouseClick: false,
                clearSelection: false,
                forceCurrentCellSelection: Rows.GetRowCount(DataGridViewElementStates.Visible) == 1);
            Debug.Assert(success);
        }
    }

    internal void OnInsertedRows_PreNotification(int rowIndex, DataGridViewRow[] dataGridViewRows)
    {
        Debug.Assert(rowIndex >= 0);
        Debug.Assert(dataGridViewRows is not null);
        Debug.Assert(dataGridViewRows.Length > 0);

        // Fix the OldFirstDisplayedScrollingRow
        DisplayedBandsInfo.CorrectRowIndexAfterInsertion(rowIndex, dataGridViewRows.Length);

        // Fix the Index of all following rows
        CorrectRowIndexesAfterInsertion(rowIndex, dataGridViewRows.Length);

        // Next, same effect as adding the rows
        OnAddedRows_PreNotification(dataGridViewRows);
    }

    internal void OnInsertedRows_PostNotification(DataGridViewRow[] dataGridViewRows, Point newCurrentCell)
    {
        Debug.Assert(dataGridViewRows is not null);
        Debug.Assert(dataGridViewRows.Length > 0);

        // Same effect as adding the rows
        OnAddedRows_PostNotification(dataGridViewRows);

        // Update current cell if needed
        if (newCurrentCell.Y != -1)
        {
            Debug.Assert(_ptCurrentCell.X == -1);
            bool success = SetAndSelectCurrentCellAddress(
                newCurrentCell.X,
                newCurrentCell.Y,
                setAnchorCellAddress: true,
                validateCurrentCell: false,
                throughMouseClick: false,
                clearSelection: false,
                forceCurrentCellSelection: false);
            Debug.Assert(success);
        }
    }

    internal void OnInsertingColumn(int columnIndexInserted, DataGridViewColumn dataGridViewColumn, out Point newCurrentCell)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);

        if (dataGridViewColumn.DataGridView is not null)
        {
            throw new InvalidOperationException(SR.DataGridView_ColumnAlreadyBelongsToDataGridView);
        }

        if (!InInitialization
            && dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic
            && (SelectionMode == DataGridViewSelectionMode.FullColumnSelect || SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect))
        {
            throw new InvalidOperationException(string.Format(
                SR.DataGridViewColumn_SortModeAndSelectionModeClash,
                DataGridViewColumnSortMode.Automatic.ToString(),
                SelectionMode.ToString()));
        }

        if (dataGridViewColumn.Visible)
        {
            // Note that dataGridViewColumn.DataGridView is set later on, so dataGridViewColumn.InheritedAutoSizeMode should not be used

            // Make sure the column does not autosize based only on header while column headers are invisible
            if (!ColumnHeadersVisible
                && (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.ColumnHeader
                    || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet
                        && AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.ColumnHeader)))
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddAutoSizedColumn);
            }

            // Make sure the column is not frozen and auto fills
            if (dataGridViewColumn.Frozen
                && (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.Fill
                    || (dataGridViewColumn.AutoSizeMode == DataGridViewAutoSizeColumnMode.NotSet
                        && AutoSizeColumnsMode == DataGridViewAutoSizeColumnsMode.Fill)))
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddAutoFillColumn);
            }
        }

        // check for correctness of frozen state - throws exception if state is incorrect.
        CorrectColumnFrozenState(dataGridViewColumn, columnIndexInserted);

        // Reset current cell if there is one, no matter the relative position of the columns involved
        if (_ptCurrentCell.X != -1)
        {
            newCurrentCell = new Point(columnIndexInserted <= _ptCurrentCell.X ? _ptCurrentCell.X + 1 : _ptCurrentCell.X,
                 _ptCurrentCell.Y);
            ResetCurrentCell();
        }
        else
        {
            newCurrentCell = new Point(-1, -1);
        }

        // prepare the existing rows by inserting cells of correct type
        if (Rows.Count > 0)
        {
            // Only require a default cell type when there are rows to fill
            if (dataGridViewColumn.CellType is null)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAddUntypedColumn);
            }

            if (dataGridViewColumn.CellTemplate!.DefaultNewRowValue is not null && NewRowIndex != -1)
            {
                // New row needs to be un-shared before addition of new cell with a Value is not null
                // (Accessing the indexer has this side effect)
                _ = Rows[NewRowIndex];
            }

            int newColumnCount = Columns.Count + 1;

            try
            {
                for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
                {
                    DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                    if (dataGridViewRow.Cells.Count < newColumnCount)
                    {
                        DataGridViewCell dataGridViewCellNew = (DataGridViewCell)dataGridViewColumn.CellTemplate.Clone();
                        dataGridViewRow.Cells.InsertInternal(columnIndexInserted, dataGridViewCellNew);
                        if (rowIndex == NewRowIndex)
                        {
                            dataGridViewCellNew.Value = dataGridViewCellNew.DefaultNewRowValue;
                        }

                        dataGridViewCellNew.DataGridView = this;
                        dataGridViewCellNew.OwningRow = dataGridViewRow;
                        dataGridViewCellNew.OwningColumn = dataGridViewColumn;
                    }
                }
            }
            catch
            {
                // An error occurred while inserting the cells. Revert all the insertions.
                for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
                {
                    DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                    if (dataGridViewRow.Cells.Count == newColumnCount)
                    {
                        dataGridViewRow.Cells.RemoveAtInternal(columnIndexInserted);
                    }
                    else
                    {
                        Debug.Assert(dataGridViewRow.Cells.Count < newColumnCount);
                        break;
                    }
                }

                throw;
            }
        }

        // Update the indexes of selected columns to compensate for the insertion of this column
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.FullColumnSelect:
            case DataGridViewSelectionMode.ColumnHeaderSelect:
                int columnEntries = _selectedBandIndexes.Count;
                int columnEntry = 0;
                while (columnEntry < columnEntries)
                {
                    int columnIndex = _selectedBandIndexes[columnEntry];
                    if (columnIndexInserted <= columnIndex)
                    {
                        _selectedBandIndexes[columnEntry] = columnIndex + 1;
                    }

                    columnEntry++;
                }

                if (_selectedBandSnapshotIndexes is not null)
                {
                    columnEntries = _selectedBandSnapshotIndexes.Count;
                    columnEntry = 0;
                    while (columnEntry < columnEntries)
                    {
                        int columnIndex = _selectedBandSnapshotIndexes[columnEntry];
                        if (columnIndexInserted <= columnIndex)
                        {
                            _selectedBandSnapshotIndexes[columnEntry] = columnIndex + 1;
                        }

                        columnEntry++;
                    }
                }

                break;
        }
    }

    internal void OnInsertingRow(
        int rowIndexInserted,
        DataGridViewRow dataGridViewRow,
        DataGridViewElementStates rowState,
        ref Point newCurrentCell,
        bool firstInsertion,
        int insertionCount,
        bool force)
    {
        // Reset the current cell's address if it's after the inserted row.
        if (firstInsertion)
        {
            if (_ptCurrentCell.Y != -1 && rowIndexInserted <= _ptCurrentCell.Y)
            {
                newCurrentCell = new Point(_ptCurrentCell.X, _ptCurrentCell.Y + insertionCount);
                if (force)
                {
                    // When force is true, the underlying data was already added, therefore we need to avoid accessing any back-end data
                    // since we might be off by 1 row.
                    _dataGridViewState1[State1_TemporarilyResetCurrentCell] = true;
                    bool success = SetCurrentCellAddressCore(
                        columnIndex: -1,
                        rowIndex: -1,
                        setAnchorCellAddress: true,
                        validateCurrentCell: false,
                        throughMouseClick: false);
                    Debug.Assert(success);
                }
                else
                {
                    ResetCurrentCell();
                }
            }
            else
            {
                newCurrentCell = new Point(-1, -1);
            }
        }
        else
        {
            if (newCurrentCell.Y != -1)
            {
                newCurrentCell.Y += insertionCount;
            }
        }

        // For now same checks as for OnAddingRow
        OnAddingRow(dataGridViewRow, rowState, checkFrozenState: false);

        // check for correctness of frozen state - throws exception if state is incorrect.
        CorrectRowFrozenState(dataGridViewRow, rowState, rowIndexInserted);

        // Update the indexes of selected rows to compensate for the insertion of this row
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.FullRowSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
                int rowEntries = _selectedBandIndexes.Count;
                int rowEntry = 0;
                while (rowEntry < rowEntries)
                {
                    int rowIndex = _selectedBandIndexes[rowEntry];
                    if (rowIndexInserted <= rowIndex)
                    {
                        _selectedBandIndexes[rowEntry] = rowIndex + insertionCount;
                    }

                    rowEntry++;
                }

                if (_selectedBandSnapshotIndexes is not null)
                {
                    rowEntries = _selectedBandSnapshotIndexes.Count;
                    rowEntry = 0;
                    while (rowEntry < rowEntries)
                    {
                        int rowIndex = _selectedBandSnapshotIndexes[rowEntry];
                        if (rowIndexInserted <= rowIndex)
                        {
                            _selectedBandSnapshotIndexes[rowEntry] = rowIndex + insertionCount;
                        }

                        rowEntry++;
                    }
                }

                break;
        }
    }

    internal void OnInsertingRows(int rowIndexInserted, DataGridViewRow[] dataGridViewRows, ref Point newCurrentCell)
    {
        Debug.Assert(dataGridViewRows is not null);

        // Reset the current cell's address if it's after the inserted row.
        if (_ptCurrentCell.Y != -1 && rowIndexInserted <= _ptCurrentCell.Y)
        {
            newCurrentCell = new Point(_ptCurrentCell.X, _ptCurrentCell.Y + dataGridViewRows.Length);
            ResetCurrentCell();
        }
        else
        {
            newCurrentCell = new Point(-1, -1);
        }

        // For now almost same checks as for OnAddingRows
        // OnAddingRows checks for Selected status of rows.
        OnAddingRows(dataGridViewRows, checkFrozenStates: false);

        // Check for Frozen state correctness
        CorrectRowFrozenStates(dataGridViewRows, rowIndexInserted);

        // Update the indexes of selected rows to compensate for the insertion of this row
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.FullRowSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
                int rowEntries = _selectedBandIndexes.Count;
                int rowEntry = 0;
                while (rowEntry < rowEntries)
                {
                    int rowIndex = _selectedBandIndexes[rowEntry];
                    if (rowIndexInserted <= rowIndex)
                    {
                        _selectedBandIndexes[rowEntry] = rowIndex + dataGridViewRows.Length;
                    }

                    rowEntry++;
                }

                if (_selectedBandSnapshotIndexes is not null)
                {
                    rowEntries = _selectedBandSnapshotIndexes.Count;
                    rowEntry = 0;
                    while (rowEntry < rowEntries)
                    {
                        int rowIndex = _selectedBandSnapshotIndexes[rowEntry];
                        if (rowIndexInserted <= rowIndex)
                        {
                            _selectedBandSnapshotIndexes[rowEntry] = rowIndex + dataGridViewRows.Length;
                        }

                        rowEntry++;
                    }
                }

                break;
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);
        if (e.Handled)
        {
            return;
        }

        // Forward key down to current cell if any
        if (_ptCurrentCell.X != -1)
        {
            DataGridViewCell dataGridViewCell = CurrentCellInternal;
            ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
            if (dataGridViewCell.KeyDownUnsharesRowInternal(e, _ptCurrentCell.Y))
            {
                // Accessing the indexer unshares the row
                _ = Rows[_ptCurrentCell.Y];
                CurrentCellInternal.OnKeyDownInternal(e, _ptCurrentCell.Y);
            }
            else
            {
                dataGridViewCell.OnKeyDownInternal(e, _ptCurrentCell.Y);
            }
        }

        if (!e.Handled)
        {
            switch (e.KeyData & Keys.KeyCode)
            {
                case Keys.A:
                case Keys.C:
                case Keys.D0:
                case Keys.NumPad0:
                case Keys.Delete:
                case Keys.Down:
                case Keys.F2:
                case Keys.F3:
                case Keys.F10:
                case Keys.End:
                case Keys.Enter:
                case Keys.Escape:
                case Keys.Home:
                case Keys.Insert:
                case Keys.Left:
                case Keys.Next:
                case Keys.Prior:
                case Keys.Right:
                case Keys.Space:
                case Keys.Tab:
                case Keys.Up:
                    {
                        e.Handled = ProcessDataGridViewKey(e);
                        break;
                    }
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        base.OnKeyPress(e);
        if (e.Handled)
        {
            return;
        }

        // Forward key press to current cell if any
        if (_ptCurrentCell.X != -1)
        {
            DataGridViewCell dataGridViewCell = CurrentCellInternal;
            ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
            if (dataGridViewCell.KeyPressUnsharesRowInternal(e, _ptCurrentCell.Y))
            {
                // Accessing the indexer unshares the row
                _ = Rows[_ptCurrentCell.Y];
                CurrentCellInternal.OnKeyPressInternal(e, _ptCurrentCell.Y);
            }
            else
            {
                dataGridViewCell.OnKeyPressInternal(e, _ptCurrentCell.Y);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override void OnKeyUp(KeyEventArgs e)
    {
        base.OnKeyUp(e);
        if (e.Handled)
        {
            return;
        }

        if (_dataGridViewOper[OperationTrackKeyboardColResize] && (e.KeyData & Keys.Alt) != Keys.Alt)
        {
            EndColumnResize(_currentColSplitBar);
            ResetKeyboardTrackingState();
            return;
        }

        // Forward key up to current cell if any
        if (_ptCurrentCell.X != -1)
        {
            DataGridViewCell dataGridViewCell = CurrentCellInternal;
            ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
            if (dataGridViewCell.KeyUpUnsharesRowInternal(e, _ptCurrentCell.Y))
            {
                // Accessing the indexer unshares the row
                _ = Rows[_ptCurrentCell.Y];
                CurrentCellInternal.OnKeyUpInternal(e, _ptCurrentCell.Y);
            }
            else
            {
                dataGridViewCell.OnKeyUpInternal(e, _ptCurrentCell.Y);
            }
        }
    }

    protected override void OnLayout(LayoutEventArgs e)
    {
        if (_dataGridViewState1[State1_EditingControlChanging])
        {
            return;
        }

        base.OnLayout(e);
        PerformLayoutPrivate(
            useRowShortcut: false,
            computeVisibleRows: false,
            invalidInAdjustFillingColumns: false,
            repositionEditingControl: false);
        if (RightToLeftInternal)
        {
            Invalidate();
        }

        if (EditingControl is not null)
        {
            PositionEditingControl(true, true, false);
        }
    }

    protected internal override void OnLeave(EventArgs e)
    {
        if (_ptCurrentCell.X > -1 && !_dataGridViewState1[State1_LeavingWithTabKey])
        {
            DataGridViewCell? dataGridViewCell = null;
            OnCellLeave(ref dataGridViewCell, _ptCurrentCell.X, _ptCurrentCell.Y);
            if (_ptCurrentCell.X == -1)
            {
                return;
            }

            OnRowLeave(ref dataGridViewCell, _ptCurrentCell.X, _ptCurrentCell.Y);
        }

        if (!_dataGridViewState1[State1_LeavingWithTabKey])
        {
            base.OnLeave(e);

            // invalidate the current cell so the data grid view does not paint the focus rectangle any longer
            if (_ptCurrentCell.X > -1 && _ptCurrentCell.Y > -1)
            {
                InvalidateCellPrivate(columnIndex: _ptCurrentCell.X, rowIndex: _ptCurrentCell.Y);
            }
        }

        // Erase focus rectangle around the grid
        if (IsGridFocusRectangleEnabled())
        {
            InvalidateRectangleEdges(GetGridFocusRectangle());
        }
    }

    protected override void OnLostFocus(EventArgs e)
    {
        base.OnLostFocus(e);
        if (_ptCurrentCell.X != -1)
        {
            InvalidateCell(_ptCurrentCell.X, _ptCurrentCell.Y);
        }

        if (CurrentCell is not null)
        {
            KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(CurrentCell);
        }
    }

    protected override void OnMouseClick(MouseEventArgs e)
    {
        bool mouseClickRaised = false;

        if (!_dataGridViewState2[State2_MessageFromEditingCtrls]
            && !_dataGridViewOper[OperationInDispose]
            && !IsDisposed
            && !_dataGridViewOper[OperationTrackColResize]
            && !_dataGridViewOper[OperationTrackRowResize]
            && !_dataGridViewOper[OperationTrackColHeadersResize]
            && !_dataGridViewOper[OperationTrackRowHeadersResize])
        {
            HitTestInfo hti = HitTest(e.X, e.Y);

            if (_ptMouseDownCell.X == hti._col
                && _ptMouseDownCell.Y == hti._row
                && (e.Button != MouseButtons.Left
                    || _ptMouseDownCell.X == -1
                    || _ptMouseDownCell.Y == -1
                    || (_ptMouseDownCell.X == _ptCurrentCell.X && _ptMouseDownCell.Y == _ptCurrentCell.Y)))
            {
                DataGridViewCellMouseEventArgs? dgvcme = null;
                if (hti.Type is not DataGridViewHitTestType.None
                    and not DataGridViewHitTestType.HorizontalScrollBar
                    and not DataGridViewHitTestType.VerticalScrollBar)
                {
                    int mouseX = e.X - hti.ColumnX;
                    if (RightToLeftInternal)
                    {
                        mouseX += ((hti._col == -1) ? RowHeadersWidth : Columns[hti._col].Thickness);
                    }

                    dgvcme = new DataGridViewCellMouseEventArgs(hti._col, hti._row, mouseX, e.Y - hti.RowY, e);
                    RecordCellMouseClick(dgvcme);
                    if (e.Button == MouseButtons.Left)
                    {
                        OnCellClick(new DataGridViewCellEventArgs(hti._col, hti._row));
                    }

                    base.OnMouseClick(e);
                    mouseClickRaised = true;
                    if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                    {
                        OnCellMouseClick(dgvcme);
                    }
                }
                else
                {
                    base.OnMouseClick(e);
                    mouseClickRaised = true;
                }

                if (!_dataGridViewOper[OperationTrackColRelocation])
                {
                    switch (hti._typeInternal)
                    {
                        case DataGridViewHitTestTypeInternal.ColumnHeader:
                        case DataGridViewHitTestTypeInternal.ColumnHeaderLeft:
                        case DataGridViewHitTestTypeInternal.ColumnHeaderRight:
                        case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft:
                            {
                                Debug.Assert(dgvcme is not null);
                                if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                                {
                                    OnColumnHeaderMouseClick(dgvcme);
                                }

                                break;
                            }

                        case DataGridViewHitTestTypeInternal.RowHeader:
                            {
                                Debug.Assert(dgvcme is not null);
                                if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                                {
                                    OnRowHeaderMouseClick(dgvcme);
                                }

                                break;
                            }
                    }
                }
            }
        }

        if (!mouseClickRaised)
        {
            base.OnMouseClick(e);
        }
    }

    protected override void OnMouseDoubleClick(MouseEventArgs e)
    {
        base.OnMouseDoubleClick(e);

        if (!_dataGridViewState2[State2_MessageFromEditingCtrls]
            && !_dataGridViewOper[OperationTrackColResize]
            && !_dataGridViewOper[OperationTrackRowResize]
            && !_dataGridViewOper[OperationTrackColHeadersResize]
            && !_dataGridViewOper[OperationTrackRowHeadersResize])
        {
            HitTestInfo hti = HitTest(e.X, e.Y);

            if (_ptMouseDownCell.X == hti._col && _ptMouseDownCell.Y == hti._row)
            {
                DataGridViewCellMouseEventArgs? dgvcme = null;
                if (hti.Type is not DataGridViewHitTestType.None
                    and not DataGridViewHitTestType.HorizontalScrollBar
                    and not DataGridViewHitTestType.VerticalScrollBar)
                {
                    int mouseX = e.X - hti.ColumnX;
                    if (RightToLeftInternal)
                    {
                        mouseX += ((hti._col == -1) ? RowHeadersWidth : Columns[hti._col].Thickness);
                    }

                    dgvcme = new DataGridViewCellMouseEventArgs(hti._col, hti._row, mouseX, e.Y - hti.RowY, e);
                    OnCellMouseDoubleClick(dgvcme);
                }

                if (!_dataGridViewOper[OperationTrackColRelocation])
                {
                    switch (hti._typeInternal)
                    {
                        case DataGridViewHitTestTypeInternal.ColumnHeader:
                        case DataGridViewHitTestTypeInternal.ColumnHeaderLeft:
                        case DataGridViewHitTestTypeInternal.ColumnHeaderRight:
                        case DataGridViewHitTestTypeInternal.FirstColumnHeaderLeft:
                            {
                                Debug.Assert(dgvcme is not null);
                                if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                                {
                                    OnColumnHeaderMouseDoubleClick(dgvcme);
                                }

                                break;
                            }

                        case DataGridViewHitTestTypeInternal.ColumnResizeLeft:
                        case DataGridViewHitTestTypeInternal.ColumnResizeRight:
                            {
                                int columnIndex = (hti._typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight) ? hti._col : hti._adjacentCol;
                                if (columnIndex < Columns.Count)
                                {
                                    HandledMouseEventArgs hme = new(
                                        e.Button,
                                        e.Clicks,
                                        e.X,
                                        e.Y,
                                        e.Delta,
                                        defaultHandledValue: false);
                                    DataGridViewColumnDividerDoubleClickEventArgs dgvcddce = new(columnIndex, hme);
                                    Debug.Assert(Columns[columnIndex].Resizable == DataGridViewTriState.True);
                                    OnColumnDividerDoubleClick(dgvcddce);
                                }

                                break;
                            }

                        case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop:
                        case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom:
                        case DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop:
                        case DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom:
                            {
                                HandledMouseEventArgs hme = new(
                                    e.Button,
                                    e.Clicks,
                                    e.X,
                                    e.Y,
                                    e.Delta,
                                    defaultHandledValue: false);
                                DataGridViewRowDividerDoubleClickEventArgs dgvrddce = new(-1, hme);
                                Debug.Assert(_columnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.EnableResizing);
                                OnRowDividerDoubleClick(dgvrddce);
                                break;
                            }

                        case DataGridViewHitTestTypeInternal.RowHeader:
                            {
                                Debug.Assert(dgvcme is not null);
                                if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                                {
                                    OnRowHeaderMouseDoubleClick(dgvcme);
                                }

                                break;
                            }

                        case DataGridViewHitTestTypeInternal.RowResizeBottom:
                        case DataGridViewHitTestTypeInternal.RowResizeTop:
                            {
                                int rowIndex = (hti._typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom) ? hti._row : hti._adjacentRow;
                                if (rowIndex < Rows.Count)
                                {
                                    HandledMouseEventArgs hme = new(
                                        e.Button,
                                        e.Clicks,
                                        e.X,
                                        e.Y,
                                        e.Delta,
                                        defaultHandledValue: false);
                                    DataGridViewRowDividerDoubleClickEventArgs dgvrddce = new(rowIndex, hme);
                                    Debug.Assert(Rows[rowIndex].Resizable == DataGridViewTriState.True);
                                    OnRowDividerDoubleClick(dgvrddce);
                                }

                                break;
                            }

                        case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft:
                        case DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight:
                        case DataGridViewHitTestTypeInternal.RowHeadersResizeLeft:
                        case DataGridViewHitTestTypeInternal.RowHeadersResizeRight:
                            {
                                HandledMouseEventArgs hme = new(
                                    e.Button,
                                    e.Clicks,
                                    e.X,
                                    e.Y,
                                    e.Delta,
                                    defaultHandledValue: false);
                                DataGridViewColumnDividerDoubleClickEventArgs dgvcddce = new(-1, hme);
                                Debug.Assert(_rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.EnableResizing);
                                OnColumnDividerDoubleClick(dgvcddce);
                                break;
                            }
                    }
                }
            }
        }
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        if (!_dataGridViewState2[State2_MessageFromEditingCtrls])
        {
            _dataGridViewOper[OperationTrackMouseMoves] = true;
        }

        base.OnMouseDown(e);

        if (_dataGridViewState2[State2_MessageFromEditingCtrls])
        {
            return;
        }

        if (_ptMouseDownCell.X != -2)
        {
            // Happens when user pushes the mouse wheel down while the left mouse button is already down
            Debug.Assert(_ptMouseDownCell.Y != -2);
            return;
        }

        HitTestInfo hti = HitTest(e.X, e.Y);

        if (hti.Type is not DataGridViewHitTestType.None
            and not DataGridViewHitTestType.HorizontalScrollBar
            and not DataGridViewHitTestType.VerticalScrollBar)
        {
            _ptMouseDownCell.X = hti._col;
            _ptMouseDownCell.Y = hti._row;
            _ptMouseDownGridCoord = new Point(e.X, e.Y);
            int mouseX = e.X - hti.ColumnX;
            if (RightToLeftInternal)
            {
                mouseX += ((hti._col == -1) ? RowHeadersWidth : Columns[hti._col].Thickness);
            }

            DataGridViewCellMouseEventArgs dgvcme = new(hti._col, hti._row, mouseX, e.Y - hti.RowY, e);
            OnCellMouseDown(dgvcme);
        }
    }

    protected override void OnMouseEnter(EventArgs e)
    {
        if (!_dataGridViewState2[State2_MouseOverRemovedEditingCtrl]
            && !_dataGridViewState2[State2_MouseOverRemovedEditingPanel]
            && _dataGridViewState2[State2_MouseEnterExpected]
            && !_toolTipControl.Activated)
        {
            base.OnMouseEnter(e);
        }

        _dataGridViewState2[State2_MouseOverRemovedEditingCtrl] = false;
        _dataGridViewState2[State2_MouseOverRemovedEditingPanel] = false;
        _dataGridViewState2[State2_MouseEnterExpected] = false;
    }

    protected override void OnMouseLeave(EventArgs e)
    {
        // when the mouse leaves the dataGridView control, reset the cursor to the previously cached one
        if (_dataGridViewState1[State1_CustomCursorSet])
        {
            _dataGridViewState1[State1_CustomCursorSet] = false;
            CursorInternal = _oldCursor;
        }

        bool mouseOverEditingControl = MouseOverEditingControl;
        bool mouseOverEditingPanel = MouseOverEditingPanel;
        bool mouseOverToolTipControl = _toolTipControl.Activated && ClientRectangle.Contains(PointToClient(MousePosition));

        if (!mouseOverEditingPanel && !mouseOverEditingControl && !mouseOverToolTipControl && _ptMouseEnteredCell.X != -2)
        {
            if (_ptMouseEnteredCell.X >= -1 && _ptMouseEnteredCell.X < Columns.Count
                && _ptMouseEnteredCell.Y >= -1 && _ptMouseEnteredCell.Y < Rows.Count)
            {
                OnCellMouseLeave(new(_ptMouseEnteredCell.X, _ptMouseEnteredCell.Y));
            }
            else
            {
                _ptMouseEnteredCell.X = _ptMouseEnteredCell.Y = -2;
            }
        }

        ResetTrackingState();
        _dataGridViewOper[OperationTrackMouseMoves] = false;

        if (!mouseOverEditingPanel && !mouseOverEditingControl && !mouseOverToolTipControl && !MouseOverScrollBar)
        {
            _toolTipControl.Activate(activate: false);
            base.OnMouseLeave(e);
            _dataGridViewState2[State2_MouseEnterExpected] = true;
        }
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);

        HitTestInfo hti = HitTest(e.X, e.Y);

        UpdateMouseEnteredCell(hti, e);

        // We need to give UI feedback when the user is resizing a column
        if (_dataGridViewOper[OperationTrackColResize])
        {
            MoveRowHeadersOrColumnResize(e.X);
        }
        else if (_dataGridViewOper[OperationTrackRowResize])
        {
            MoveColumnHeadersOrRowResize(e);
        }
        else if (_dataGridViewOper[OperationTrackColRelocation])
        {
            MoveColumnRelocation(e, hti);
        }
        else if (_dataGridViewOper[OperationTrackColHeadersResize])
        {
            MoveColumnHeadersOrRowResize(e);
        }
        else if (_dataGridViewOper[OperationTrackRowHeadersResize])
        {
            MoveRowHeadersOrColumnResize(e.X);
        }

        if (_dataGridViewOper[OperationTrackColResize]
            || _dataGridViewOper[OperationTrackRowHeadersResize]
            || ((hti._typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeLeft
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeLeft
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeRight
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.ColumnResizeRight
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.RowHeadersResizeLeft
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.RowHeadersResizeRight)
                && !_dataGridViewOper[OperationTrackColHeadersResize]
                && !_dataGridViewOper[OperationTrackColRelocation]
                && !_dataGridViewOper[OperationTrackColSelect]
                && !_dataGridViewOper[OperationTrackRowSelect]
                && !_dataGridViewOper[OperationTrackCellSelect]))
        {
            if (!_dataGridViewState1[State1_CustomCursorSet])
            {
                _dataGridViewState1[State1_CustomCursorSet] = true;
                _oldCursor = Cursor;
            }

            CursorInternal = Cursors.SizeWE;
            return;
        }
        else if (_dataGridViewOper[OperationTrackRowResize]
            || _dataGridViewOper[OperationTrackColHeadersResize]
            || ((hti._typeInternal == DataGridViewHitTestTypeInternal.RowResizeBottom
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeTop
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.TopLeftHeaderResizeBottom
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.RowResizeTop
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeTop
                    || hti._typeInternal == DataGridViewHitTestTypeInternal.ColumnHeadersResizeBottom)
                && !_dataGridViewOper[OperationTrackRowHeadersResize]
                && !_dataGridViewOper[OperationTrackColRelocation]
                && !_dataGridViewOper[OperationTrackColSelect]
                && !_dataGridViewOper[OperationTrackRowSelect]
                && !_dataGridViewOper[OperationTrackCellSelect]))
        {
            if (!_dataGridViewState1[State1_CustomCursorSet])
            {
                _dataGridViewState1[State1_CustomCursorSet] = true;
                _oldCursor = Cursor;
            }

            CursorInternal = Cursors.SizeNS;
            return;
        }
        else if (_dataGridViewState1[State1_CustomCursorSet])
        {
            _dataGridViewState1[State1_CustomCursorSet] = false;
            CursorInternal = _oldCursor;
        }

        if (_dataGridViewOper[OperationTrackColSelect] || _dataGridViewOper[OperationTrackRowSelect] || _dataGridViewOper[OperationTrackCellSelect])
        {
            int mouseX = e.X, mouseY = e.Y;
            if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out int xOffset, out int yOffset))
            {
                if (xOffset == 0)
                {
                    if (_horizScrollTimer is not null && _horizScrollTimer.Enabled)
                    {
                        // Mouse's X came in-bound - need to stop the horizontal scroll timer
                        _horizScrollTimer.Enabled = false;
                    }
                }
                else if (_horizScrollTimer is null || !_horizScrollTimer.Enabled)
                {
                    // Need to start delayed horizontal scroll
                    HorizScrollTimer.Interval = GetColumnScrollRate(Math.Abs(xOffset));
                    HorizScrollTimer.Enabled = true;
                }

                if (yOffset == 0)
                {
                    if (_vertScrollTimer is not null && _vertScrollTimer.Enabled)
                    {
                        // Mouse's Y came in-bound - need to stop the vertical scroll timer
                        _vertScrollTimer.Enabled = false;
                    }
                }
                else if (_vertScrollTimer is null || !_vertScrollTimer.Enabled)
                {
                    // Need to start delayed vertical scroll
                    VertScrollTimer.Interval = GetRowScrollRate(Math.Abs(yOffset));
                    VertScrollTimer.Enabled = true;
                }

                if (HorizScrollTimer.Enabled || VertScrollTimer.Enabled)
                {
                    return;
                }

                if (hti.Type is not DataGridViewHitTestType.None
                    and not DataGridViewHitTestType.HorizontalScrollBar
                    and not DataGridViewHitTestType.VerticalScrollBar)
                {
                    if (_dataGridViewOper[OperationTrackColSelect] && hti._col >= 0)
                    {
                        OnColumnSelectMouseMove(hti);
                    }
                    else if (_dataGridViewOper[OperationTrackRowSelect] && hti._row >= 0)
                    {
                        OnRowSelectMouseMove(hti);
                    }
                    else if (_dataGridViewOper[OperationTrackCellSelect] && hti._col >= 0 && hti._row >= 0)
                    {
                        OnCellSelectMouseMove(hti);
                    }
                }
            }
        }
#if DEBUG
        else
        {
            Debug.Assert(_vertScrollTimer is null || !_vertScrollTimer.Enabled);
            Debug.Assert(_horizScrollTimer is null || !_horizScrollTimer.Enabled);
        }
#endif
        if (!_toolTipControl.Activated)
        {
            //
            // After processing the MouseMove event, the tool tip is still not activated.
            // Reset the tool tip cell.
            _ptToolTipCell = new Point(-1, -1);
        }
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        if (!_dataGridViewState2[State2_MessageFromEditingCtrls])
        {
            _dataGridViewState1[State1_ScrolledSinceMouseDown] = false;

            HitTestInfo hti = HitTest(e.X, e.Y);

            if (!IsMouseOperationActive())
            {
                if (hti.Type is not DataGridViewHitTestType.None
                    and not DataGridViewHitTestType.HorizontalScrollBar
                    and not DataGridViewHitTestType.VerticalScrollBar)
                {
                    int mouseX = e.X - hti.ColumnX;
                    if (RightToLeftInternal)
                    {
                        mouseX += ((hti._col == -1) ? RowHeadersWidth : Columns[hti._col].Thickness);
                    }

                    DataGridViewCellMouseEventArgs dgvcme;
                    if (_dataGridViewState2[State2_NextMouseUpIsDouble])
                    {
                        MouseEventArgs meTmp = new(e.Button, 2, e.X, e.Y, e.Delta);
                        dgvcme = new DataGridViewCellMouseEventArgs(hti._col, hti._row, mouseX, e.Y - hti.RowY, meTmp);
                    }
                    else
                    {
                        dgvcme = new DataGridViewCellMouseEventArgs(hti._col, hti._row, mouseX, e.Y - hti.RowY, e);
                    }

                    if (hti._col >= 0
                        && _ptMouseDownCell.X == hti._col
                        && hti._row >= 0
                        && _ptMouseDownCell.Y == hti._row
                        && EditMode == DataGridViewEditMode.EditOnEnter
                        && EditingControl is not null)
                    {
                        OnClick(e);
                        OnMouseClick(e);
                    }

                    CorrectFocus(onlyIfGridHasFocus: true);

                    if (dgvcme.ColumnIndex < Columns.Count && dgvcme.RowIndex < Rows.Count)
                    {
                        OnCellMouseUp(dgvcme);
                    }
                }
                else if (hti.Type == DataGridViewHitTestType.None)
                {
                    // VS Whidbey
                    CorrectFocus(onlyIfGridHasFocus: true);
                }
            }
            else
            {
                if (_dataGridViewOper[OperationTrackColResize])
                {
                    EndColumnResize(e);
                }

                if (_dataGridViewOper[OperationTrackRowResize])
                {
                    EndRowResize(e);
                }

                if (_dataGridViewOper[OperationTrackColRelocation])
                {
                    EndColumnRelocation(e, hti);
                }

                if (_dataGridViewOper[OperationTrackColHeadersResize])
                {
                    EndColumnHeadersResize(e);
                }

                if (_dataGridViewOper[OperationTrackRowHeadersResize])
                {
                    EndRowHeadersResize(e);
                }

                // VS Whidbey
                CorrectFocus(onlyIfGridHasFocus: true);

                // Updating the hit test info since the EndXXX operation above may have invalidated the previously
                // determined hti.
                hti = HitTest(e.X, e.Y);
                if (hti.Type is not DataGridViewHitTestType.None
                    and not DataGridViewHitTestType.HorizontalScrollBar
                    and not DataGridViewHitTestType.VerticalScrollBar)
                {
                    int mouseX = e.X - hti.ColumnX;
                    if (RightToLeftInternal)
                    {
                        mouseX += ((hti._col == -1) ? RowHeadersWidth : Columns[hti._col].Thickness);
                    }

                    OnCellMouseUp(new DataGridViewCellMouseEventArgs(hti._col, hti._row, mouseX, e.Y - hti.RowY, e));
                }
            }

            ResetTrackingState();
        }

        base.OnMouseUp(e);
    }

    protected override void OnMouseWheel(MouseEventArgs e)
    {
        base.OnMouseWheel(e);

        HandledMouseEventArgs? hme = e as HandledMouseEventArgs;
        if (hme is not null && hme.Handled)
        {
            // The application event handler handled the scrolling - don't do anything more.
            return;
        }

        if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None)
        {
            return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down.
        }

        bool verticalScroll = ((ModifierKeys & Keys.Control) == 0);

        ScrollBar sb = (verticalScroll ? _vertScrollBar : _horizScrollBar);

        if (!sb.Visible || !sb.Enabled)
        {
            return; // Do not scroll when the corresponding scrollbar is invisible or disabled
        }

        if (hme is not null)
        {
            hme.Handled = true;
        }

        int wheelScrollLines = SystemInformation.MouseWheelScrollLines;
        if (wheelScrollLines == 0)
        {
            return; // Do not scroll when the user system setting is 0 lines per notch
        }

        Debug.Assert(_cumulativeVerticalWheelDelta > -PInvoke.WHEEL_DELTA);
        Debug.Assert(_cumulativeVerticalWheelDelta < PInvoke.WHEEL_DELTA);
        Debug.Assert(_cumulativeHorizontalWheelDelta > -PInvoke.WHEEL_DELTA);
        Debug.Assert(_cumulativeHorizontalWheelDelta < PInvoke.WHEEL_DELTA);

        float partialNotches;

        if (verticalScroll)
        {
            _cumulativeVerticalWheelDelta += e.Delta;
            partialNotches = _cumulativeVerticalWheelDelta / (float)PInvoke.WHEEL_DELTA;
        }
        else
        {
            _cumulativeHorizontalWheelDelta += e.Delta;
            partialNotches = _cumulativeHorizontalWheelDelta / (float)PInvoke.WHEEL_DELTA;
        }

        int fullNotches = (int)partialNotches;

        if (wheelScrollLines == -1)
        {
            // Equivalent to large change scrolls
            if (fullNotches != 0)
            {
                if (_ptCurrentCell.X >= 0 && !CommitEdit(
                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll,
                    forCurrentCellChange: false,
                    forCurrentRowChange: false))
                {
                    // Could not commit edited cell value
                    return;
                }

                if (verticalScroll)
                {
                    int originalVerticalOffset = VerticalOffset;
                    VerticalOffset -= fullNotches * _vertScrollBar.LargeChange;
                    if (Math.Abs(VerticalOffset - originalVerticalOffset) >= Math.Abs(fullNotches * _vertScrollBar.LargeChange))
                    {
                        _cumulativeVerticalWheelDelta -= fullNotches * (int)PInvoke.WHEEL_DELTA;
                    }
                    else
                    {
                        _cumulativeVerticalWheelDelta = 0;
                    }
                }
                else
                {
                    int originalHorizontalOffset = HorizontalOffset;
                    HorizontalOffset -= fullNotches * _horizScrollBar.LargeChange;
                    if (Math.Abs(HorizontalOffset - originalHorizontalOffset) >= Math.Abs(fullNotches * _horizScrollBar.LargeChange))
                    {
                        _cumulativeHorizontalWheelDelta -= fullNotches * (int)PInvoke.WHEEL_DELTA;
                    }
                    else
                    {
                        _cumulativeHorizontalWheelDelta = 0;
                    }
                }
            }
        }
        else
        {
            // Evaluate number of bands to scroll
            int scrollBands = (int)(wheelScrollLines * partialNotches);
            if (scrollBands != 0)
            {
                if (_ptCurrentCell.X >= 0 && !CommitEdit(
                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll,
                    forCurrentCellChange: false,
                    forCurrentRowChange: false))
                {
                    // Could not commit edited cell value
                    return;
                }

                int absScrollBands;
                if (verticalScroll)
                {
                    if (scrollBands > 0)
                    {
                        absScrollBands = scrollBands;
                        while (_vertScrollBar.Value != _vertScrollBar.Minimum && absScrollBands > 0)
                        {
                            ScrollRowsByCount(-1, ScrollEventType.SmallDecrement);
                            absScrollBands--;
                        }

                        if (_vertScrollBar.Value == _vertScrollBar.Minimum)
                        {
                            _cumulativeVerticalWheelDelta = 0;
                        }
                        else
                        {
                            _cumulativeVerticalWheelDelta -= (int)(scrollBands * (PInvoke.WHEEL_DELTA / (float)wheelScrollLines));
                        }
                    }
                    else
                    {
                        absScrollBands = -scrollBands;
                        Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
                        int totalVisibleFrozenHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                        while (absScrollBands > 0
                            && _vertScrollBar.Value + Rows.SharedRow(DisplayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(DisplayedBandsInfo.FirstDisplayedScrollingRow)
                                <= _vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight))
                        {
                            ScrollRowsByCount(1, ScrollEventType.SmallIncrement);

                            // Assuming totalVisibleFrozenHeight is unchanged by scrolling operation
                            Debug.Assert(totalVisibleFrozenHeight == Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen));
                            Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
                            absScrollBands--;
                        }

                        if (_vertScrollBar.Value + Rows.SharedRow(DisplayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(DisplayedBandsInfo.FirstDisplayedScrollingRow)
                            > _vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight))
                        {
                            _cumulativeVerticalWheelDelta = 0;
                        }
                        else
                        {
                            _cumulativeVerticalWheelDelta -= (int)(scrollBands * (PInvoke.WHEEL_DELTA / (float)wheelScrollLines));
                        }
                    }
                }
                else
                {
                    int extremeScrollBarValue, scrollBand;
                    if (scrollBands > 0)
                    {
                        extremeScrollBarValue = _horizScrollBar.Minimum;
                        scrollBand = -1;
                    }
                    else
                    {
                        extremeScrollBarValue = _horizScrollBar.Maximum;
                        scrollBand = 1;
                    }

                    absScrollBands = Math.Abs(scrollBands);
                    while (_horizScrollBar.Value != extremeScrollBarValue && absScrollBands > 0)
                    {
                        ScrollColumns(scrollBand);
                        absScrollBands--;
                    }

                    if (_horizScrollBar.Value == extremeScrollBarValue)
                    {
                        _cumulativeHorizontalWheelDelta = 0;
                    }
                    else
                    {
                        _cumulativeHorizontalWheelDelta -= (int)(scrollBands * (PInvoke.WHEEL_DELTA / (float)wheelScrollLines));
                    }
                }
            }
        }
    }

    internal void OnMouseWheelInternal(MouseEventArgs e)
    {
        // Notification forwarded from editing control
        OnMouseWheel(e);
    }

    protected virtual void OnMultiSelectChanged(EventArgs e) =>
        GetEvent<EventHandler>(s_multiselectChangedEvent)?.Invoke(this, e);

    protected virtual void OnNewRowNeeded(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        GetEvent<DataGridViewRowEventHandler>(s_newRowNeededEvent)?.Invoke(this, e);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        try
        {
            // We can't paint if we are disposed.
            if (_dataGridViewOper[OperationInDispose] || IsDisposed)
            {
                return;
            }

            if (_layout._dirty)
            {
                PerformLayoutPrivate(
                    useRowShortcut: false,
                    computeVisibleRows: true,
                    invalidInAdjustFillingColumns: false,
                    repositionEditingControl: false);
            }

            Graphics g = e.GraphicsInternal;
            Rectangle clipRect = e.ClipRectangle;
            Rectangle gridRect = GetGridRectangle();

            if (_currentRowSplitBar != -1)
            {
                clipRect = Rectangle.Union(clipRect, CalcRowResizeFeedbackRect(_currentRowSplitBar));
            }
            else if (_currentColSplitBar != -1)
            {
                clipRect = Rectangle.Union(clipRect, CalcColResizeFeedbackRect(_currentColSplitBar));
            }

            if (clipRect.IntersectsWith(gridRect))
            {
                using GraphicsClipScope clipScope = new(g);
                g.SetClip(gridRect);
                PaintBackground(g, clipRect, gridRect);
                PaintGrid(g, gridRect, clipRect, SingleVerticalBorderAdded, SingleHorizontalBorderAdded);
            }

            PaintBorder(g, clipRect, _layout.ClientRectangle);
            if (clipRect.IntersectsWith(_layout.ResizeBoxRect))
            {
                g.FillRectangle(SystemBrushes.Control, _layout.ResizeBoxRect);
            }

            // Draw focus rectangle around the grid
            if (Focused && IsGridFocusRectangleEnabled())
            {
                if (SystemInformation.HighContrast)
                {
                    ControlPaint.DrawHighContrastFocusRectangle(g, GetGridFocusRectangle(), SystemColors.ActiveCaptionText);
                }
                else
                {
                    ControlPaint.DrawFocusRectangle(g, GetGridFocusRectangle());
                }
            }

            // Raise paint event
            base.OnPaint(e);
        }
        catch (Exception ex) when (!ex.IsCriticalException())
        {
            Debug.Fail($"DataGridView.OnPaint exception: {ex.Message} stack trace {ex.StackTrace}");
        }
    }

    // Determines if a focus rectangle may be drawn along the perimeter of the DataGridView control
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private bool IsGridFocusRectangleEnabled()
    {
        return ShowFocusCues && CurrentCell is null;
    }

    // Creates a rectangle by merging row headers, column headers
    // and cells rectangles (from layout data)
    private Rectangle GetGridRectangle()
    {
        Rectangle gridRect = _layout.Data;
        if (_layout.RowHeadersVisible)
        {
            gridRect = Rectangle.Union(gridRect, _layout.RowHeaders);
        }
        else if (SingleVerticalBorderAdded)
        {
            if (!RightToLeftInternal)
            {
                gridRect.X--;
            }

            gridRect.Width++;
        }

        if (_layout.ColumnHeadersVisible)
        {
            gridRect = Rectangle.Union(gridRect, _layout.ColumnHeaders);
        }
        else if (SingleHorizontalBorderAdded)
        {
            if (gridRect.Y == _layout.Data.Y)
            {
                gridRect.Y--;
                gridRect.Height++;
            }
        }

        return gridRect;
    }

    // Creates a grid focus rectangle
    private Rectangle GetGridFocusRectangle()
    {
        Rectangle focusRect = GetGridRectangle();
        focusRect.Inflate(1 - FocusRectOffset, 1 - FocusRectOffset);
        return focusRect;
    }

    private void InvalidateGridFocusOnScroll(int change, ScrollOrientation orientation)
    {
        if (change == 0)
        {
            return;
        }

        Rectangle focusRect = GetGridFocusRectangle();

        if (orientation == ScrollOrientation.HorizontalScroll)
        {
            // Scroll right
            if (change > 0)
            {
                focusRect.Width -= change;
            }

            // Scroll left
            else
            {
                focusRect.X -= change;
                focusRect.Width += change;
            }
        }
        else
        {
            // Scroll down
            if (change > 0)
            {
                focusRect.Height -= change;
            }

            // Scroll up
            else
            {
                focusRect.Y -= change;
                focusRect.Height += change;
            }
        }

        InvalidateRectangleEdges(focusRect);
    }

    private void InvalidateRectangleEdges(Rectangle rect)
    {
        // Left edge
        Rectangle edge = rect;
        edge.Width = 1;
        Invalidate(edge);

        // Right edge
        edge.X += rect.Width - 1;
        Invalidate(edge);

        // Top edge
        edge = rect;
        edge.Height = 1;
        Invalidate(edge);

        // Bottom edge
        edge.Y += rect.Height - 1;
        Invalidate(edge);
    }

    internal override void OnParentBecameInvisible()
    {
        base.OnParentBecameInvisible();
        if (GetState(States.Visible))
        {
            // This control became invisible too - Update the Displayed properties of the bands.
            OnVisibleChangedPrivate();
        }
    }

    protected virtual void OnReadOnlyChanged(EventArgs e)
    {
        GetEvent<EventHandler>(s_readOnlyChangedEvent)?.Invoke(this, e);
        VerifyImeRestrictedModeChanged();

        if (!ReadOnly
            && _ptCurrentCell.X != -1
            && ColumnEditable(_ptCurrentCell.X)
            && !IsCurrentCellInEditMode
            && (EditMode == DataGridViewEditMode.EditOnEnter
                || (EditMode != DataGridViewEditMode.EditProgrammatically && CurrentCellInternal.EditType is null))
            && !Rows[_ptCurrentCell.Y].Cells[_ptCurrentCell.X].ReadOnly) // Un-shares the row
        {
            // Current cell becomes read/write. Enter editing mode.
            BeginEditInternal(selectAll: true);
        }
    }

    internal void OnRemovedColumn_PreNotification(DataGridViewColumn dataGridViewColumn)
    {
        Debug.Assert(dataGridViewColumn.Index >= 0);
        Debug.Assert(dataGridViewColumn.DataGridView is null);

        // Clear the potential header sort glyph
        if (dataGridViewColumn.HasHeaderCell)
        {
            dataGridViewColumn.HeaderCell.SortGlyphDirectionInternal = SortOrder.None;
        }

        // Intentionally keep the DisplayIndex intact after detaching the column.
        CorrectColumnIndexesAfterDeletion(dataGridViewColumn);

        CorrectColumnDisplayIndexesAfterDeletion(dataGridViewColumn);

        // Fix the OldFirstDisplayedScrollingCol
        DisplayedBandsInfo.CorrectRowIndexAfterDeletion(dataGridViewColumn.Index);

        // Raise the ColumnRemoved event
        OnColumnRemoved(dataGridViewColumn);
    }

    internal void OnRemovedColumn_PostNotification(DataGridViewColumn dataGridViewColumn, Point newCurrentCell)
    {
        // Update current cell if needed
        if (newCurrentCell.X != -1)
        {
            Debug.Assert(_ptCurrentCell.X == -1);
            bool success = SetAndSelectCurrentCellAddress(
                newCurrentCell.X,
                newCurrentCell.Y,
                setAnchorCellAddress: true,
                validateCurrentCell: false,
                throughMouseClick: false,
                clearSelection: false,
                forceCurrentCellSelection: false);
            Debug.Assert(success);
        }

        // Raise SelectionChanged event if needed
        FlushSelectionChanged();

        // Raise ColumnStateChanged event for Displayed state of deleted column
        OnColumnHidden(dataGridViewColumn);

        // Columns that are removed from the collection take their non-autosized width
        // Note that in some edge cases, the dev could have changed:
        //     - the grid's AutoSizeColumnsMode
        //     - the column's Width or AutoSizeMode
        // in a ColumnRemoved event handler for example, in which case using the CachedThickness may be wrong.
        // At least we make sure the column is not sized smaller than its minimum width.
        DataGridViewAutoSizeColumnMode autoSizeColumnMode = dataGridViewColumn.GetInheritedAutoSizeMode(this);
        Debug.Assert(autoSizeColumnMode != DataGridViewAutoSizeColumnMode.NotSet);
        if (autoSizeColumnMode != DataGridViewAutoSizeColumnMode.None
            && autoSizeColumnMode != DataGridViewAutoSizeColumnMode.Fill
            && dataGridViewColumn.ThicknessInternal != dataGridViewColumn.CachedThickness)
        {
            dataGridViewColumn.ThicknessInternal = Math.Max(dataGridViewColumn.MinimumWidth, dataGridViewColumn.CachedThickness);
        }

        // Autosize rows if needed
        AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
    }

    internal void OnRemovedRow_PreNotification(int rowIndexDeleted)
    {
        // Fix the OldFirstDisplayedScrollingRow
        DisplayedBandsInfo.CorrectRowIndexAfterDeletion(rowIndexDeleted);

        CorrectRowIndexesAfterDeletion(rowIndexDeleted);
        ComputeVisibleRows();
    }

    internal void OnRemovedRow_PostNotification(DataGridViewRow dataGridViewRow, Point newCurrentCell)
    {
        // Update current cell if needed
        if (newCurrentCell.Y != -1)
        {
            Debug.Assert(_ptCurrentCell.X == -1);
            bool success = SetAndSelectCurrentCellAddress(
                newCurrentCell.X,
                newCurrentCell.Y,
                setAnchorCellAddress: true,
                validateCurrentCell: false,
                throughMouseClick: false,
                clearSelection: false,
                forceCurrentCellSelection: false);
            Debug.Assert(success);
        }

        // Raise SelectionChange event if needed
        FlushSelectionChanged();

        bool rowDisplayed = dataGridViewRow.DataGridView is null && dataGridViewRow.Displayed;

        // Raise RowStateChanged event for Displayed state of deleted row
        if (rowDisplayed)
        {
            dataGridViewRow.Displayed = false;
            DataGridViewRowStateChangedEventArgs dgvrsce = new(dataGridViewRow, DataGridViewElementStates.Displayed);
            OnRowStateChanged(rowIndex: -1, dgvrsce);
        }

        // Rows that are removed from the collection take their non-autosized height
        // Note that in some edge cases, the dev could have changed:
        //     - the grid's AutoSizeRowsMode
        //     - the row's Height
        // in a RowsRemoved event handler for example, in which case using the CachedThickness may be wrong.
        // At least we make sure the row is not sized smaller than its minimum height.
        if (_autoSizeRowsMode != DataGridViewAutoSizeRowsMode.None && dataGridViewRow.ThicknessInternal != dataGridViewRow.CachedThickness)
        {
            dataGridViewRow.ThicknessInternal = Math.Max(dataGridViewRow.MinimumHeight, dataGridViewRow.CachedThickness);
        }

        // Auto size columns also if needed
        DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows;
        if (rowDisplayed)
        {
            autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows;
        }

        bool columnAutoSized = AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, fixedHeight: true);
        bool fixedColumnHeadersHeight = ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize;
        bool fixedRowHeadersWidth = _rowHeadersWidthSizeMode is DataGridViewRowHeadersWidthSizeMode.EnableResizing
            or DataGridViewRowHeadersWidthSizeMode.DisableResizing;

        if (fixedRowHeadersWidth && !columnAutoSized)
        {
            // No need to autosize the column headers when the row headers and columns don't change.
            fixedColumnHeadersHeight = true;
        }

        // Auto size column headers
        if (!fixedColumnHeadersHeight)
        {
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, fixedColumnsWidth: true);
        }

        // Auto size row headers
        if (!fixedRowHeadersWidth)
        {
            AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight: true);
        }

        if (!fixedColumnHeadersHeight && !fixedRowHeadersWidth)
        {
            // Second round of column headers autosizing
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
        }
    }

    internal void OnRemovingColumn(DataGridViewColumn dataGridViewColumn, out Point newCurrentCell, bool force)
    {
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        Debug.Assert(dataGridViewColumn.Index >= 0 && dataGridViewColumn.Index < Columns.Count);

        _dataGridViewState1[State1_TemporarilyResetCurrentCell] = false;
        int columnIndex = dataGridViewColumn.Index;

        // Reset the current cell's address if there is one.
        if (_ptCurrentCell.X != -1)
        {
            int newX = _ptCurrentCell.X;
            if (columnIndex == _ptCurrentCell.X)
            {
                DataGridViewColumn? dataGridViewColumnNext = Columns.GetNextColumn(
                    Columns[columnIndex],
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.None);

                if (dataGridViewColumnNext is not null)
                {
                    newX = dataGridViewColumnNext.Index > columnIndex
                        ? dataGridViewColumnNext.Index - 1
                        : dataGridViewColumnNext.Index;
                }
                else
                {
                    DataGridViewColumn? dataGridViewColumnPrevious = Columns.GetPreviousColumn(
                        Columns[columnIndex],
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);

                    newX = dataGridViewColumnPrevious is not null
                        ? dataGridViewColumnPrevious.Index > columnIndex
                            ? dataGridViewColumnPrevious.Index - 1
                            : dataGridViewColumnPrevious.Index
                        : -1;
                }
            }
            else if (columnIndex < _ptCurrentCell.X)
            {
                newX = _ptCurrentCell.X - 1;
            }

            newCurrentCell = new Point(newX, (newX == -1) ? -1 : _ptCurrentCell.Y);
            if (columnIndex == _ptCurrentCell.X)
            {
                // Left cell is not validated since cancelling validation wouldn't have any effect anyways.
                bool success = SetCurrentCellAddressCore(
                    columnIndex: -1,
                    rowIndex: -1,
                    setAnchorCellAddress: true,
                    validateCurrentCell: false,
                    throughMouseClick: false);
                Debug.Assert(success);
            }
            else if (force)
            {
                // Underlying data of deleted column is gone. It cannot be accessed anymore.
                // Do not end editing mode so that CellValidation doesn't get raised, since that event needs the current formatted value.
                _dataGridViewState1[State1_TemporarilyResetCurrentCell] = true;
                bool success = SetCurrentCellAddressCore(
                    columnIndex: -1,
                    rowIndex: -1,
                    setAnchorCellAddress: true,
                    validateCurrentCell: false,
                    throughMouseClick: false);
                Debug.Assert(success);
            }
            else
            {
                // Quit editing mode and set the current cell to its new location once everything is in sync again.
                ResetCurrentCell();
            }
        }
        else
        {
            newCurrentCell = new Point(-1, -1);
        }

        // If the last column is removed, delete all the rows first.
        if (Columns.Count == 1)
        {
            Debug.Assert(columnIndex == 0);
            Rows.ClearInternal(recreateNewRow: false);
        }

        // Prepare the existing rows by removing cells at correct index
        int newColumnCount = Columns.Count - 1;

        for (int rowIndex = 0; rowIndex < Rows.Count; rowIndex++)
        {
            DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
            if (dataGridViewRow.Cells.Count > newColumnCount)
            {
                KeyboardToolTipStateMachine.Instance.Unhook(dataGridViewRow.Cells[columnIndex], KeyboardToolTip);
                dataGridViewRow.Cells.RemoveAtInternal(columnIndex);
            }
        }

        // Detach column header cell
        if (dataGridViewColumn.HasHeaderCell)
        {
            dataGridViewColumn.HeaderCell.DataGridView = null;
        }

        // Reset sort related variables.
        if (dataGridViewColumn == SortedColumn)
        {
            SortedColumn = null;
            SortOrder = SortOrder.None;

            if (dataGridViewColumn.IsDataBound)
            {
                // If the column being removed is the sorted column and it is also the dataBound column
                // then see if there is another dataBound column which has the same property name as the sorted column.
                // If so, then make that dataGridViewColumn the sorted column in the data grid view.
                for (int i = 0; i < Columns.Count; i++)
                {
                    if (dataGridViewColumn != Columns[i]
                        && Columns[i].SortMode != DataGridViewColumnSortMode.NotSortable
                        && string.Compare(dataGridViewColumn.DataPropertyName,
                            Columns[i].DataPropertyName,
                            ignoreCase: true,
                            CultureInfo.InvariantCulture) == 0)
                    {
                        Debug.Assert(Columns[i].IsDataBound, "two columns w/ the same DataPropertyName should be DataBound at the same time");
                        Debug.Assert(Columns[i].HeaderCell.SortGlyphDirection == dataGridViewColumn.HeaderCell.SortGlyphDirection, "DataBound columns should have the same SortGlyphDirection as the one on the DataGridView");
                        SortedColumn = Columns[i];
                        SortOrder = Columns[i].HeaderCell.SortGlyphDirection;
                        break;
                    }
                }
            }
        }

        // Is deleted column scrolled off screen?
        if (dataGridViewColumn.Visible && !dataGridViewColumn.Frozen && DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0)
        {
            // Deleted column is part of scrolling columns.
            if (DisplayedBandsInfo.FirstDisplayedScrollingCol == dataGridViewColumn.Index)
            {
                // Deleted column is first scrolling column
                _horizontalOffset -= FirstDisplayedScrollingColumnHiddenWidth;
                FirstDisplayedScrollingColumnHiddenWidth = 0;
            }
            else if (Columns.DisplayInOrder(DisplayedBandsInfo.FirstDisplayedScrollingCol, dataGridViewColumn.Index))
            {
                // Deleted column is displayed after first scrolling column
                if (_horizScrollBar.Enabled)
                {
                    int newHorizontalOffset = _horizScrollBar.Maximum - _horizScrollBar.LargeChange - dataGridViewColumn.Thickness;
                    if (newHorizontalOffset >= 0 && newHorizontalOffset < _horizScrollBar.Value)
                    {
                        _horizontalOffset = newHorizontalOffset;
                        FirstDisplayedScrollingColumnHiddenWidth = GetNegOffsetFromHorizontalOffset(_horizontalOffset);
                    }
                }
                else
                {
                    _horizontalOffset = FirstDisplayedScrollingColumnHiddenWidth = 0;
                }
            }
            else
            {
                // Deleted column is displayed before first scrolling column
                Debug.Assert(_horizontalOffset >= dataGridViewColumn.Thickness);
                _horizontalOffset -= dataGridViewColumn.Thickness;
            }

            if (_horizScrollBar.Enabled)
            {
                _horizScrollBar.Value = _horizontalOffset;
            }
        }

        bool raiseSelectionChanged = false;

        // Update the indexes of selected columns or individual cells to compensate for the removal of this column
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.FullColumnSelect:
            case DataGridViewSelectionMode.ColumnHeaderSelect:
                int columnEntries = _selectedBandIndexes.Count;
                int columnEntry = 0;
                while (columnEntry < columnEntries)
                {
                    int columnIndexSelected = _selectedBandIndexes[columnEntry];
                    if (columnIndex == columnIndexSelected)
                    {
                        _selectedBandIndexes.RemoveAt(columnEntry);
                        columnEntries--;
                        raiseSelectionChanged = true;
                    }
                    else
                    {
                        if (columnIndex < columnIndexSelected)
                        {
                            _selectedBandIndexes[columnEntry] = columnIndexSelected - 1;
                        }

                        columnEntry++;
                    }
                }

                break;
        }

        _dataGridViewState2[State2_RaiseSelectionChanged] |= _individualSelectedCells.RemoveAllCellsAtBand(column: true, columnIndex) > 0
            || raiseSelectionChanged;
        _individualReadOnlyCells.RemoveAllCellsAtBand(column: true, columnIndex);
    }

    internal void OnRemovingRow(int rowIndexDeleted, out Point newCurrentCell, bool force)
    {
        // if force is true, the row needs to be deleted no matter what. The underlying data row was already deleted.

        Debug.Assert(rowIndexDeleted >= 0 && rowIndexDeleted < Rows.Count);

        if (rowIndexDeleted >= 0 && rowIndexDeleted < Rows.Count)
        {
            foreach (DataGridViewCell cell in Rows[rowIndexDeleted].Cells)
            {
                KeyboardToolTipStateMachine.Instance.Unhook(cell, KeyboardToolTip);
            }
        }

        _dataGridViewState1[State1_TemporarilyResetCurrentCell] = false;
        newCurrentCell = new Point(-1, -1);

        // Reset the current cell's address if it's on the deleted row, or after it.
        if (_ptCurrentCell.Y != -1 && rowIndexDeleted <= _ptCurrentCell.Y)
        {
            int newY;
            if (rowIndexDeleted == _ptCurrentCell.Y)
            {
                int rowIndexPrevious = Rows.GetPreviousRow(rowIndexDeleted, DataGridViewElementStates.Visible);
                int rowIndexNext = Rows.GetNextRow(rowIndexDeleted, DataGridViewElementStates.Visible);
                if (rowIndexPrevious > -1 && AllowUserToAddRowsInternal)
                {
                    Debug.Assert(NewRowIndex != -1);
                    Debug.Assert(NewRowIndex == Rows.Count - 1);
                    newY = rowIndexNext > -1 && rowIndexNext < Rows.Count - 1 ? rowIndexNext - 1 : rowIndexPrevious;
                }
                else
                {
                    newY = rowIndexNext > -1 ? rowIndexNext - 1 : rowIndexPrevious;
                }

                // Since the current row is deleted, the dirty states need to be reset
                IsCurrentCellDirtyInternal = false;
                IsCurrentRowDirtyInternal = false;
            }
            else
            {
                Debug.Assert(rowIndexDeleted < _ptCurrentCell.Y);
                newY = _ptCurrentCell.Y - 1;
            }

            newCurrentCell = new Point(_ptCurrentCell.X, newY);
            if (rowIndexDeleted == _ptCurrentCell.Y)
            {
                // Left cell is not validated since cancelling validation wouldn't have any effect anyways.
                bool success = SetCurrentCellAddressCore(
                    columnIndex: -1,
                    rowIndex: -1,
                    setAnchorCellAddress: true,
                    validateCurrentCell: false,
                    throughMouseClick: false);
                Debug.Assert(success);
            }
            else if (force)
            {
                // Underlying data of deleted row is gone. It cannot be accessed anymore.
                // Do not end editing mode so that CellValidation doesn't get raised, since that event needs the current formatted value.
                _dataGridViewState1[State1_TemporarilyResetCurrentCell] = true;
                bool success = SetCurrentCellAddressCore(
                    columnIndex: -1,
                    rowIndex: -1,
                    setAnchorCellAddress: true,
                    validateCurrentCell: false,
                    throughMouseClick: false);
                Debug.Assert(success);
            }
            else
            {
                // Quit editing mode and set the current cell to its new location once everything is in sync again.
                ResetCurrentCell();
            }
        }

        bool raiseSelectionChanged = false;

        // Update the indexes of selected rows to compensate for the removal of this row
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.FullRowSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
                int rowEntries = _selectedBandIndexes.Count;
                int rowEntry = 0;
                while (rowEntry < rowEntries)
                {
                    int rowIndex = _selectedBandIndexes[rowEntry];
                    if (rowIndexDeleted == rowIndex)
                    {
                        raiseSelectionChanged = true;
                        _selectedBandIndexes.RemoveAt(rowEntry);
                        rowEntries--;
                    }
                    else
                    {
                        if (rowIndexDeleted < rowIndex)
                        {
                            _selectedBandIndexes[rowEntry] = rowIndex - 1;
                        }

                        rowEntry++;
                    }
                }

                if (_selectedBandSnapshotIndexes is not null)
                {
                    rowEntries = _selectedBandSnapshotIndexes.Count;
                    rowEntry = 0;
                    while (rowEntry < rowEntries)
                    {
                        int rowIndex = _selectedBandSnapshotIndexes[rowEntry];
                        if (rowIndexDeleted == rowIndex)
                        {
                            _selectedBandSnapshotIndexes.RemoveAt(rowEntry);
                            rowEntries--;
                        }
                        else
                        {
                            if (rowIndexDeleted < rowIndex)
                            {
                                _selectedBandSnapshotIndexes[rowEntry] = rowIndex - 1;
                            }

                            rowEntry++;
                        }
                    }
                }

                break;
        }

        _dataGridViewState2[State2_RaiseSelectionChanged] |= _individualSelectedCells.RemoveAllCellsAtBand(column: false, rowIndexDeleted) > 0
            || raiseSelectionChanged;
        _individualReadOnlyCells.RemoveAllCellsAtBand(column: false, rowIndexDeleted);
    }

    internal void OnReplacedCell(DataGridViewRow dataGridViewRow, int columnIndex)
    {
        DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex];
        if (_dataGridViewState2[State2_ReplacedCellSelected])
        {
            _individualSelectedCells.Add(dataGridViewCell);
        }

        if (_dataGridViewState2[State2_ReplacedCellReadOnly])
        {
            _individualReadOnlyCells.Add(dataGridViewCell);
        }

        // AutoSize column and row if needed
        OnCellCommonChange(columnIndex, dataGridViewRow.Index);

        if (_ptCurrentCellCache.X != -1)
        {
            if (!IsInnerCellOutOfBounds(_ptCurrentCellCache.X, _ptCurrentCellCache.Y))
            {
                SetCurrentCellAddressCore(_ptCurrentCellCache.X, _ptCurrentCellCache.Y, false, false, false);
            }

            _ptCurrentCellCache.X = -1;
            _ptCurrentCellCache.Y = -1;
        }
    }

    internal void OnReplacingCell(DataGridViewRow dataGridViewRow, int columnIndex)
    {
        if (_ptCurrentCell.X == dataGridViewRow.Index && _ptCurrentCell.Y == columnIndex)
        {
            // Trying to replace the current cell. Exiting editing mode first (if needed).
            // Remember to reset the current cell in OnReplacedCell notification
            _ptCurrentCellCache.X = _ptCurrentCell.X;
            _ptCurrentCellCache.Y = _ptCurrentCell.Y;

            // This may fail and throw an exception
            ResetCurrentCell();
        }
        else
        {
            _ptCurrentCellCache.X = -1;
            _ptCurrentCellCache.Y = -1;
        }

        DataGridViewCell dataGridViewCell = dataGridViewRow.Cells[columnIndex];
        _dataGridViewState2[State2_ReplacedCellSelected] = _individualSelectedCells.Contains(dataGridViewCell);
        if (_dataGridViewState2[State2_ReplacedCellSelected])
        {
            _individualSelectedCells.Remove(dataGridViewCell);
        }

        _dataGridViewState2[State2_ReplacedCellReadOnly] = _individualReadOnlyCells.Contains(dataGridViewCell);
        if (_dataGridViewState2[State2_ReplacedCellReadOnly])
        {
            _individualReadOnlyCells.Remove(dataGridViewCell);
        }
    }

    protected override void OnResize(EventArgs e)
    {
        int borderWidth = BorderWidth;
        Rectangle right;
        Rectangle bottom;
        Rectangle oldClientRectangle = _layout.ClientRectangle;
        Rectangle oldGridFocusRectangle = GetGridFocusRectangle();

        right = new Rectangle(oldClientRectangle.X + oldClientRectangle.Width - borderWidth,
            oldClientRectangle.Y,
            borderWidth,
            oldClientRectangle.Height);
        bottom = new Rectangle(oldClientRectangle.X,
            oldClientRectangle.Y + oldClientRectangle.Height - borderWidth,
            oldClientRectangle.Width,
            borderWidth);

        if (!IsMinimized)
        {
            // When owning form is minimized, act as if it had a normal size
            _normalClientRectangle = ClientRectangle;
        }

        Rectangle newClientRectangle = _normalClientRectangle;
        Rectangle newGridFocusRectangle = DisplayRectangle;
        newGridFocusRectangle.Inflate(1 - borderWidth - FocusRectOffset, 1 - borderWidth - FocusRectOffset);

        if (newClientRectangle.Width != oldClientRectangle.Width)
        {
            Invalidate(right);
            right = new Rectangle(newClientRectangle.X + newClientRectangle.Width - borderWidth,
                newClientRectangle.Y,
                borderWidth,
                newClientRectangle.Height);
            Invalidate(right);
        }

        if (newClientRectangle.Height != oldClientRectangle.Height)
        {
            Invalidate(bottom);
            bottom = new Rectangle(newClientRectangle.X,
                newClientRectangle.Y + newClientRectangle.Height - borderWidth,
                newClientRectangle.Width,
                borderWidth);
            Invalidate(bottom);
        }

        // Invalidate grid focus rectangle
        if (Focused && IsGridFocusRectangleEnabled() && oldGridFocusRectangle != newGridFocusRectangle)
        {
            right = new Rectangle(oldGridFocusRectangle.X + oldGridFocusRectangle.Width - 1,
                oldGridFocusRectangle.Y,
                1,
                oldGridFocusRectangle.Height);
            Invalidate(right);

            bottom = new Rectangle(oldGridFocusRectangle.X,
                oldGridFocusRectangle.Y + oldGridFocusRectangle.Height - 1,
                oldGridFocusRectangle.Width,
                1);
            Invalidate(bottom);

            InvalidateRectangleEdges(newGridFocusRectangle);
        }

        // also, invalidate the ResizeBoxRect
        if (!_layout.ResizeBoxRect.IsEmpty)
        {
            Invalidate(_layout.ResizeBoxRect);
        }

        _layout.ClientRectangle = newClientRectangle;

        int oldfirstDisplayedScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
        base.OnResize(e);
        if (oldfirstDisplayedScrollingRow != DisplayedBandsInfo.FirstDisplayedScrollingRow)
        {
            Invalidate();
        }
    }

    protected override void OnRightToLeftChanged(EventArgs e)
    {
        _dataGridViewState2[State2_RightToLeftValid] = false;
        base.OnRightToLeftChanged(e);
        CorrectFocus(onlyIfGridHasFocus: true);
        PerformLayoutPrivate(
            useRowShortcut: false,
            computeVisibleRows: false,
            invalidInAdjustFillingColumns: true,
            repositionEditingControl: false);
    }

    internal void OnRowCollectionChanged_PostNotification(
        bool recreateNewRow,
        bool allowSettingCurrentCell,
        CollectionChangeAction cca,
        DataGridViewRow dataGridViewRow,
        int rowIndex)
    {
        if (recreateNewRow
            && cca == CollectionChangeAction.Refresh
            && Columns.Count != 0
            && Rows.Count == 0
            && AllowUserToAddRowsInternal)
        {
            AddNewRow(createdByEditing: false);
        }

        if (cca == CollectionChangeAction.Refresh)
        {
            FlushSelectionChanged();
        }

        if ((cca == CollectionChangeAction.Refresh || cca == CollectionChangeAction.Add)
            && _ptCurrentCell.X == -1
            && allowSettingCurrentCell && !InSortOperation)
        {
            MakeFirstDisplayedCellCurrentCell(includeNewRow: false);
        }

        if (AutoSize)
        {
            bool invalidatePreferredSizeCache = true;
            switch (cca)
            {
                case CollectionChangeAction.Add:
                    Debug.Assert(rowIndex >= 0);
                    DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
                    invalidatePreferredSizeCache = ((rowState & DataGridViewElementStates.Visible) != 0);
                    break;
                case CollectionChangeAction.Remove:
                    invalidatePreferredSizeCache = dataGridViewRow.DataGridView is null && dataGridViewRow.Visible;
                    break;
                    // case CollectionChangeAction.Refresh: invalidatePreferredSizeCache stays true
            }

            if (invalidatePreferredSizeCache)
            {
                LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Rows);
            }
        }
    }

    protected virtual void OnRowContextMenuStripChanged(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        GetEvent<DataGridViewRowEventHandler>(s_rowContextMenuStripChangedEvent)?.Invoke(this, e);
    }

    internal ContextMenuStrip? OnRowContextMenuStripNeeded(int rowIndex, ContextMenuStrip? contextMenuStrip)
    {
        DataGridViewRowContextMenuStripNeededEventArgs dgvrcmsne = new(rowIndex, contextMenuStrip);
        OnRowContextMenuStripNeeded(dgvrcmsne);
        return dgvrcmsne.ContextMenuStrip;
    }

    protected virtual void OnRowContextMenuStripNeeded(DataGridViewRowContextMenuStripNeededEventArgs e) =>
        GetEvent<DataGridViewRowContextMenuStripNeededEventHandler>(s_rowContextMenuStripNeededEvent)?.Invoke(this, e);

    protected virtual void OnRowDefaultCellStyleChanged(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        OnRowGlobalAutoSize(e.Row.Index);
        GetEvent<DataGridViewRowEventHandler>(s_rowDefaultCellStyleChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnRowDirtyStateNeeded(QuestionEventArgs e) =>
        GetEvent<QuestionEventHandler>(s_rowDirtyStateNeededEvent)?.Invoke(this, e);

    protected virtual void OnRowDividerDoubleClick(DataGridViewRowDividerDoubleClickEventArgs e)
    {
        GetEvent<DataGridViewRowDividerDoubleClickEventHandler>(s_rowDividerDoubleClickEvent)?.Invoke(this, e);

        if (!e.Handled && e.Button == MouseButtons.Left && e.RowIndex < Rows.Count)
        {
            if (e.RowIndex == -1)
            {
                AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
            }
            else
            {
                if (_autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None)
                {
                    AutoResizeRowInternal(e.RowIndex, DataGridViewAutoSizeRowMode.AllCells, fixedWidth: true, internalAutosizing: true);
                }
                else
                {
                    AutoResizeRowInternal(e.RowIndex, MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode), fixedWidth: true, internalAutosizing: true);
                }
            }
        }
    }

    protected virtual void OnRowDividerHeightChanged(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        OnRowGlobalAutoSize(e.Row.Index);
        GetEvent<DataGridViewRowEventHandler>(s_rowDividerHeightChangedEvent)?.Invoke(this, e);
    }

    private void OnRowEnter(
        ref DataGridViewCell? dataGridViewCell,
        int columnIndex,
        int rowIndex,
        bool canCreateNewRow,
        bool validationFailureOccurred)
    {
        Debug.Assert(columnIndex >= 0 && rowIndex >= 0);

        if (!validationFailureOccurred)
        {
            _dataGridViewState1[State1_NewRowEdited] = false;
        }

        if (rowIndex < Rows.Count && columnIndex < Columns.Count)
        {
            bool calledAddNewOnTheDataConnection = false;
            if (!validationFailureOccurred && AllowUserToAddRowsInternal && NewRowIndex == rowIndex)
            {
                _dataGridViewState1[State1_NewRowEdited] = true;

                if (canCreateNewRow)
                {
                    DataGridViewRowEventArgs dgvre = new(Rows[NewRowIndex]);
                    if (VirtualMode || DataSource is not null)
                    {
                        if (DataConnection is not null && DataConnection.InterestedInRowEvents)
                        {
                            DataConnection.OnNewRowNeeded();
                            calledAddNewOnTheDataConnection = true;
                        }

                        if (VirtualMode)
                        {
                            OnNewRowNeeded(dgvre);
                        }
                    }

                    // AllowUserToAddRowsInternal can become FALSE while adding a row.
                    // NOTE: we don't need to invalidate if AllowUserToAddRowsInternal changed to FALSE.
                    //
                    if (AllowUserToAddRowsInternal)
                    {
                        OnDefaultValuesNeeded(dgvre);
                        InvalidateRowPrivate(NewRowIndex);
                    }
#if DEBUG
                    else
                    {
                        Debug.Assert(NewRowIndex == -1, "newRowIndex and AllowUserToAddRowsInternal became out of sync");
                    }
#endif //
                }
            }

            if (calledAddNewOnTheDataConnection && rowIndex > Rows.Count - 1)
            {
                // Calling AddNew on the DataConnection can result in the entire list being wiped out.
                rowIndex = Math.Min(rowIndex, Rows.Count - 1);
            }

            DataGridViewCellEventArgs dgvce = new(columnIndex, rowIndex);
            OnRowEnter(dgvce);
            if (DataConnection is not null
                && DataConnection.InterestedInRowEvents
                && !DataConnection.PositionChangingOutsideDataGridView
                && !DataConnection.ListWasReset
                && (!calledAddNewOnTheDataConnection || (DataConnection.List?.Count ?? 0) > 0))
            {
                DataConnection.OnRowEnter(dgvce);
            }

            if (dataGridViewCell is not null)
            {
                if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
                {
                    dataGridViewCell = null;
                }
                else
                {
                    Debug.Assert(rowIndex < Rows.Count && columnIndex < Columns.Count);
                    dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
                }
            }
        }
    }

    protected virtual void OnRowEnter(DataGridViewCellEventArgs e)
    {
        try
        {
            _noDimensionChangeCount++;
            if (GetEvent<DataGridViewCellEventHandler>(s_rowEnterEvent) is { } eh)
            {
                eh(this, e);
                CorrectFocus(onlyIfGridHasFocus: true);
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
        }
    }

    internal void OnRowErrorTextChanged(DataGridViewRow dataGridViewRow)
    {
        DataGridViewRowEventArgs dgvre = new(dataGridViewRow);
        OnRowErrorTextChanged(dgvre);
    }

    protected virtual void OnRowErrorTextChanged(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        UpdateRowErrorText(e.Row.Index);
        GetEvent<DataGridViewRowEventHandler>(s_rowErrorTextChangedEvent)?.Invoke(this, e);
    }

    internal string OnRowErrorTextNeeded(int rowIndex, string errorText)
    {
        Debug.Assert(rowIndex >= 0);
        DataGridViewRowErrorTextNeededEventArgs dgvretne = new(rowIndex, errorText);
        OnRowErrorTextNeeded(dgvretne);
        return dgvretne.ErrorText;
    }

    protected virtual void OnRowErrorTextNeeded(DataGridViewRowErrorTextNeededEventArgs e) =>
        GetEvent<DataGridViewRowErrorTextNeededEventHandler>(s_rowErrorTextNeededEvent)?.Invoke(this, e);

    private void OnRowGlobalAutoSize(int rowIndex)
    {
        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
        if ((rowState & DataGridViewElementStates.Visible) == 0)
        {
            return;
        }

        InvalidateRowPrivate(rowIndex);

        if (_noAutoSizeCount > 0)
        {
            return;
        }

        DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode;
        bool autoSizeRow = false;
        bool rowDisplayed = (rowState & DataGridViewElementStates.Displayed) != 0;
        if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None
            && !((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed))
        {
            AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode), fixedWidth: false, internalAutosizing: true);
            autoSizeRow = true;
        }

        // Auto size columns also if needed
        DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows;
        if (rowDisplayed)
        {
            autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows;
        }

        AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, fixedHeight: true);

        bool fixedRowHeadersWidth = _rowHeadersWidthSizeMode is DataGridViewRowHeadersWidthSizeMode.EnableResizing
            or DataGridViewRowHeadersWidthSizeMode.DisableResizing;

        // Auto size column headers
        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
        {
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, fixedColumnsWidth: true);
        }

        // Auto size row headers
        if (!fixedRowHeadersWidth)
        {
            AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight: true);
        }

        if (autoSizeRow)
        {
            // Second round of row autosizing
            AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode), fixedWidth: true, internalAutosizing: true);
        }

        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize && !fixedRowHeadersWidth)
        {
            // Second round of column headers autosizing
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
        }
    }

    protected virtual void OnRowHeaderCellChanged(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        OnRowHeaderGlobalAutoSize(e.Row.Index);
        GetEvent<DataGridViewRowEventHandler>(s_rowHeaderCellChangedEvent)?.Invoke(this, e);
    }

    private void OnRowHeaderGlobalAutoSize(int rowIndex)
    {
        if (!RowHeadersVisible)
        {
            return;
        }

        InvalidateCellPrivate(-1, rowIndex);

        if (_noAutoSizeCount > 0)
        {
            return;
        }

        bool rowDisplayed = false;
        if (rowIndex != -1)
        {
            rowDisplayed = (Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0;
        }

        bool fixedColumnHeadersHeight = rowIndex != -1 || ColumnHeadersHeightSizeMode != DataGridViewColumnHeadersHeightSizeMode.AutoSize;
        bool fixedRowHeight = rowIndex == -1
            || ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) == 0)
            || ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0
                && rowIndex != -1
                && !rowDisplayed);

        bool autoSizeRowHeaders = false;
        if (_rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders
            || (_rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders && rowIndex != -1 && rowDisplayed)
            || (_rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.EnableResizing
                && _rowHeadersWidthSizeMode != DataGridViewRowHeadersWidthSizeMode.DisableResizing
                && rowIndex == -1)
            || (_rowHeadersWidthSizeMode == DataGridViewRowHeadersWidthSizeMode.AutoSizeToFirstHeader
                && rowIndex != -1
                && rowIndex == Rows.GetFirstRow(DataGridViewElementStates.Visible)))
        {
            AutoResizeRowHeadersWidth(rowIndex, _rowHeadersWidthSizeMode, fixedColumnHeadersHeight, fixedRowHeight);
            autoSizeRowHeaders = true;
        }

        if (!fixedColumnHeadersHeight)
        {
            AutoResizeColumnHeadersHeight(columnIndex: -1, fixedRowHeadersWidth: true, fixedColumnWidth: true);
        }

        if (!fixedRowHeight)
        {
            AutoResizeRowInternal(
                rowIndex,
                MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode),
                fixedWidth: true,
                internalAutosizing: true);
        }

        if (autoSizeRowHeaders && (!fixedColumnHeadersHeight || !fixedRowHeight))
        {
            // Second round of row headers autosizing
            AutoResizeRowHeadersWidth(
                rowIndex,
                _rowHeadersWidthSizeMode,
                fixedColumnHeadersHeight: true,
                fixedRowHeight: true);
        }
    }

    protected virtual void OnRowHeaderMouseClick(DataGridViewCellMouseEventArgs e) =>
        GetEvent<DataGridViewCellMouseEventHandler>(s_rowHeaderMouseClickEvent)?.Invoke(this, e);

    protected virtual void OnRowHeaderMouseDoubleClick(DataGridViewCellMouseEventArgs e) =>
        GetEvent<DataGridViewCellMouseEventHandler>(s_rowHeaderMouseDoubleClickEvent)?.Invoke(this, e);

    private void OnRowHeaderMouseDown(HitTestInfo hti, bool isShiftDown, bool isControlDown)
    {
        Debug.Assert(hti.Type == DataGridViewHitTestType.RowHeader);
        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.FullColumnSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    break;

                case DataGridViewSelectionMode.FullRowSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    {
                        bool select = true;
                        if (isControlDown && ((Rows.GetRowState(hti._row) & DataGridViewElementStates.Selected) != 0))
                        {
                            select = false;
                        }

                        if (select)
                        {
                            DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
                            if (dataGridViewColumn is not null && hti._row != _ptCurrentCell.Y)
                            {
                                int oldCurrentCellX = _ptCurrentCell.X;
                                int oldCurrentCellY = _ptCurrentCell.Y;
                                // Make sure we will be able to scroll into view
                                if (!EndEdit(
                                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange,
                                    validateCell: DataGridViewValidateCellInternal.Always,
                                    fireCellLeave: true,
                                    fireCellEnter: true,
                                    fireRowLeave: hti._row != _ptCurrentCell.Y,
                                    fireRowEnter: hti._row != _ptCurrentCell.Y,
                                    fireLeave: false,
                                    keepFocus: EditMode != DataGridViewEditMode.EditOnEnter,
                                    resetCurrentCell: true,
                                    resetAnchorCell: false))
                                {
                                    // Just cancel operation silently instead of throwing InvalidOperationException
                                    return;
                                }

                                if (oldCurrentCellY != -1)
                                {
                                    DataGridViewCell? dataGridViewCellTmp = null;
                                    if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                    {
                                        return;
                                    }

                                    if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY))
                                    {
                                        // Row validation was cancelled
                                        if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                        {
                                            return;
                                        }

                                        OnRowEnter(
                                            ref dataGridViewCellTmp,
                                            oldCurrentCellX,
                                            oldCurrentCellY,
                                            canCreateNewRow: true,
                                            validationFailureOccurred: true);
                                        if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                        {
                                            return;
                                        }

                                        OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY);
                                        return;
                                    }

                                    if (IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                    {
                                        return;
                                    }

                                    OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY);

                                    // Row validation was not cancelled, but operation needs to be re-evaluated.
                                    if (hti._row >= Rows.Count)
                                    {
                                        int lastVisibleRowIndex = Rows.GetLastRow(DataGridViewElementStates.Visible);
                                        if (_ptCurrentCell.X == -1 && lastVisibleRowIndex != -1)
                                        {
                                            // CurrentCell was reset because commit deleted row(s).
                                            // Since the user wants to select a row, we don't want to
                                            // end up with no CurrentCell. We pick the last visible
                                            // row in the grid which may be the 'new row'.
                                            if (IsColumnOutOfBounds(oldCurrentCellX))
                                            {
                                                return;
                                            }

                                            bool success = SetAndSelectCurrentCellAddress(
                                                oldCurrentCellX,
                                                lastVisibleRowIndex,
                                                setAnchorCellAddress: true,
                                                validateCurrentCell: false,
                                                throughMouseClick: false,
                                                clearSelection: false,
                                                forceCurrentCellSelection: false);
                                            Debug.Assert(success);
                                        }

                                        return;
                                    }
                                    else if ((Rows.GetRowState(hti._row) & DataGridViewElementStates.Visible) == 0)
                                    {
                                        return;
                                    }
                                }
                            }

                            bool selectRowRange = false;
                            _trackRow = hti._row;
                            _trackRowEdge = -1;
                            if (MultiSelect
                                && isShiftDown
                                && _ptAnchorCell.Y > -1
                                && (Rows.GetRowState(_ptAnchorCell.Y) & DataGridViewElementStates.Selected) != 0)
                            {
                                selectRowRange = true;
                            }

                            if (!MultiSelect || !isControlDown || isShiftDown)
                            {
                                Debug.Assert(MultiSelect || _selectedBandIndexes.Count <= 1);
                                int bandIndex = 0;
                                bool switchedToBulkPaint = false;
                                if (_selectedBandIndexes.Count > BulkPaintThreshold)
                                {
                                    _inBulkPaintCount++;
                                    switchedToBulkPaint = true;
                                }

                                try
                                {
                                    while (bandIndex < _selectedBandIndexes.Count)
                                    {
                                        if (_selectedBandIndexes[bandIndex] != hti._row)
                                        {
                                            // deselect currently selected row
                                            SetSelectedRowCore(_selectedBandIndexes[bandIndex], false);
                                        }
                                        else
                                        {
                                            bandIndex++;
                                        }
                                    }

                                    if (SelectionMode == DataGridViewSelectionMode.RowHeaderSelect)
                                    {
                                        RemoveIndividuallySelectedCells();
                                    }
                                    else
                                    {
                                        Debug.Assert(_individualSelectedCells.Count == 0);
                                    }
                                }
                                finally
                                {
                                    if (switchedToBulkPaint)
                                    {
                                        ExitBulkPaint(-1, -1);
                                    }
                                }
                            }

                            if (MultiSelect && _dataGridViewOper[OperationTrackMouseMoves])
                            {
                                _dataGridViewOper[OperationTrackRowSelect] = true;
                            }

                            if (selectRowRange)
                            {
                                if (hti._row >= _ptAnchorCell.Y)
                                {
                                    SelectRowRange(_ptAnchorCell.Y, hti._row, true);
                                }
                                else
                                {
                                    SelectRowRange(hti._row, _ptAnchorCell.Y, true);
                                }
                            }
                            else if ((Rows.GetRowState(hti._row) & DataGridViewElementStates.Selected) == 0)
                            {
                                Debug.Assert(_selectedBandIndexes.Contains(hti._row) ==
                                             ((Rows.GetRowState(hti._row) & DataGridViewElementStates.Selected) != 0));
                                SetSelectedRowCore(hti._row, true);
                            }

                            if (dataGridViewColumn is not null)
                            {
                                if (hti._row != _ptCurrentCell.Y)
                                {
                                    if (IsInnerCellOutOfBounds(dataGridViewColumn.Index, hti._row))
                                    {
                                        return;
                                    }

                                    // set current cell to the left most visible cell in the row
                                    bool success = ScrollIntoView(dataGridViewColumn.Index, hti._row, false);
                                    Debug.Assert(success);
                                    if (IsInnerCellOutOfBounds(dataGridViewColumn.Index, hti._row))
                                    {
                                        return;
                                    }

                                    success = SetCurrentCellAddressCore(dataGridViewColumn.Index, hti._row, !selectRowRange, false, true);
                                    Debug.Assert(success);
                                }
                                else if (_ptCurrentCell.Y != -1)
                                {
                                    // Potentially have to give focus back to the current edited cell.
                                    bool success = SetCurrentCellAddressCore(
                                        _ptCurrentCell.X,
                                        _ptCurrentCell.Y,
                                        setAnchorCellAddress: false,
                                        validateCurrentCell: false,
                                        throughMouseClick: false);
                                    Debug.Assert(success);
                                }
                            }
                            else
                            {
                                Debug.Assert(CurrentCellAddress == new Point(-1, -1));
                            }
                        }
                        else
                        {
                            Debug.Assert(_selectedBandIndexes.Contains(hti._row));
                            SetSelectedRowCore(hti._row, false);
                        }

                        break;
                    }
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }
    }

    protected virtual void OnRowHeadersBorderStyleChanged(EventArgs e)
    {
        PerformLayoutPrivate(useRowShortcut: false, computeVisibleRows: false, invalidInAdjustFillingColumns: true, repositionEditingControl: false);
        Invalidate();
        GetEvent<EventHandler>(s_rowHeadersBorderStyleChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnRowHeadersDefaultCellStyleChanged(EventArgs e)
    {
        if (RowHeadersVisible)
        {
            Invalidate(Rectangle.Union(_layout.TopLeftHeader, _layout.RowHeaders));

            if (!(e is DataGridViewCellStyleChangedEventArgs dgvcsce) || dgvcsce.ChangeAffectsPreferredSize)
            {
                OnRowHeadersGlobalAutoSize(expandingRows: false);
                if (EditingControl is not null)
                {
                    PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
                }
            }
        }

        GetEvent<EventHandler>(s_rowHeadersDefaultCellStyleChangedEvent)?.Invoke(this, e);
    }

    private void OnRowHeadersGlobalAutoSize(bool expandingRows)
    {
        if (_noAutoSizeCount > 0)
        {
            return;
        }

        bool fixedRowsHeight = (((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.Header) == 0
            || !RowHeadersVisible;
        bool autoSizeRowHeaders = _rowHeadersWidthSizeMode is not DataGridViewRowHeadersWidthSizeMode.EnableResizing
            and not DataGridViewRowHeadersWidthSizeMode.DisableResizing;

        if (autoSizeRowHeaders)
        {
            AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight);
        }

        if (!fixedRowsHeight)
        {
            if (expandingRows)
            {
                AdjustExpandingRows(columnIndex: -1, fixedWidth: true);
            }
            else
            {
                AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
            }

            if (autoSizeRowHeaders)
            {
                // Second round of row headers autosizing
                AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight: true);
            }
        }
    }

    protected virtual void OnRowHeadersWidthChanged(EventArgs e)
    {
        if (RowHeadersVisible)
        {
            if (EditingControl is not null)
            {
                PositionEditingControl(true, false, false);
            }

            UpdateMouseEnteredCell(hti: null, e: null);

            OnRowHeadersGlobalAutoSize(expandingRows: false);
        }

        GetEvent<EventHandler>(s_rowHeadersWidthChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnRowHeadersWidthSizeModeChanged(DataGridViewAutoSizeModeEventArgs e)
    {
        if (_rowHeadersWidthSizeMode is not DataGridViewRowHeadersWidthSizeMode.EnableResizing
            and not DataGridViewRowHeadersWidthSizeMode.DisableResizing)
        {
            if (!e.PreviousModeAutoSized)
            {
                // Save current row headers width for later reuse
                _cachedRowHeadersWidth = RowHeadersWidth;
            }

            AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight: true);
        }
        else if (e.PreviousModeAutoSized)
        {
            RowHeadersWidth = _cachedRowHeadersWidth;
        }

        GetEvent<DataGridViewAutoSizeModeEventHandler>(s_rowHeadersWidthSizeModeChangedEvent)?.Invoke(this, e);
    }

    protected virtual void OnRowHeightChanged(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        UpdateRowHeightInfoPrivate(e.Row.Index, updateToEnd: false, invalidInAdjustFillingColumns: false);
        GetEvent<DataGridViewRowEventHandler>(s_rowHeightChangedEvent)?.Invoke(this, e);
        OnRowGlobalAutoSize(e.Row.Index);
    }

    internal DataGridViewRowHeightInfoNeededEventArgs OnRowHeightInfoNeeded(int rowIndex, int height, int minimumHeight)
    {
        DataGridViewRowHeightInfoNeededEventArgs dgvrhine = RowHeightInfoNeededEventArgs;
        dgvrhine.SetProperties(rowIndex, height, minimumHeight);
        OnRowHeightInfoNeeded(dgvrhine);
        return dgvrhine;
    }

    protected virtual void OnRowHeightInfoNeeded(DataGridViewRowHeightInfoNeededEventArgs e) =>
        GetEvent<DataGridViewRowHeightInfoNeededEventHandler>(s_rowHeightInfoNeededEvent)?.Invoke(this, e);

    private bool OnRowHeightInfoPushed(int rowIndex, int height, int minimumHeight)
    {
        Debug.Assert(rowIndex != -1);
        Debug.Assert(_autoSizeRowsMode == DataGridViewAutoSizeRowsMode.None);
        if (VirtualMode || DataSource is not null)
        {
            DataGridViewRowHeightInfoPushedEventArgs dgvrhipe = new(rowIndex, height, minimumHeight);
            OnRowHeightInfoPushed(dgvrhipe);
            if (dgvrhipe.Handled)
            {
                UpdateRowHeightInfoPrivate(rowIndex, updateToEnd: false, invalidInAdjustFillingColumns: true);
                return true;
            }
        }

        return false;
    }

    protected virtual void OnRowHeightInfoPushed(DataGridViewRowHeightInfoPushedEventArgs e) =>
        GetEvent<DataGridViewRowHeightInfoPushedEventHandler>(s_rowHeightInfoPushedEvent)?.Invoke(this, e);

    private void OnRowLeave(ref DataGridViewCell? dataGridViewCell, int columnIndex, int rowIndex)
    {
        Debug.Assert(columnIndex >= 0 && rowIndex >= 0);
        if (rowIndex < Rows.Count && columnIndex < Columns.Count)
        {
            DataGridViewCellEventArgs dgvce = new(columnIndex, rowIndex);
            OnRowLeave(dgvce);
            if (dataGridViewCell is not null)
            {
                if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
                {
                    dataGridViewCell = null;
                }
                else
                {
                    Debug.Assert(rowIndex < Rows.Count && columnIndex < Columns.Count);
                    dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
                }
            }
        }
    }

    protected virtual void OnRowLeave(DataGridViewCellEventArgs e)
    {
        try
        {
            _noDimensionChangeCount++;
            if (GetEvent<DataGridViewCellEventHandler>(s_rowLeaveEvent) is { } eh)
            {
                eh(this, e);
                CorrectFocus(onlyIfGridHasFocus: true);
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
        }
    }

    protected virtual void OnRowMinimumHeightChanged(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        GetEvent<DataGridViewRowEventHandler>(s_rowMinimumHeightChangeEvent)?.Invoke(this, e);
    }

    protected internal virtual void OnRowPostPaint(DataGridViewRowPostPaintEventArgs e) =>
        GetEvent<DataGridViewRowPostPaintEventHandler>(s_rowPostPaintEvent)?.Invoke(this, e);

    protected internal virtual void OnRowPrePaint(DataGridViewRowPrePaintEventArgs e) =>
        GetEvent<DataGridViewRowPrePaintEventHandler>(s_rowPrePaintEvent)?.Invoke(this, e);

    internal void OnRowsAddedInternal(int rowIndex, int rowCount) =>
        OnRowsAdded(new DataGridViewRowsAddedEventArgs(rowIndex, rowCount));

    protected virtual void OnRowsAdded(DataGridViewRowsAddedEventArgs e) =>
        GetEvent<DataGridViewRowsAddedEventHandler>(s_rowsAddedEvent)?.Invoke(this, e);

    protected virtual void OnRowsDefaultCellStyleChanged(EventArgs e)
    {
        if (e is DataGridViewCellStyleChangedEventArgs dgvcsce && !dgvcsce.ChangeAffectsPreferredSize)
        {
            InvalidateData();
        }
        else
        {
            OnRowsGlobalAutoSize();
            if (EditingControl is not null)
            {
                PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
            }
        }

        GetEvent<EventHandler>(s_rowsDefaultCellStyleChangedEvent)?.Invoke(this, e);
    }

    private void OnRowSelectMouseMove(HitTestInfo hti)
    {
        Debug.Assert(hti._row >= 0);
        Debug.Assert(MultiSelect);

        if (_ptCurrentCell.Y != -1
            && hti._row != _ptCurrentCell.Y
            && !CommitEditForOperation(_ptCurrentCell.X, hti._row, forCurrentCellChange: true))
        {
            // Return silently if validating/commit/abort failed
            return;
        }

        if (IsRowOutOfBounds(hti._row))
        {
            return;
        }

        _noSelectionChangeCount++;
        try
        {
            if (_trackRowEdge >= _trackRow && hti._row > _trackRowEdge && _trackRowEdge >= 0)
            {
                SelectRowRange(Rows.GetNextRow(_trackRowEdge, DataGridViewElementStates.Visible),
                    hti._row, true);
                _trackRowEdge = hti._row;
            }
            else if (_trackRowEdge > _trackRow && hti._row < _trackRowEdge && hti._row >= _trackRow && _trackRowEdge >= 0)
            {
                SelectRowRange(Rows.GetNextRow(hti._row, DataGridViewElementStates.Visible),
                    _trackRowEdge, false);
                _trackRowEdge = hti._row;
            }
            else if (hti._row > _trackRow && _trackRowEdge == -1)
            {
                SelectRowRange(Rows.GetNextRow(_trackRow, DataGridViewElementStates.Visible),
                    hti._row, true);
                _trackRowEdge = hti._row;
            }
            else if (_trackRowEdge <= _trackRow && hti._row < _trackRowEdge && _trackRowEdge >= 0)
            {
                SelectRowRange(hti._row,
                    Rows.GetPreviousRow(_trackRowEdge, DataGridViewElementStates.Visible),
                    true);
                _trackRowEdge = hti._row;
            }
            else if (_trackRowEdge < _trackRow && hti._row > _trackRowEdge && hti._row <= _trackRow && _trackRowEdge >= 0)
            {
                SelectRowRange(_trackRowEdge,
                    Rows.GetPreviousRow(hti._row, DataGridViewElementStates.Visible),
                    false);
                _trackRowEdge = hti._row;
            }
            else if (hti._row < _trackRow && _trackRowEdge == -1)
            {
                SelectRowRange(hti._row,
                    Rows.GetPreviousRow(_trackRow, DataGridViewElementStates.Visible),
                    true);
                _trackRowEdge = hti._row;
            }
            else if (_trackRowEdge > _trackRow && hti._row < _trackRow)
            {
                SelectRowRange(Rows.GetNextRow(_trackRow, DataGridViewElementStates.Visible),
                    _trackRowEdge, false);
                SelectRowRange(hti._row,
                    Rows.GetPreviousRow(_trackRow, DataGridViewElementStates.Visible),
                    true);
                _trackRowEdge = hti._row;
            }
            else if (hti._row > _trackRow && _trackRowEdge < _trackRow && _trackRowEdge >= 0)
            {
                SelectRowRange(_trackRowEdge,
                    Rows.GetPreviousRow(_trackRow, DataGridViewElementStates.Visible),
                    false);
                SelectRowRange(Rows.GetNextRow(_trackRow, DataGridViewElementStates.Visible),
                    hti._row, true);
                _trackRowEdge = hti._row;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        if (_ptCurrentCell.Y != -1 && hti._row != _ptCurrentCell.Y)
        {
            if (IsRowOutOfBounds(hti._row))
            {
                return;
            }

            bool success = SetCurrentCellAddressCore(_ptCurrentCell.X,
                hti._row,
                setAnchorCellAddress: false,
                validateCurrentCell: false,
                throughMouseClick: false);
            Debug.Assert(success);
        }
    }

    private void OnRowsGlobalAutoSize()
    {
        InvalidateData();

        if (_noAutoSizeCount > 0)
        {
            return;
        }

        // Autosize rows if needed
        if ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0)
        {
            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: false, internalAutosizing: true);
        }

        // Auto size columns also if needed
        AutoResizeAllVisibleColumnsInternal(
            DataGridViewAutoSizeColumnCriteriaInternal.AllRows | DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows,
            fixedHeight: true);

        bool fixedRowHeadersWidth = _rowHeadersWidthSizeMode is DataGridViewRowHeadersWidthSizeMode.EnableResizing
            or DataGridViewRowHeadersWidthSizeMode.DisableResizing;

        // Auto size column headers
        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
        {
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth, fixedColumnsWidth: true);
        }

        // Auto size row headers
        if (!fixedRowHeadersWidth)
        {
            AutoResizeRowHeadersWidth(_rowHeadersWidthSizeMode, fixedColumnHeadersHeight: true, fixedRowsHeight: true);
        }

        // Second round of rows autosizing
        if ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0)
        {
            AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
        }

        // Second round of column headers autosizing
        if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize && !fixedRowHeadersWidth)
        {
            AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, true);
        }
    }

    internal void OnRowsRemovedInternal(int rowIndex, int rowCount) =>
        OnRowsRemoved(new DataGridViewRowsRemovedEventArgs(rowIndex, rowCount));

    protected virtual void OnRowsRemoved(DataGridViewRowsRemovedEventArgs e) =>
        GetEvent<DataGridViewRowsRemovedEventHandler>(s_rowsRemovedEvent)?.Invoke(this, e);

    protected virtual void OnRowStateChanged(int rowIndex, DataGridViewRowStateChangedEventArgs e)
    {
        Debug.Assert(rowIndex >= -1);

        // If row.Frozen changed, we need to update the vertical scroll bar
        // A hidden row may become visible and vice-versa, we'd better repaint the whole control
        DataGridViewRow dataGridViewRow = e.Row;
        DataGridViewElementStates newState = DataGridViewElementStates.None;
        bool rowVisible = false;
        if (rowIndex >= 0)
        {
            newState = Rows.GetRowState(rowIndex);
            rowVisible = ((newState & DataGridViewElementStates.Visible) != 0);
        }

        switch (e.StateChanged)
        {
            // At this point we assume that only the Selected state has an influence on the rendering of the row.
            // If there is a customer scenario where another state has an influence, the dev will have to invalidate the row by hand.
            // case DataGridViewElementStates.ReadOnly:
            // case DataGridViewElementStates.Resizable:

            case DataGridViewElementStates.Selected:
                if (rowVisible && _inBulkPaintCount == 0)
                {
                    InvalidateRowPrivate(rowIndex);
                }

                break;

            case DataGridViewElementStates.Frozen:
                if (rowVisible)
                {
                    if ((newState & DataGridViewElementStates.Frozen) == 0)
                    {
                        // row was unfrozen - make it the first visible scrolling row if there is room
                        FirstVisibleScrollingRowTempted(rowIndex);
                    }

                    PerformLayoutPrivate(
                        useRowShortcut: false,
                        computeVisibleRows: false,
                        invalidInAdjustFillingColumns: true,
                        repositionEditingControl: true);
                    Invalidate();
                }

                break;

            case DataGridViewElementStates.Visible:
                {
                    if (!rowVisible && (newState & DataGridViewElementStates.Displayed) != 0)
                    {
                        // Displayed row becomes invisible. Turns off the Displayed state.
                        Rows.SetRowState(rowIndex, DataGridViewElementStates.Displayed, false);
                    }

                    PerformLayoutPrivate(
                        useRowShortcut: false,
                        computeVisibleRows: false,
                        invalidInAdjustFillingColumns: true,
                        repositionEditingControl: true);

                    Invalidate();

                    bool rowDisplayed = (Rows.GetRowState(rowIndex) & DataGridViewElementStates.Displayed) != 0;
                    DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode;

                    bool autoSizeRow = false;

                    if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None)
                    {
                        int height = dataGridViewRow.ThicknessInternal;
                        if (rowVisible)
                        {
                            // Cache row's height before potential autosizing occurs
                            // Valid operation even for shared rows
                            dataGridViewRow.CachedThickness = height;
                            if (!((autoSizeRowsModeInternal & DataGridViewAutoSizeRowsModeInternal.DisplayedRows) != 0 && !rowDisplayed))
                            {
                                AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode), fixedWidth: false, internalAutosizing: true);
                                autoSizeRow = true;
                            }
                        }
                        else if (height != dataGridViewRow.CachedThickness)
                        {
                            // Rows that are made invisible in the collection take their non-autosized height
                            // Not calling OnRowHeightInfoPushed(...) because rows are autosized
                            // Make sure the affected row is un-shared
                            if (dataGridViewRow.Index == -1)
                            {
                                dataGridViewRow = Rows[rowIndex];
                            }

                            dataGridViewRow.ThicknessInternal = Math.Max(dataGridViewRow.MinimumHeight, dataGridViewRow.CachedThickness);
                        }
                    }

                    // Auto size columns also if needed
                    DataGridViewAutoSizeColumnCriteriaInternal autoSizeColumnCriteriaFilter = DataGridViewAutoSizeColumnCriteriaInternal.AllRows;
                    if (rowDisplayed)
                    {
                        autoSizeColumnCriteriaFilter |= DataGridViewAutoSizeColumnCriteriaInternal.DisplayedRows;
                    }

                    if (rowVisible && Rows.GetRowCount(DataGridViewElementStates.Visible) > 1)
                    {
                        // Columns can only expand, and not collapse.
                        AdjustExpandingColumns(autoSizeColumnCriteriaFilter, rowIndex);
                    }
                    else
                    {
                        AutoResizeAllVisibleColumnsInternal(autoSizeColumnCriteriaFilter, fixedHeight: true);
                    }

                    if (autoSizeRow)
                    {
                        // Second round of row autosizing
                        AutoResizeRowInternal(rowIndex, MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode), fixedWidth: true, internalAutosizing: true);
                    }

                    break;
                }
        }

        GetEvent<DataGridViewRowStateChangedEventHandler>(s_rowStateChangedEvent)?.Invoke(this, e);

        if (e.StateChanged == DataGridViewElementStates.ReadOnly && rowIndex == _ptCurrentCell.Y && !_dataGridViewOper[OperationInReadOnlyChange])
        {
            VerifyImeRestrictedModeChanged();

            if ((newState & DataGridViewElementStates.ReadOnly) == 0
                && !ReadOnly
                && !Columns[_ptCurrentCell.X].ReadOnly
                && ColumnEditable(_ptCurrentCell.X)
                && !IsCurrentCellInEditMode
                && (EditMode == DataGridViewEditMode.EditOnEnter
                    || (EditMode != DataGridViewEditMode.EditProgrammatically && CurrentCellInternal.EditType is null)))
            {
                // Current row becomes read/write. Enter editing mode.
                BeginEditInternal(selectAll: true);
            }
        }
    }

    internal void OnRowUnshared(DataGridViewRow dataGridViewRow)
    {
        if (_ptCurrentCell.X != -1 && dataGridViewRow.Index == _ptCurrentCell.Y && EditingControl is not null)
        {
            CurrentCellInternal.CacheEditingControl();
        }

        DataGridViewRowEventArgs dgvre = new(dataGridViewRow);
        OnRowUnshared(dgvre);
    }

    protected virtual void OnRowUnshared(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        GetEvent<DataGridViewRowEventHandler>(s_rowUnsharedEvent)?.Invoke(this, e);
    }

    private bool OnRowValidating(ref DataGridViewCell? dataGridViewCell, int columnIndex, int rowIndex)
    {
        DataGridViewCellCancelEventArgs dgvcce = new(columnIndex, rowIndex);
        OnRowValidating(dgvcce);
        if (!dgvcce.Cancel)
        {
            if (DataConnection is not null
                && DataConnection.InterestedInRowEvents
                && !DataConnection.PositionChangingOutsideDataGridView
                && !DataConnection.ListWasReset)
            {
                DataConnection.OnRowValidating(dgvcce);
            }
        }

        if (dataGridViewCell is not null && rowIndex < Rows.Count && columnIndex < Columns.Count)
        {
            dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
        }

        return dgvcce.Cancel;
    }

    protected virtual void OnRowValidating(DataGridViewCellCancelEventArgs e)
    {
        try
        {
            _noDimensionChangeCount++;

            if (GetEvent<DataGridViewCellCancelEventHandler>(s_rowValidatingEvent) is { } eh)
            {
                eh(this, e);
                CorrectFocus(onlyIfGridHasFocus: true);
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
        }
    }

    private void OnRowValidated(ref DataGridViewCell? dataGridViewCell, int columnIndex, int rowIndex)
    {
        IsCurrentRowDirtyInternal = false;
        _dataGridViewState1[State1_NewRowCreatedByEditing] = false;
        if (rowIndex == NewRowIndex)
        {
            // Stop displaying the default cell values on the 'new row'.
            InvalidateRowPrivate(rowIndex);
        }

        DataGridViewCellEventArgs dgvce = new(columnIndex, rowIndex);
        OnRowValidated(dgvce);
        if (dataGridViewCell is not null)
        {
            if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
            {
                dataGridViewCell = null;
            }
            else
            {
                Debug.Assert(rowIndex < Rows.Count && columnIndex < Columns.Count);
                dataGridViewCell = Rows.SharedRow(rowIndex).Cells[columnIndex];
            }
        }
    }

    protected virtual void OnRowValidated(DataGridViewCellEventArgs e)
    {
        try
        {
            _noDimensionChangeCount++;

            if (GetEvent<DataGridViewCellEventHandler>(s_rowValidatedEvent) is { } eh)
            {
                eh(this, e);
                CorrectFocus(onlyIfGridHasFocus: true);
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
        }
    }

    private void RefreshByCurrentPos(int oldValue, int newValue)
    {
        Point pt = PointToScreen(Location);
        int step = newValue - oldValue;

        // horizontal scroll left
        if (pt.X < 0 && step < 0)
        {
            Invalidate(new Rectangle(new Point(-pt.X, ColumnHeadersHeight), new Size(-step, ClientSize.Height)));
        }

        pt.X += Width;
        pt.Y += Height;
        Rectangle rect = Screen.GetBounds(pt);

        // horizontal scroll right
        if (pt.X > rect.Right && step > 0)
        {
            Invalidate(new Rectangle(new Point(ClientSize.Width - (pt.X - rect.Right) - step, ColumnHeadersHeight),
                                     new Size(step, ClientSize.Height)));
        }

        // vertical scroll up
        if (pt.Y < 0 && step < 0)
        {
            Invalidate(new Rectangle(new Point(0, -pt.Y),
                                     new Size(-step, ClientSize.Width)));
        }

        // vertical scroll down
        if (pt.Y > rect.Bottom && step > 0)
        {
            Invalidate(new Rectangle(new Point(0, ColumnHeadersHeight),
                                     new Size(ClientSize.Width, ClientSize.Height - (pt.Y - rect.Bottom) - step)));
        }
    }

    private void OnScroll(ScrollEventType scrollEventType, int oldValue, int newValue, ScrollOrientation orientation)
    {
        ScrollEventArgs se = new(scrollEventType, oldValue, newValue, orientation);
        OnScroll(se);
        RefreshByCurrentPos(oldValue, newValue);
        if (Focused && IsGridFocusRectangleEnabled())
        {
            InvalidateGridFocusOnScroll(newValue - oldValue, orientation);
        }

        if (orientation == ScrollOrientation.VerticalScroll)
        {
            if (se.NewValue != newValue)
            {
                try
                {
                    _dataGridViewState2[State2_StopRaisingVerticalScroll] = true;
                    int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                    int rowIndexPrevious = rowIndex;
                    newValue = se.NewValue;
                    while (rowIndex != -1 && newValue > 0)
                    {
                        rowIndexPrevious = rowIndex;
                        rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                        newValue--;
                    }

                    if (rowIndex != -1)
                    {
                        rowIndexPrevious = rowIndex;
                    }

                    if (rowIndexPrevious != -1)
                    {
                        FirstDisplayedScrollingRowIndex = rowIndexPrevious;
                    }
                }
                finally
                {
                    _dataGridViewState2[State2_StopRaisingVerticalScroll] = false;
                }
            }
        }
        else
        {
            if (se.NewValue != newValue)
            {
                try
                {
                    _dataGridViewState2[State2_StopRaisingHorizontalScroll] = true;
                    HorizontalOffset = se.NewValue;
                }
                finally
                {
                    _dataGridViewState2[State2_StopRaisingHorizontalScroll] = false;
                }
            }
        }
    }

    protected virtual void OnScroll(ScrollEventArgs e) =>
        GetEvent<ScrollEventHandler>(s_scrollEvent)?.Invoke(this, e);

    protected virtual void OnSelectionChanged(EventArgs e)
    {
        _dataGridViewState2[State2_RaiseSelectionChanged] = false;
        GetEvent<EventHandler>(s_selectionChangedEvent)?.Invoke(this, e);
    }

    internal bool OnSortCompare(
        DataGridViewColumn dataGridViewSortedColumn,
        object? value1,
        object? value2,
        int rowIndex1,
        int rowIndex2,
        out int sortResult)
    {
        DataGridViewSortCompareEventArgs dgvsce = new(
            dataGridViewSortedColumn,
            value1,
            value2,
            rowIndex1,
            rowIndex2);
        OnSortCompare(dgvsce);
        sortResult = dgvsce.SortResult;
        return dgvsce.Handled;
    }

    protected virtual void OnSortCompare(DataGridViewSortCompareEventArgs e) =>
        GetEvent<DataGridViewSortCompareEventHandler>(s_sortCompareEvent)?.Invoke(this, e);

    protected virtual void OnSorted(EventArgs e) => GetEvent<EventHandler>(s_sortedEvent)?.Invoke(this, e);

    internal void OnSortGlyphDirectionChanged(DataGridViewColumnHeaderCell dataGridViewColumnHeaderCell)
    {
        Debug.Assert(dataGridViewColumnHeaderCell is not null);

        if (dataGridViewColumnHeaderCell.OwningColumn == SortedColumn)
        {
            if (dataGridViewColumnHeaderCell.SortGlyphDirection == SortOrder.None)
            {
                SortedColumn = null;
                DataGridViewColumn? dataGridViewColumn = dataGridViewColumnHeaderCell.OwningColumn;

                if (dataGridViewColumn is not null && dataGridViewColumn.IsDataBound)
                {
                    // If the column whose SortGlyphChanges is the sorted column and it is also the dataBound column
                    // then see if there is another dataBound column which has the same property name as the sorted column.
                    // If so, then make that dataGridViewColumn the sorted column in the data grid view.
                    for (int i = 0; i < Columns.Count; i++)
                    {
                        if (dataGridViewColumn != Columns[i]
                            && Columns[i].SortMode != DataGridViewColumnSortMode.NotSortable
                            && string.Compare(
                                dataGridViewColumn.DataPropertyName,
                                Columns[i].DataPropertyName,
                                ignoreCase: true,
                                CultureInfo.InvariantCulture) == 0)
                        {
                            Debug.Assert(Columns[i].IsDataBound, "two columns w/ the same DataPropertyName should be DataBound at the same time");
                            SortedColumn = Columns[i];
                            break;
                        }
                    }
                }
            }

            SortOrder = SortedColumn is not null ? SortedColumn.HeaderCell.SortGlyphDirection : SortOrder.None;
        }

        InvalidateCellPrivate(dataGridViewColumnHeaderCell);
    }

    private void OnTopLeftHeaderMouseDown()
    {
        if (MultiSelect)
        {
            SelectAll();
            if (_ptCurrentCell.X != -1)
            {
                // Potentially have to give focus back to the current edited cell.
                bool success = SetCurrentCellAddressCore(
                    _ptCurrentCell.X,
                    _ptCurrentCell.Y,
                    setAnchorCellAddress: false,
                    validateCurrentCell: false,
                    throughMouseClick: false);
                Debug.Assert(success);
            }
        }
    }

    protected virtual void OnUserAddedRow(DataGridViewRowEventArgs e)
    {
        if (e.Row.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_RowDoesNotBelongToDataGridView, "e.Row");
        }

        GetEvent<DataGridViewRowEventHandler>(s_userAddedRowEvent)?.Invoke(this, e);
    }

    protected virtual void OnUserDeletedRow(DataGridViewRowEventArgs e) =>
        GetEvent<DataGridViewRowEventHandler>(s_userDeletedRowEvent)?.Invoke(this, e);

    protected virtual void OnUserDeletingRow(DataGridViewRowCancelEventArgs e) =>
        GetEvent<DataGridViewRowCancelEventHandler>(s_userDeletingRowEvent)?.Invoke(this, e);

    private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
    {
        if (e.Category is UserPreferenceCategory.Color
            or UserPreferenceCategory.Locale
            or UserPreferenceCategory.General
            or UserPreferenceCategory.Window
            or UserPreferenceCategory.VisualStyle)
        {
            OnGlobalAutoSize();
            if (e.Category == UserPreferenceCategory.Window)
            {
                _cachedEditingControl = null;
                if (EditingControl is not null)
                {
                    // The editing control may not adapt well to the new system rendering,
                    // so instead of caching it into the this.cachedEditingControl variable
                    // next time editing mode is exited, simply discard the control.
                    _dataGridViewState2[State2_DiscardEditingControl] = true;
                }

                PerformLayoutPrivate(
                    useRowShortcut: false,
                    computeVisibleRows: false,
                    invalidInAdjustFillingColumns: false,
                    repositionEditingControl: true);
            }
        }
    }

    protected override void OnValidating(CancelEventArgs e)
    {
        // Avoid Cell/Row Validation events when the grid or its editing control gets the focus
        if (!BecomingActiveControl && (EditingControl is null || !EditingControl.BecomingActiveControl))
        {
            if (!_dataGridViewState1[State1_LeavingWithTabKey])
            {
                if (!EndEdit(
                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.LeaveControl,
                    DataGridViewValidateCellInternal.Always,
                    fireCellLeave: false,
                    fireCellEnter: false,
                    fireRowLeave: false,
                    fireRowEnter: false,
                    fireLeave: false,
                    keepFocus: false,
                    resetCurrentCell: false,
                    resetAnchorCell: false))
                {
                    e.Cancel = true;
                    return;
                }
            }

            if (_ptCurrentCell.X >= 0)
            {
                DataGridViewCell? dataGridViewCellTmp = null;
                if (OnRowValidating(ref dataGridViewCellTmp, _ptCurrentCell.X, _ptCurrentCell.Y))
                {
                    // Row validation was cancelled
                    e.Cancel = true;
                    return;
                }

                if (_ptCurrentCell.X == -1)
                {
                    return;
                }

                OnRowValidated(ref dataGridViewCellTmp, _ptCurrentCell.X, _ptCurrentCell.Y);

                // Row validation was not cancelled, but does operation need to be re-evaluated.
                if (DataSource is not null && _ptCurrentCell.X >= 0 && AllowUserToAddRowsInternal && NewRowIndex == _ptCurrentCell.Y)
                {
                    // Current cell needs to be moved to the row just above the 'new row' if possible.
                    int rowIndex = Rows.GetPreviousRow(_ptCurrentCell.Y, DataGridViewElementStates.Visible);
                    if (rowIndex > -1)
                    {
                        bool success = SetAndSelectCurrentCellAddress(
                            _ptCurrentCell.X,
                            rowIndex,
                            setAnchorCellAddress: true,
                            validateCurrentCell: false,
                            throughMouseClick: false,
                            clearSelection: false,
                            forceCurrentCellSelection: false);

                        Debug.Assert(success);
                    }
                    else
                    {
                        bool success = SetCurrentCellAddressCore(
                            columnIndex: -1,
                            rowIndex: -1,
                            setAnchorCellAddress: true,
                            validateCurrentCell: false,
                            throughMouseClick: false);
                        Debug.Assert(success);
                    }
                }
            }
        }

        base.OnValidating(e);
    }

    protected override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);
        OnVisibleChangedPrivate();
    }

    private void OnVisibleChangedPrivate()
    {
        int rowIndexTmp;

        if (Visible)
        {
            // Make sure all displayed bands get the Displayed state: 1 & 2 for rows

            // 1. Make sure all displayed frozen rows have their Displayed state set to true
            int numDisplayedFrozenRows = DisplayedBandsInfo.NumDisplayedFrozenRows;
            if (numDisplayedFrozenRows > 0)
            {
                rowIndexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                while (numDisplayedFrozenRows > 0)
                {
                    Debug.Assert(rowIndexTmp != -1);
                    if ((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0)
                    {
#if DEBUG
                        int numDisplayedFrozenRowsDbg = numDisplayedFrozenRows;
                        while (numDisplayedFrozenRowsDbg > 0)
                        {
                            Debug.Assert(rowIndexTmp != -1);
                            Debug.Assert((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0);
                            rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                            numDisplayedFrozenRowsDbg--;
                        }
#endif
                        return; // rows' Displayed states are already up-to-date. OnHandleCreated already did the job.
                    }
                    else
                    {
                        Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true);
                        rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                        numDisplayedFrozenRows--;
                    }
                }
            }

            // 2. Make sure all displayed scrolling rows have their Displayed state set to true
            rowIndexTmp = DisplayedBandsInfo.FirstDisplayedScrollingRow;
            if (rowIndexTmp > -1)
            {
                Debug.Assert((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Visible) != 0);
                int numDisplayedScrollingRows = DisplayedBandsInfo.NumDisplayedScrollingRows;
                Debug.Assert(numDisplayedScrollingRows > 0);
                while (numDisplayedScrollingRows > 0)
                {
                    Debug.Assert(rowIndexTmp != -1);
                    if ((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0)
                    {
#if DEBUG
                        int numDisplayedScrollingRowsDbg = numDisplayedScrollingRows;
                        while (numDisplayedScrollingRowsDbg > 0)
                        {
                            Debug.Assert(rowIndexTmp != -1);
                            Debug.Assert((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) != 0);
                            rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible);
                            numDisplayedScrollingRowsDbg--;
                        }
#endif
                        return; // rows' Displayed states are already up-to-date. OnHandleCreated already did the job.
                    }
                    else
                    {
                        Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, true);
                        rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible);
                        numDisplayedScrollingRows--;
                    }
                }
            }
        }
        else
        {
            // Make sure all displayed bands lose the Displayed state
            UpdateRowsDisplayedState(displayed: false);
        }

        UpdateColumnsDisplayedState(displayed: Visible);
    }

    protected virtual void PaintBackground(Graphics graphics, Rectangle clipBounds, Rectangle gridBounds)
    {
        // Paint potential block below rows
        Rectangle rcBelowRows = gridBounds;

        // Remaining cell images on DataGridView
        int visibleRowsHeight = Rows.GetRowsHeight(DataGridViewElementStates.Displayed);
        if (_layout.ColumnHeadersVisible)
        {
            rcBelowRows.Y += _layout.ColumnHeaders.Height;
            rcBelowRows.Height -= _layout.ColumnHeaders.Height;
        }
        else if (SingleHorizontalBorderAdded && visibleRowsHeight > 0)
        {
            rcBelowRows.Y++;
            rcBelowRows.Height--;
        }

        rcBelowRows.Y += visibleRowsHeight;
        rcBelowRows.Height -= visibleRowsHeight;
        if (rcBelowRows.Width > 0 && rcBelowRows.Height > 0)
        {
            using var brush = BackgroundColor.GetCachedSolidBrushScope();
            graphics.FillRectangle(brush, rcBelowRows);
        }

        // Paint potential block next to column headers and rows
        // Remaining cell images on DataGridView
        int visibleColumnsWidth = Columns.GetColumnsWidth(DataGridViewElementStates.Displayed);
        Rectangle rcNextRows = gridBounds;
        if (Columns.Count > 0)
        {
            if (_layout.RowHeadersVisible)
            {
                if (!RightToLeftInternal)
                {
                    rcNextRows.X += _layout.RowHeaders.Width;
                }

                rcNextRows.Width -= _layout.RowHeaders.Width;
            }
            else if (SingleVerticalBorderAdded && visibleColumnsWidth > 0)
            {
                if (!RightToLeftInternal)
                {
                    rcNextRows.X++;
                }

                rcNextRows.Width--;
            }
        }

        int rowsWidth = visibleColumnsWidth - _horizontalOffset;
        if (!RightToLeftInternal)
        {
            rcNextRows.X += rowsWidth;
        }

        rcNextRows.Width -= rowsWidth;
        if (rcBelowRows.Height > 0)
        {
            rcNextRows.Height = gridBounds.Height - rcBelowRows.Height;
        }

        if (rcNextRows.Width > 0 && rcNextRows.Height > 0)
        {
            using var brush = BackgroundColor.GetCachedSolidBrushScope();
            graphics.FillRectangle(brush, rcNextRows);
        }
    }

    private void PaintBorder(Graphics g, Rectangle clipRect, Rectangle bounds)
    {
        Debug.Assert(bounds.Left == 0);
        Debug.Assert(bounds.Top == 0);
        if (BorderStyle == BorderStyle.None)
        {
            return;
        }

        int borderWidth = BorderWidth;

        // Does the clipRect intersect with the top edge?
        Rectangle edge = new(0, 0, bounds.Width, borderWidth);
        bool paintingNeeded = clipRect.IntersectsWith(edge);
        if (!paintingNeeded)
        {
            // Does the clipRect intersect with the bottom edge?
            edge.Y = bounds.Height - borderWidth;
            paintingNeeded = clipRect.IntersectsWith(edge);
            if (!paintingNeeded)
            {
                // Does the clipRect intersect with the left edge?
                edge.Y = 0;
                edge.Height = bounds.Height;
                edge.Width = borderWidth;
                paintingNeeded = clipRect.IntersectsWith(edge);
                if (!paintingNeeded)
                {
                    // Does the clipRect intersect with the right edge?
                    edge.X = bounds.Width - borderWidth;
                    paintingNeeded = clipRect.IntersectsWith(edge);
                }
            }
        }

        if (paintingNeeded)
        {
            if (BorderStyle == BorderStyle.Fixed3D)
            {
                if (Application.RenderWithVisualStyles)
                {
                    using var pen = VisualStyleInformation.TextControlBorder.GetCachedPenScope();
                    g.DrawRectangle(pen, new Rectangle(0, 0, bounds.Width - 1, bounds.Height - 1));
                }
                else
                {
                    ControlPaint.DrawBorder3D(g, bounds, Border3DStyle.Sunken);
                }
            }
            else if (BorderStyle == BorderStyle.FixedSingle)
            {
                using var pen = SystemColors.ControlText.GetCachedPenScope();
                g.DrawRectangle(pen, new Rectangle(0, 0, bounds.Width - 1, bounds.Height - 1));
            }
            else
            {
                Debug.Fail("DataGridView.PaintBorder - Unexpected BorderStyle value.");
            }
        }
    }

    private void PaintColumnHeaders(Graphics g, Rectangle clipBounds, bool singleBorderAdded)
    {
        if (g.IsVisible(_layout.ColumnHeaders))
        {
            Rectangle bandBounds, cellBounds;
            bandBounds = cellBounds = _layout.ColumnHeaders;
            bandBounds.Height = cellBounds.Height = _columnHeadersHeight;
            int cx = 0;
            bool isFirstDisplayedColumn = true;
            bool isLastVisibleColumn = false;
            DataGridViewCell cell;
            DataGridViewCellStyle inheritedCellStyle = new();
            DataGridViewAdvancedBorderStyle dataGridViewAdvancedBorderStylePlaceholder = new(), dgvabsEffective;

            // first paint the visible frozen columns
            DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            DataGridViewColumn? dataGridViewColumnNext;

            while (dataGridViewColumn is not null)
            {
                cell = dataGridViewColumn.HeaderCell;
                cellBounds.Width = dataGridViewColumn.Thickness;
                if (singleBorderAdded && isFirstDisplayedColumn)
                {
                    cellBounds.Width++;
                }

                Debug.Assert(cellBounds.Width > 0);
                cellBounds.X = RightToLeftInternal ? bandBounds.Right - cx - cellBounds.Width : bandBounds.X + cx;

                BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell);

                dataGridViewColumnNext = Columns.GetNextColumn(
                    dataGridViewColumn,
                    DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen,
                    DataGridViewElementStates.None);
                if (dataGridViewColumnNext is null)
                {
                    isLastVisibleColumn = (DisplayedBandsInfo.FirstDisplayedScrollingCol < 0);
                }

                dgvabsEffective = AdjustColumnHeaderBorderStyle(
                    AdvancedColumnHeadersBorderStyle,
                    dataGridViewAdvancedBorderStylePlaceholder,
                    isFirstDisplayedColumn,
                    isLastVisibleColumn);

                // Microsoft: should paintSelectionBackground be dev-settable?
                cell.PaintWork(
                    g,
                    clipBounds,
                    cellBounds,
                    -1,
                    dataGridViewColumn.State,
                    inheritedCellStyle,
                    dgvabsEffective,
                    DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground);

                cx += cellBounds.Width;
                if (cx >= bandBounds.Width)
                {
                    break;
                }

                dataGridViewColumn = dataGridViewColumnNext;
                isFirstDisplayedColumn = false;
            }

            // then paint the visible scrolling ones
            Rectangle scrollingBounds = bandBounds;
            if (!RightToLeftInternal)
            {
                scrollingBounds.X -= FirstDisplayedScrollingColumnHiddenWidth;
            }

            scrollingBounds.Width += FirstDisplayedScrollingColumnHiddenWidth;

            if (DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0 && cx < scrollingBounds.Width)
            {
                Region? clipRegion = null;
                if (FirstDisplayedScrollingColumnHiddenWidth > 0)
                {
                    clipRegion = g.Clip;
                    Rectangle rowRect = bandBounds;
                    if (!RightToLeftInternal)
                    {
                        rowRect.X += cx;
                    }

                    rowRect.Width -= cx;
                    g.SetClip(rowRect);
                }

                dataGridViewColumn = Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol];
                while (dataGridViewColumn is not null)
                {
                    Debug.Assert(dataGridViewColumn.Visible && !dataGridViewColumn.Frozen);

                    cell = dataGridViewColumn.HeaderCell;
                    cellBounds.Width = dataGridViewColumn.Thickness;
                    if (singleBorderAdded && isFirstDisplayedColumn)
                    {
                        cellBounds.Width++;
                    }

                    Debug.Assert(cellBounds.Width > 0);
                    cellBounds.X = RightToLeftInternal
                        ? scrollingBounds.Right - cx - cellBounds.Width
                        : scrollingBounds.X + cx;

                    BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell);

                    dataGridViewColumnNext = Columns.GetNextColumn(dataGridViewColumn,
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);
                    isLastVisibleColumn = (dataGridViewColumnNext is null);

                    dgvabsEffective = AdjustColumnHeaderBorderStyle(
                        AdvancedColumnHeadersBorderStyle,
                        dataGridViewAdvancedBorderStylePlaceholder,
                        isFirstDisplayedColumn,
                        isLastVisibleColumn);

                    cell.PaintWork(g,
                                   clipBounds,
                                   cellBounds,
                                   -1,
                                   dataGridViewColumn.State,
                                   inheritedCellStyle,
                                   dgvabsEffective,
                                   DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground);

                    cx += cellBounds.Width;
                    if (cx >= scrollingBounds.Width)
                    {
                        break;
                    }

                    dataGridViewColumn = dataGridViewColumnNext;
                    isFirstDisplayedColumn = false;
                }

                if (FirstDisplayedScrollingColumnHiddenWidth > 0)
                {
                    Debug.Assert(clipRegion is not null);
                    g.Clip = clipRegion;
                    clipRegion.Dispose();
                }
            }
        }
    }

    private void PaintGrid(
        Graphics g,
        Rectangle gridBounds,
        Rectangle clipRect,
        bool singleVerticalBorderAdded,
        bool singleHorizontalBorderAdded)
    {
        Rectangle rc = gridBounds;

        if (_layout.TopLeftHeader.Width > 0 && (clipRect.IntersectsWith(_layout.TopLeftHeader) || _lastHeaderShadow != -1))
        {
            if (Columns.Count > 0 || Rows.Count > 0)
            {
                using Region clipRegion = g.Clip;
                g.SetClip(_layout.TopLeftHeader);
                PaintTopLeftHeaderCell(g);
                g.Clip = clipRegion;
            }
        }

        if (_layout.ColumnHeadersVisible)
        {
            Rectangle columnHeadersClip = _layout.ColumnHeaders;
            if (singleVerticalBorderAdded)
            {
                columnHeadersClip.Width++;
            }

            if (clipRect.IntersectsWith(columnHeadersClip) || _lastHeaderShadow != -1)
            {
                using Region clipRegion = g.Clip;
                g.SetClip(columnHeadersClip);
                PaintColumnHeaders(g, columnHeadersClip, singleVerticalBorderAdded);
                g.Clip = clipRegion;
            }

            int columnHeadersHeight = _layout.ColumnHeaders.Height;
            rc.Y += columnHeadersHeight;
            rc.Height -= columnHeadersHeight;

            if (_lastHeaderShadow != -1)
            {
                DrawColHeaderShadow(g, _lastHeaderShadow);
            }
        }

        if (rc.Height > 0)
        {
            PaintRows(g, rc, clipRect, /*singleVerticalBorderAdded, */ singleHorizontalBorderAdded);
        }

        if (_currentRowSplitBar != -1)
        {
            Debug.Assert(_dataGridViewOper[OperationTrackColHeadersResize] || _dataGridViewOper[OperationTrackRowResize]);
            DrawRowSplitBar(_currentRowSplitBar);
        }
        else if (_currentColSplitBar != -1)
        {
            Debug.Assert(_dataGridViewOper[OperationTrackRowHeadersResize]
                || _dataGridViewOper[OperationTrackColResize]
                || _dataGridViewOper[OperationTrackKeyboardColResize]);
            DrawColSplitBar(_currentColSplitBar);
        }
    }

    private void PaintRows(Graphics g,
        Rectangle boundingRect,
        Rectangle clipRect,
        /*bool singleVerticalBorderAdded,*/
        bool singleHorizontalBorderAdded)
    {
        int cy = 0;
        Rectangle rowBounds;
        DataGridViewRow dataGridViewRow;
        bool isFirstDisplayedRow = true;
        int indexTmp, indexTmpNext;

        // paint visible none-scrolling rows
        indexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        while (indexTmp != -1)
        {
            rowBounds = boundingRect;

            // DataGridView AutoSizeRowsMode does not work properly after column sort
            // Should un-shared the row and set the thickness to a perfect value
            // every time user scroll to display the specific row.

            DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode;
            // Auto size row if needed
            if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None)
            {
                // this call may un-share the row.
                int rowHeight = Rows.SharedRow(indexTmp).GetHeight(indexTmp);
                Rows.SharedRow(indexTmp).CachedThickness = rowHeight;
                AutoResizeRowInternal(indexTmp, MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode), fixedWidth: false, internalAutosizing: true);
            }

            rowBounds.Height = Rows.SharedRow(indexTmp).GetHeight(indexTmp);
            if (isFirstDisplayedRow && singleHorizontalBorderAdded)
            {
                rowBounds.Height++;
            }

            rowBounds.Y = boundingRect.Y + cy;

            indexTmpNext = Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);

            if (clipRect.IntersectsWith(rowBounds))
            {
                dataGridViewRow = Rows.SharedRow(indexTmp);
                dataGridViewRow.Paint(g,
                    clipRect,
                    rowBounds,
                    indexTmp,
                    Rows.GetRowState(indexTmp),
                    isFirstDisplayedRow,
                    (indexTmpNext == -1) && (DisplayedBandsInfo.FirstDisplayedScrollingRow == -1));
            }

            cy += rowBounds.Height;
            if (cy >= boundingRect.Height)
            {
                break;
            }

            indexTmp = indexTmpNext;
            isFirstDisplayedRow = false;
        }

        // paint scrolling rows
        if (DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0 && cy < boundingRect.Height)
        {
            indexTmp = DisplayedBandsInfo.FirstDisplayedScrollingRow;
            Debug.Assert((Rows.GetRowState(indexTmp) & DataGridViewElementStates.Frozen) == 0);
            Debug.Assert((Rows.GetRowState(indexTmp) & DataGridViewElementStates.Visible) != 0);
            while (indexTmp != -1)
            {
                rowBounds = boundingRect;

                // DataGridView AutoSizeRowsMode does not work properly after column sort
                // Should un-shared the row and set the thickness to a perfect value
                // every time user scroll to display the specific row.

                DataGridViewAutoSizeRowsModeInternal autoSizeRowsModeInternal = (DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode;
                // Auto size row if needed
                if (autoSizeRowsModeInternal != DataGridViewAutoSizeRowsModeInternal.None)
                {
                    // this call may un-share the row.
                    int rowHeight = Rows.SharedRow(indexTmp).GetHeight(indexTmp);
                    Rows.SharedRow(indexTmp).CachedThickness = rowHeight;
                    AutoResizeRowInternal(indexTmp, MapAutoSizeRowsModeToRowMode(_autoSizeRowsMode), fixedWidth: false, internalAutosizing: true);
                }

                rowBounds.Height = Rows.SharedRow(indexTmp).GetHeight(indexTmp);
                if (isFirstDisplayedRow && singleHorizontalBorderAdded)
                {
                    rowBounds.Height++;
                }

                rowBounds.Y = boundingRect.Y + cy;

                indexTmpNext = Rows.GetNextRow(indexTmp, DataGridViewElementStates.Visible);

                if (clipRect.IntersectsWith(rowBounds))
                {
                    dataGridViewRow = Rows.SharedRow(indexTmp);
                    dataGridViewRow.Paint(g,
                        clipRect,
                        rowBounds,
                        indexTmp,
                        Rows.GetRowState(indexTmp),
                        isFirstDisplayedRow,
                        indexTmpNext == -1);
                }

                cy += rowBounds.Height;
                if (cy >= boundingRect.Height)
                {
                    break;
                }

                indexTmp = indexTmpNext;
                isFirstDisplayedRow = false;
            }
        }
    }

    private void PaintTopLeftHeaderCell(Graphics g)
    {
        if (g.IsVisible(_layout.TopLeftHeader))
        {
            DataGridViewCell cell = TopLeftHeaderCell;
            DataGridViewCellStyle inheritedCellStyle = new();
            BuildInheritedColumnHeaderCellStyle(inheritedCellStyle, cell);
            Rectangle cellBounds = _layout.TopLeftHeader;
            cellBounds.Width = RowHeadersWidth;
            cellBounds.Height = _columnHeadersHeight;
            // Microsoft: Should paintSelectionBackground be dev-settable?
            cell.PaintWork(
                g,
                _layout.TopLeftHeader,
                cellBounds,
                -1,
                cell.State,
                inheritedCellStyle,
                AdjustedTopLeftHeaderBorderStyle,
                DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.ErrorIcon | DataGridViewPaintParts.SelectionBackground);
        }
    }

    private void PerformLayoutPrivate(
        bool useRowShortcut,
        bool computeVisibleRows,
        bool invalidInAdjustFillingColumns,
        bool repositionEditingControl)
    {
        _inPerformLayoutCount++;
        try
        {
            if (invalidInAdjustFillingColumns && InAdjustFillingColumns)
            {
                throw new InvalidOperationException(SR.DataGridView_CannotAlterAutoFillColumnParameter);
            }

            if (IsHandleCreated)
            {
                bool columnsAdjusted = false;
                if (useRowShortcut)
                {
                    ComputeLayoutShortcut(computeVisibleRows);
                }
                else
                {
                    columnsAdjusted = ComputeLayout();
                }

                FlushDisplayedChanged();
                if (columnsAdjusted && _inPerformLayoutCount < 3)
                {
                    // Some columns were auto-filled, the rows and column headers may need to be autosized.
                    if ((((DataGridViewAutoSizeRowsModeInternal)_autoSizeRowsMode) & DataGridViewAutoSizeRowsModeInternal.AllColumns) != 0)
                    {
                        AdjustShrinkingRows(_autoSizeRowsMode, fixedWidth: true, internalAutosizing: true);
                    }

                    if (ColumnHeadersHeightSizeMode == DataGridViewColumnHeadersHeightSizeMode.AutoSize)
                    {
                        AutoResizeColumnHeadersHeight(fixedRowHeadersWidth: true, fixedColumnsWidth: true);
                    }
                }

                if (repositionEditingControl && EditingControl is not null)
                {
                    PositionEditingControl(setLocation: true, setSize: false, setFocus: false);
                }
            }
            else
            {
                DisplayedBandsInfo.FirstDisplayedFrozenCol = -1;
                DisplayedBandsInfo.FirstDisplayedFrozenRow = -1;
                DisplayedBandsInfo.FirstDisplayedScrollingRow = -1;
                DisplayedBandsInfo.FirstDisplayedScrollingCol = -1;
                DisplayedBandsInfo.NumDisplayedFrozenRows = 0;
                DisplayedBandsInfo.NumDisplayedFrozenCols = 0;
                DisplayedBandsInfo.NumDisplayedScrollingRows = 0;
                DisplayedBandsInfo.NumDisplayedScrollingCols = 0;
                DisplayedBandsInfo.NumTotallyDisplayedFrozenRows = 0;
                DisplayedBandsInfo.NumTotallyDisplayedScrollingRows = 0;
                DisplayedBandsInfo.LastDisplayedScrollingRow = -1;
                DisplayedBandsInfo.LastTotallyDisplayedScrollingCol = -1;
                if (_layout is not null)
                {
                    _layout._dirty = true;
                }
            }
        }
        finally
        {
            _inPerformLayoutCount--;
            Debug.Assert(_inPerformLayoutCount >= 0);
        }
    }

    private void PopulateNewRowWithDefaultValues()
    {
        if (NewRowIndex != -1)
        {
            DataGridViewRow newRow = Rows.SharedRow(NewRowIndex);
            DataGridViewCellCollection newRowCells = newRow.Cells;
            foreach (DataGridViewCell dataGridViewCell in newRowCells)
            {
                if (dataGridViewCell.DefaultNewRowValue is not null)
                {
                    newRow = Rows[NewRowIndex]; // un-share the 'new row'.
                    newRowCells = newRow.Cells;
                    break;
                }
            }

            foreach (DataGridViewCell dataGridViewCell in newRowCells)
            {
                dataGridViewCell.SetValueInternal(NewRowIndex, dataGridViewCell.DefaultNewRowValue);
            }
        }
    }

    private void PositionEditingControl(bool setLocation, bool setSize, bool setFocus)
    {
        Debug.Assert(EditingControl is not null);

        if (!IsHandleCreated)
        {
            return;
        }

#if DEBUG
        DataGridViewCell dataGridViewCell = CurrentCellInternal;
        ThrowInvalidOperationExceptionIfNull(dataGridViewCell);
        Debug.Assert(dataGridViewCell.ColumnIndex == _ptCurrentCell.X);
        Debug.Assert(dataGridViewCell.RowIndex == _ptCurrentCell.Y || dataGridViewCell.RowIndex == -1);
#endif

        Rectangle editingZone = _layout.Data;
        if (editingZone.Width == 0 || editingZone.Height == 0)
        {
            return;
        }

        _dataGridViewState1[State1_EditingControlChanging] = true;

        try
        {
            int leftEdge = GetColumnXFromIndex(_ptCurrentCell.X);
            if (RightToLeftInternal)
            {
                leftEdge -= Columns[_ptCurrentCell.X].Width - 1;
            }

            Rectangle cellBounds = new(leftEdge, GetRowYFromIndex(_ptCurrentCell.Y),
                                                 Columns[_ptCurrentCell.X].Width, Rows.SharedRow(_ptCurrentCell.Y).GetHeight(_ptCurrentCell.Y));
            Rectangle cellClip = cellBounds;
            // Need to clip the zones of the frozen columns and rows and headers.
            if (!Columns[_ptCurrentCell.X].Frozen)
            {
                int totalVisibleFrozenWidth = Columns.GetColumnsWidth(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                if (!RightToLeftInternal)
                {
                    editingZone.X += totalVisibleFrozenWidth;
                }

                editingZone.Width = Math.Max(0, editingZone.Width - totalVisibleFrozenWidth);
            }

            if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0)
            {
                int totalVisibleFrozenHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                editingZone.Y += totalVisibleFrozenHeight;
            }

            cellClip.Intersect(editingZone);

            if (_editingPanel is null)
            {
                throw new InvalidOperationException();
            }

            if (cellClip.Width == 0 || cellClip.Height == 0)
            {
                // we cannot simply make the control invisible because we want it to keep the focus.
                // (and Control::CanFocus returns false if the control is not visible).
                // So we place the editing control to the right of the DataGridView.
                _editingPanel.Location = new Point(Width + 1, 0);
                _dataGridViewState1[State1_EditingControlHidden] = true;
            }
            else
            {
                bool singleVerticalBorderAdded = SingleVerticalBorderAdded;
                bool singleHorizontalBorderAdded = SingleHorizontalBorderAdded;
                bool isFirstDisplayedColumn = FirstDisplayedColumnIndex == _ptCurrentCell.X;
                bool isFirstDisplayedRow = FirstDisplayedRowIndex == _ptCurrentCell.Y;

                if (singleVerticalBorderAdded && isFirstDisplayedColumn)
                {
                    if (!RightToLeftInternal)
                    {
                        cellBounds.X--;
                        cellClip.X--;
                    }

                    cellBounds.Width++;
                    cellClip.Width++;
                }

                if (singleHorizontalBorderAdded && isFirstDisplayedRow)
                {
                    cellBounds.Y--;
                    cellClip.Y--;
                    cellBounds.Height++;
                    cellClip.Height++;
                }

                if (InheritedEditingCellStyle is null)
                {
                    throw new InvalidOperationException();
                }

                CurrentCellInternal.PositionEditingControl(
                    setLocation || _dataGridViewState1[State1_EditingControlHidden],
                    setSize || _dataGridViewState1[State1_EditingControlHidden],
                    cellBounds,
                    cellClip,
                    InheritedEditingCellStyle,
                    singleVerticalBorderAdded,
                    singleHorizontalBorderAdded,
                    isFirstDisplayedColumn,
                    isFirstDisplayedRow);
                _dataGridViewState1[State1_EditingControlHidden] = false;
            }

            _editingPanel.Visible = true;
            if (setFocus)
            {
                CorrectFocus(onlyIfGridHasFocus: false);
            }
        }
        finally
        {
            _dataGridViewState1[State1_EditingControlChanging] = false;
        }
    }

    protected bool ProcessAKey(Keys keyData)
    {
        if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == Keys.Control && MultiSelect)
        {
            SelectAll();
            return true;
        }

        return false;
    }

    protected bool ProcessDeleteKey(Keys keyData)
    {
        if (AllowUserToDeleteRowsInternal)
        {
            if (EditingControl is not null)
            {
                // editing control gets a chance to handle the Delete key first
                return false;
            }

            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.FullRowSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    int remainingSelectedRows = 0;
                    try
                    {
                        _selectedBandSnapshotIndexes = new DataGridViewIntLinkedList(_selectedBandIndexes);
                        while (_selectedBandSnapshotIndexes.Count > remainingSelectedRows)
                        {
                            int rowIndex = _selectedBandSnapshotIndexes[remainingSelectedRows];
                            Debug.Assert(rowIndex >= 0);
                            if (rowIndex == NewRowIndex || rowIndex >= Rows.Count)
                            {
                                remainingSelectedRows++;
                            }
                            else
                            {
                                DataGridViewRowCancelEventArgs dgvrce = new(Rows[rowIndex]);
                                OnUserDeletingRow(dgvrce);
                                if (!dgvrce.Cancel)
                                {
                                    DataGridViewRow dataGridViewRow = Rows[rowIndex];
                                    if (DataSource is not null)
                                    {
                                        int dataGridRowsCount = Rows.Count;
#if DEBUG
                                        int dataGridViewRowsCount = Rows.Count;           // the number of rows in the dataGridView row collection not counting the AddNewRow

                                        // We already check inside DataSource if DataConnection is null.
                                        int rowCount = DataConnection!.CurrencyManager?.List.Count ?? 0;
                                        if (AllowUserToAddRowsInternal)
                                        {
                                            if (NewRowIndex < rowCount)
                                            {
                                                // the user did not type inside the 'add new row'
                                                Debug.Assert(rowCount == dataGridViewRowsCount, "out of sync in AddNewTransaction when the user did not type in the 'add new row'");
                                            }
                                            else
                                            {
                                                dataGridViewRowsCount--;
                                            }
                                        }

                                        Debug.Assert(rowCount == dataGridViewRowsCount, "out of sync");
#endif
                                        DataGridViewDataErrorEventArgs? dgvdee = null;
                                        try
                                        {
                                            // We already check inside DataSource if DataConnection is null.
                                            DataConnection!.DeleteRow(rowIndex);
                                        }
                                        catch (Exception exception) when (!exception.IsCriticalException())
                                        {
                                            // this is tricky.
                                            // the back-end threw an exception. At that stage, we did not delete the dataGridView row
                                            // from our collection of dataGridView rows.
                                            // So all we do is to throw the exception if the user wants. Otherwise we don't do anything.
                                            dgvdee = new(
                                                exception,
                                                -1,
                                                rowIndex,
                                                DataGridViewDataErrorContexts.RowDeletion);
                                            OnDataErrorInternal(dgvdee);

                                            if (dgvdee.ThrowException)
                                            {
                                                throw dgvdee.Exception;
                                            }
                                            else
                                            {
                                                remainingSelectedRows++;
                                            }
                                        }

                                        if (dataGridRowsCount != Rows.Count)
                                        {
                                            Debug.Assert(dataGridViewRow.Index == -1);
                                            DataGridViewRowEventArgs dgvre = new(dataGridViewRow);
                                            OnUserDeletedRow(dgvre);
                                        }
                                        else if (dgvdee is null)
                                        {
                                            remainingSelectedRows++;
                                        }
                                    }
                                    else
                                    {
                                        Rows.RemoveAtInternal(rowIndex, force: false);
                                        Debug.Assert(dataGridViewRow.Index == -1);
                                        DataGridViewRowEventArgs dgvre = new(dataGridViewRow);
                                        OnUserDeletedRow(dgvre);
                                    }
                                }
                                else
                                {
                                    remainingSelectedRows++;
                                }
                            }
                        }
                    }
                    finally
                    {
                        _selectedBandSnapshotIndexes = null;
                    }

                    return true;
            }
        }

        return false;
    }

    /// <summary>
    ///  Gets or sets a value that indicates whether a key should be processed
    ///  further.
    /// </summary>
    protected override bool ProcessDialogKey(Keys keyData)
    {
        Keys key = (keyData & Keys.KeyCode);

        if (key == Keys.Enter)
        {
            if (ProcessEnterKey(keyData))
            {
                return true;
            }
        }
        else if (key == Keys.Escape)
        {
            bool keyEffective = IsEscapeKeyEffective;
            bool ret = base.ProcessDialogKey(keyData);
            if (!keyEffective)
            {
                // This call may perform Click of Cancel button of form.
                if (Focused)
                {
                    // Make sure the current cell is in editing mode if needed.
                    if (_ptCurrentCell.X > -1
                        && !IsCurrentCellInEditMode
                        && (EditMode == DataGridViewEditMode.EditOnEnter
                            || (EditMode != DataGridViewEditMode.EditProgrammatically && CurrentCellInternal.EditType is null)))
                    {
                        BeginEditInternal(selectAll: true);
                    }
                }
            }

            return ret;
        }
        else if (key is Keys.D0 or Keys.NumPad0)
        {
            if (ProcessZeroKey(keyData))
            {
                return true;
            }
        }
        else if (key is Keys.C or Keys.Insert)
        {
            if (ProcessInsertKey(keyData))
            {
                return true;
            }
        }
        else if (key == Keys.Tab)
        {
            if (ProcessTabKey(keyData))
            {
                return true;
            }
            else
            {
                if (EditingControl is not null)
                {
                    _dataGridViewState1[State1_LeavingWithTabKey] = true;
                    if (!EndEdit(
                        DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.LeaveControl,
                        DataGridViewValidateCellInternal.Always,
                        fireCellLeave: true,
                        fireCellEnter: true,
                        fireRowLeave: true,
                        fireRowEnter: true,
                        fireLeave: true,
                        keepFocus: false,
                        resetCurrentCell: false,
                        resetAnchorCell: false))
                    {
                        return true;
                    }
                }

                keyData &= ~Keys.Control;
                bool result = base.ProcessDialogKey(keyData);

                if (_dataGridViewState1[State1_LeavingWithTabKey] && Focused)
                {
                    // There was no other control to tab to. The CellLeave, RowLeave, Leave events were raised.
                    // Since the DataGridView control still has the focus, Enter, RowEnter, CellEnter events need to be raised.
                    OnEnter(EventArgs.Empty);
                }

                return result;
            }
        }

        return base.ProcessDialogKey(keyData);
    }

    protected bool ProcessDownKey(Keys keyData) => ProcessDownKeyInternal(keyData, out bool _);

    private bool ProcessDownKeyInternal(Keys keyData, out bool moved)
    {
        bool success;
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        int lastVisibleRowIndex = Rows.GetLastRow(DataGridViewElementStates.Visible);
        if (firstVisibleColumnIndex == -1 || lastVisibleRowIndex == -1)
        {
            moved = false;
            return false;
        }

        int nextVisibleRowIndex = -1;
        if (_ptCurrentCell.Y != -1)
        {
            nextVisibleRowIndex = Rows.GetNextRow(_ptCurrentCell.Y, DataGridViewElementStates.Visible);
        }

        moved = true;

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    // SetCurrentCellAddressCore can fail if by navigating to a cell
                                    // the list under the DataGridView changes.
                                    // In this case set moved to false so the users that call ProcessDownKey
                                    // will commit the data.
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (MultiSelect)
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    Debug.Assert(_ptAnchorCell.Y >= 0);
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptCurrentCell.X == -1 || _ptAnchorCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                    {
                                        moved = false;
                                        return true;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        oldEdgeColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        lastVisibleRowIndex);
                                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, false, false, false);
                                    if (!success)
                                    {
                                        moved = false;
                                    }
                                }
                                else
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                    {
                                        moved = false;
                                        return true;
                                    }

                                    ClearSelection();
                                    SetSelectedCellCore(_ptCurrentCell.X, lastVisibleRowIndex, true);
                                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, true, false, false);
                                    if (!success)
                                    {
                                        moved = false;
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedCellCore(_ptCurrentCell.X, lastVisibleRowIndex, true);
                                success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (nextVisibleRowIndex == -1)
                                {
                                    moved = false;
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, nextVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                if (MultiSelect)
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        moved = false;
                                        return true;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        oldEdgeColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        nextVisibleRowIndex);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(_ptCurrentCell.X, nextVisibleRowIndex, true);
                                }

                                success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextVisibleRowIndex, !MultiSelect, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (nextVisibleRowIndex == -1)
                                {
                                    moved = false;
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, nextVisibleRowIndex, forCurrentCellChange: true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedCellCore(_ptCurrentCell.X, nextVisibleRowIndex, true);
                                success = SetCurrentCellAddressCore(
                                    _ptCurrentCell.X,
                                    nextVisibleRowIndex,
                                    setAnchorCellAddress: true,
                                    validateCurrentCell: false,
                                    throughMouseClick: false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedRowCore(lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (MultiSelect)
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptAnchorCell.Y == -1 || _ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                    {
                                        moved = false;
                                        return true;
                                    }

                                    ClearSelection();
                                    Debug.Assert(_ptAnchorCell.Y >= 0);
                                    SelectRowRange(_ptAnchorCell.Y, lastVisibleRowIndex, true);
                                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, false, false, false);
                                    if (!success)
                                    {
                                        moved = false;
                                    }
                                }
                                else
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                    {
                                        moved = false;
                                        return true;
                                    }

                                    SetSelectedRowCore(_ptCurrentCell.Y, false);
                                    SetSelectedRowCore(lastVisibleRowIndex, true);
                                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, true, false, false);
                                    if (!success)
                                    {
                                        moved = false;
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedRowCore(lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedRowCore(lastVisibleRowIndex, true);
                                success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedRowCore(lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (nextVisibleRowIndex == -1)
                                {
                                    moved = false;
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, nextVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                ClearSelection();
                                if (MultiSelect)
                                {
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        moved = false;
                                        return true;
                                    }

                                    if (nextVisibleRowIndex >= _ptAnchorCell.Y)
                                    {
                                        SelectRowRange(_ptAnchorCell.Y, nextVisibleRowIndex, true);
                                    }
                                    else
                                    {
                                        SelectRowRange(nextVisibleRowIndex, _ptAnchorCell.Y, true);
                                    }
                                }
                                else
                                {
                                    SetSelectedRowCore(nextVisibleRowIndex, true);
                                }

                                success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextVisibleRowIndex, !MultiSelect, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedRowCore(lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (nextVisibleRowIndex == -1)
                                {
                                    moved = false;
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, nextVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedRowCore(nextVisibleRowIndex, true);
                                success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.RowHeaderSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (MultiSelect)
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    Debug.Assert(_ptAnchorCell.Y >= 0);
                                    if (_ptAnchorCell.Y == -1 || _ptCurrentCell.Y == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                    {
                                        moved = false;
                                        return true;
                                    }

                                    if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                    {
                                        ClearSelection();
                                        SelectRowRange(_ptAnchorCell.Y, lastVisibleRowIndex, true);
                                    }
                                    else
                                    {
                                        int oldEdgeColumnIndex = _ptCurrentCell.X;
                                        int oldEdgeRowIndex = _ptCurrentCell.Y;
                                        UpdateSelectedCellsBlock(
                                            _ptAnchorCell.X,
                                            ref oldEdgeColumnIndex,
                                            oldEdgeColumnIndex,
                                            _ptAnchorCell.Y,
                                            ref oldEdgeRowIndex,
                                            lastVisibleRowIndex);
                                    }

                                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, false, false, false);
                                    if (!success)
                                    {
                                        moved = false;
                                    }
                                }
                                else
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptCurrentCell.Y == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                    {
                                        moved = false;
                                        return true;
                                    }

                                    if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                    {
                                        SetSelectedRowCore(_ptCurrentCell.Y, false);
                                        SetSelectedRowCore(lastVisibleRowIndex, true);
                                    }
                                    else
                                    {
                                        SetSelectedCellCore(_ptCurrentCell.X, _ptCurrentCell.Y, false);
                                        SetSelectedCellCore(_ptCurrentCell.X, lastVisibleRowIndex, true);
                                    }

                                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, true, false, false);
                                    if (!success)
                                    {
                                        moved = false;
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    SetSelectedRowCore(lastVisibleRowIndex, true);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(_ptCurrentCell.X, lastVisibleRowIndex, true);
                                }

                                success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (nextVisibleRowIndex == -1)
                                {
                                    moved = false;
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, nextVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsRowOutOfBounds(nextVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    if (MultiSelect)
                                    {
                                        if (_ptAnchorCell.Y == -1)
                                        {
                                            moved = false;
                                            return true;
                                        }

                                        if (nextVisibleRowIndex >= _ptAnchorCell.Y)
                                        {
                                            SelectRowRange(_ptAnchorCell.Y, nextVisibleRowIndex, true);
                                        }
                                        else
                                        {
                                            SelectRowRange(nextVisibleRowIndex, _ptAnchorCell.Y, true);
                                        }
                                    }
                                    else
                                    {
                                        SetSelectedRowCore(nextVisibleRowIndex, true);
                                    }
                                }
                                else
                                {
                                    if (MultiSelect)
                                    {
                                        int oldEdgeColumnIndex = _ptCurrentCell.X;
                                        int oldEdgeRowIndex = _ptCurrentCell.Y;
                                        if (_ptAnchorCell.X == -1)
                                        {
                                            moved = false;
                                            return true;
                                        }

                                        UpdateSelectedCellsBlock(
                                            _ptAnchorCell.X,
                                            ref oldEdgeColumnIndex,
                                            oldEdgeColumnIndex,
                                            _ptAnchorCell.Y,
                                            ref oldEdgeRowIndex,
                                            nextVisibleRowIndex);
                                    }
                                    else
                                    {
                                        ClearSelection();
                                        SetSelectedCellCore(_ptCurrentCell.X, nextVisibleRowIndex, true);
                                    }
                                }

                                success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextVisibleRowIndex, !MultiSelect, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, lastVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                            else
                            {
                                if (nextVisibleRowIndex == -1)
                                {
                                    moved = false;
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, nextVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsRowOutOfBounds(nextVisibleRowIndex))
                                {
                                    moved = false;
                                    return true;
                                }

                                if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    SetSelectedRowCore(nextVisibleRowIndex, true);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(_ptCurrentCell.X, nextVisibleRowIndex, true);
                                }

                                success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextVisibleRowIndex, true, false, false);
                                if (!success)
                                {
                                    moved = false;
                                }
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                            {
                                moved = false;
                                return true;
                            }

                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                            if (!success)
                            {
                                moved = false;
                            }
                        }
                        else
                        {
                            if (!ScrollIntoView(_ptCurrentCell.X, lastVisibleRowIndex, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(lastVisibleRowIndex))
                            {
                                moved = false;
                                return true;
                            }

                            success = SetCurrentCellAddressCore(_ptCurrentCell.X, lastVisibleRowIndex, true, false, false);
                            if (!success)
                            {
                                moved = false;
                            }
                        }
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            success = ScrollIntoView(firstVisibleColumnIndex, lastVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, lastVisibleRowIndex))
                            {
                                moved = false;
                                return true;
                            }

                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                            if (!success)
                            {
                                moved = false;
                            }
                        }
                        else
                        {
                            if (nextVisibleRowIndex == -1)
                            {
                                moved = false;
                                return true;
                            }

                            if (!ScrollIntoView(_ptCurrentCell.X, nextVisibleRowIndex, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextVisibleRowIndex))
                            {
                                moved = false;
                                return true;
                            }

                            success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextVisibleRowIndex, true, false, false);
                            if (!success)
                            {
                                moved = false;
                            }
                        }
                    }

                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    protected bool ProcessEndKey(Keys keyData)
    {
        DataGridViewColumn? dataGridViewColumn = Columns.GetLastColumn(
            DataGridViewElementStates.Visible,
            DataGridViewElementStates.None);
        int lastVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
        int lastVisibleRowIndex = Rows.GetLastRow(DataGridViewElementStates.Visible);
        if (lastVisibleColumnIndex == -1 || firstVisibleRowIndex == -1)
        {
            return false;
        }

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true))
                        {
                            return true;
                        }

                        if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex))
                        {
                            return true;
                        }

                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (MultiSelect && _ptAnchorCell.X >= 0)
                            {
                                int oldEdgeColumnIndex = _ptCurrentCell.X;
                                int oldEdgeRowIndex = _ptCurrentCell.Y;
                                UpdateSelectedCellsBlock(
                                    _ptAnchorCell.X,
                                    ref oldEdgeColumnIndex,
                                    lastVisibleColumnIndex,
                                    _ptAnchorCell.Y,
                                    ref oldEdgeRowIndex,
                                    lastVisibleRowIndex);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true);
                            }

                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            ClearSelection();
                            SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true);
                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.RowHeaderSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true))
                        {
                            return true;
                        }

                        if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex))
                        {
                            return true;
                        }

                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (MultiSelect && _ptAnchorCell.X >= 0)
                            {
                                if (_ptCurrentCell.Y > -1
                                    && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    SelectRowRange(_ptCurrentCell.Y, lastVisibleRowIndex, select: true);
                                }
                                else
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        lastVisibleColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        lastVisibleRowIndex);
                                }
                            }
                            else
                            {
                                if (_ptCurrentCell.Y > -1
                                    && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    SetSelectedRowCore(lastVisibleRowIndex, selected: true);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true);
                                }
                            }

                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            if (_ptCurrentCell.Y > -1
                                && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                            {
                                ClearSelection();
                                SetSelectedRowCore(lastVisibleRowIndex, selected: true);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true);
                            }

                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true))
                        {
                            return true;
                        }

                        if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex))
                        {
                            return true;
                        }

                        ClearSelection();
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (MultiSelect && _ptCurrentCell.X >= 0)
                            {
                                SelectColumnRange(_ptAnchorCell.X, lastVisibleColumnIndex, true);
                            }
                            else
                            {
                                SetSelectedColumnCore(lastVisibleColumnIndex, true);
                            }

                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            SetSelectedColumnCore(lastVisibleColumnIndex, true);
                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true))
                            {
                                return true;
                            }

                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X >= 0 && Columns[_ptCurrentCell.X].Selected)
                            {
                                ClearSelection();
                                if (MultiSelect)
                                {
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return true;
                                    }

                                    SelectColumnRange(_ptAnchorCell.X, lastVisibleColumnIndex, true);
                                }

                                SetSelectedColumnCore(lastVisibleColumnIndex, true);
                            }
                            else
                            {
                                if (MultiSelect && _ptCurrentCell.X >= 0)
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return true;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        lastVisibleColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        lastVisibleRowIndex);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true);
                                }
                            }

                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true))
                            {
                                return true;
                            }

                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X >= 0 && Columns[_ptCurrentCell.X].Selected)
                            {
                                ClearSelection();
                                SetSelectedColumnCore(lastVisibleColumnIndex, true);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, lastVisibleRowIndex, true);
                            }

                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (!ScrollIntoView(lastVisibleColumnIndex, lastVisibleRowIndex, true))
                        {
                            return true;
                        }

                        if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, lastVisibleRowIndex))
                        {
                            return true;
                        }

                        ClearSelection();
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (MultiSelect && _ptCurrentCell.Y >= 0)
                            {
                                if (_ptAnchorCell.Y == -1)
                                {
                                    return true;
                                }

                                SelectRowRange(_ptAnchorCell.Y, lastVisibleRowIndex, true);
                            }
                            else
                            {
                                SetSelectedRowCore(lastVisibleRowIndex, true);
                            }

                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            SetSelectedRowCore(lastVisibleRowIndex, true);
                            SetCurrentCellAddressCore(lastVisibleColumnIndex, lastVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    protected bool ProcessEnterKey(Keys keyData)
    {
        // When Enter is pressed, no matter Ctrl is also pressed or not,
        // changes in a cell should be commited.
        // Therefore, commitRow should be always true, and useless here.

        bool moved = false, ret = true; // , commitRow = true;
        if ((keyData & Keys.Control) == 0)
        {
            // Enter behaves like down arrow - it commits the potential editing and goes down one cell.
            // commitRow = false;
            keyData &= ~Keys.Shift;
            ret = ProcessDownKeyInternal(keyData, out moved);
        }

        if (!moved)
        {
            DataGridViewCell? dataGridViewCurrentCell = null;
            // Try to commit the potential editing
            if (EditMode == DataGridViewEditMode.EditOnEnter)
            {
                if (_ptCurrentCell.X != -1)
                {
                    dataGridViewCurrentCell = CurrentCellInternal;
                    DataGridViewDataErrorEventArgs? dgvdee = CommitEdit(
                        ref dataGridViewCurrentCell,
                        DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit,
                        DataGridViewValidateCellInternal.WhenChanged,
                        fireCellLeave: false,
                        fireCellEnter: false,
                        fireRowLeave: false,
                        fireRowEnter: false,
                        fireLeave: false);
                    if (dgvdee is not null)
                    {
                        if (dgvdee.ThrowException)
                        {
                            throw dgvdee.Exception;
                        }
                    }
                }
            }
            else
            {
                EndEdit(
                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit,
                    validateCell: DataGridViewValidateCellInternal.WhenChanged,
                    fireCellLeave: false,
                    fireCellEnter: false,
                    fireRowLeave: false,
                    fireRowEnter: false,
                    fireLeave: false,
                    keepFocus: true,
                    resetCurrentCell: true,
                    resetAnchorCell: true);
            }

            if (/*commitRow && */IsCurrentRowDirty)
            {
                dataGridViewCurrentCell = null;
                int columnIndex = _ptCurrentCell.X;
                int rowIndex = _ptCurrentCell.Y;
                if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
                {
                    return ret;
                }

                if (!OnRowValidating(ref dataGridViewCurrentCell, columnIndex, rowIndex))
                {
                    if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
                    {
                        return ret;
                    }

                    OnRowValidated(ref dataGridViewCurrentCell, columnIndex, rowIndex);
                }
            }
        }

        return ret;
    }

    protected bool ProcessEscapeKey(Keys keyData)
    {
        if (IsEscapeKeyEffective)
        {
            if (IsMouseOperationActive())
            {
                ResetTrackingState();
            }
            else
            {
                CancelEdit(endEdit: true /*, DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.InitialValueRestoration*/);
            }

            return true;
        }

        return false;
    }

    protected bool ProcessF2Key(Keys keyData)
    {
        if (_ptCurrentCell.X != -1 && !IsCurrentCellInEditMode && ModifierKeys == 0)
        {
            Debug.Assert(_ptCurrentCell.Y != -1);
            Debug.Assert(CurrentCellInternal is not null);
            Debug.Assert(EditMode != DataGridViewEditMode.EditOnEnter
                || IsSharedCellReadOnly(CurrentCellInternal, _ptCurrentCell.Y)
                || !ColumnEditable(_ptCurrentCell.X));

            if (ColumnEditable(_ptCurrentCell.X)
                && !IsSharedCellReadOnly(CurrentCellInternal, _ptCurrentCell.Y)
                && (EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2 || EditMode == DataGridViewEditMode.EditOnF2))
            {
                bool success = ScrollIntoView(_ptCurrentCell.X, _ptCurrentCell.Y, forCurrentCellChange: false);
                Debug.Assert(success);
                BeginEditInternal(selectAll: EditMode == DataGridViewEditMode.EditOnF2);
                return true;
            }
        }

        return false;
    }

    /// <summary>
    ///  Sorts the current column.
    ///  'UseLegacyAccessibilityFeatures2' accessibility switch
    ///  should be set to false to enable the feature.
    /// </summary>
    protected bool ProcessF3Key(Keys keyData)
    {
        if (_ptCurrentCell.X != -1)
        {
            DataGridViewColumn dataGridViewColumn = Columns[_ptCurrentCell.X];
            if (dataGridViewColumn is not null && CanSort(dataGridViewColumn))
            {
                ListSortDirection listSortDirection = SortedColumn == dataGridViewColumn && SortOrder == SortOrder.Ascending ?
                    ListSortDirection.Descending : ListSortDirection.Ascending;

                Sort(dataGridViewColumn, listSortDirection);
                return true;
            }
        }

        return false;
    }

    protected bool ProcessHomeKey(Keys keyData)
    {
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
        if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1)
        {
            return false;
        }

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true))
                        {
                            return true;
                        }

                        if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                        {
                            return true;
                        }

                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (MultiSelect && _ptAnchorCell.X >= 0)
                            {
                                int oldEdgeColumnIndex = _ptCurrentCell.X;
                                int oldEdgeRowIndex = _ptCurrentCell.Y;
                                UpdateSelectedCellsBlock(
                                    _ptAnchorCell.X,
                                    ref oldEdgeColumnIndex,
                                    firstVisibleColumnIndex,
                                    _ptAnchorCell.Y,
                                    ref oldEdgeRowIndex,
                                    firstVisibleRowIndex);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                            }

                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            ClearSelection();
                            SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.RowHeaderSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true))
                        {
                            return true;
                        }

                        if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                        {
                            return true;
                        }

                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (MultiSelect && _ptAnchorCell.X >= 0)
                            {
                                if (_ptCurrentCell.Y > -1
                                    && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    SelectRowRange(firstVisibleRowIndex, _ptAnchorCell.Y, select: true);
                                }
                                else
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        firstVisibleColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        firstVisibleRowIndex);
                                }
                            }
                            else
                            {
                                if (_ptCurrentCell.Y > -1
                                    && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    SetSelectedRowCore(firstVisibleRowIndex, true);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                }
                            }

                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            if (_ptCurrentCell.Y > -1
                                && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                            {
                                ClearSelection();
                                SetSelectedRowCore(firstVisibleRowIndex, true);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                            }

                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true))
                        {
                            return true;
                        }

                        if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                        {
                            return true;
                        }

                        ClearSelection();
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (MultiSelect && _ptCurrentCell.X >= 0)
                            {
                                if (_ptAnchorCell.X == -1)
                                {
                                    return true;
                                }

                                SelectColumnRange(firstVisibleColumnIndex, _ptAnchorCell.X, true);
                            }
                            else
                            {
                                SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            }

                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true))
                            {
                                return true;
                            }

                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X >= 0 && Columns[_ptCurrentCell.X].Selected)
                            {
                                ClearSelection();
                                if (MultiSelect)
                                {
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return true;
                                    }

                                    SelectColumnRange(firstVisibleColumnIndex, _ptAnchorCell.X, true);
                                }

                                SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            }
                            else
                            {
                                if (MultiSelect && _ptCurrentCell.X >= 0)
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return true;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        firstVisibleColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        firstVisibleRowIndex);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                }
                            }

                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, !MultiSelect, false, false);
                        }
                        else
                        {
                            if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true))
                            {
                                return true;
                            }

                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X >= 0 && Columns[_ptCurrentCell.X].Selected)
                            {
                                ClearSelection();
                                SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                            }

                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if ((keyData & Keys.Control) == 0)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (!ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, true))
                        {
                            return true;
                        }

                        if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                        {
                            return true;
                        }

                        ClearSelection();
                        SetSelectedRowCore(firstVisibleRowIndex, true);
                        SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                    }

                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    protected bool ProcessInsertKey(Keys keyData)
    {
        if (((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == Keys.Control
                || ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == (Keys.Control | Keys.Shift)
                    && (keyData & Keys.KeyCode) == Keys.C))
            && ClipboardCopyMode != DataGridViewClipboardCopyMode.Disable)
        {
            DataObject? dataObject = GetClipboardContent();
            if (dataObject is not null)
            {
                Clipboard.SetDataObject(dataObject);
                return true;
            }
        }

        return false;
    }

    protected override bool ProcessKeyEventArgs(ref Message m)
    {
        if (m.Msg is ((int)PInvokeCore.WM_SYSKEYDOWN) or ((int)PInvokeCore.WM_KEYDOWN))
        {
            if (_ptCurrentCell.X != -1)
            {
                DataGridViewCell dataGridViewCell = CurrentCellInternal;
                ThrowInvalidOperationExceptionIfNull(dataGridViewCell);

                if (!IsCurrentCellInEditMode
                    && ColumnEditable(_ptCurrentCell.X)
                    && !IsSharedCellReadOnly(dataGridViewCell, _ptCurrentCell.Y)
                    && (EditMode == DataGridViewEditMode.EditOnKeystroke || EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2))
                {
                    KeyEventArgs ke = new((Keys)(nint)m.WParamInternal | ModifierKeys);
                    if (ke.KeyCode != Keys.ProcessKey || m.LParamInternal != 0x01) // Changing IME context does not trigger editing mode
                    {
                        Type? editControlType = dataGridViewCell.EditType;
                        Type? editingCellInterface = null;
                        if (editControlType is null)
                        {
                            // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell?
                            editingCellInterface = dataGridViewCell.GetType().GetInterface("System.Windows.Forms.IDataGridViewEditingCell");
                        }

                        if ((editControlType is not null || editingCellInterface is null) && dataGridViewCell.KeyEntersEditMode(ke))
                        {
                            // Cell wants to go to edit mode
                            bool success = ScrollIntoView(_ptCurrentCell.X, _ptCurrentCell.Y, false);
                            Debug.Assert(success);
                            if (BeginEditInternal(selectAll: !(ke.KeyCode == Keys.F2 && ModifierKeys == 0 && EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2)))
                            {
                                // Forward the key message to the editing control if any
                                if (EditingControl is not null)
                                {
                                    PInvokeCore.SendMessage(EditingControl, m.MsgInternal, m.WParamInternal, m.LParamInternal);
                                    _dataGridViewState1[State1_ForwardCharMessage] = true;
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }
        else if (_dataGridViewState1[State1_ForwardCharMessage]
            && (m.MsgInternal == PInvokeCore.WM_SYSCHAR || m.MsgInternal == PInvokeCore.WM_CHAR || m.MsgInternal == PInvokeCore.WM_IME_CHAR))
        {
            _dataGridViewState1[State1_ForwardCharMessage] = false;
            if (EditingControl is not null)
            {
                PInvokeCore.SendMessage(EditingControl, m.MsgInternal, m.WParamInternal, m.LParamInternal);
                return true;
            }
        }

        return base.ProcessKeyEventArgs(ref m);
    }

    protected override bool ProcessKeyPreview(ref Message m)
    {
        bool dataGridViewWantsInputKey;
        KeyEventArgs ke = new((Keys)(nint)m.WParamInternal | ModifierKeys);

        // Refactor the special keys into two parts.
        // 1. Escape and Space exist in both WM_CHAR and WM_KEYDOWN, WM_KEYUP.
        // 2. Other special keys do not exist in WM_CHAR message, and character code of WM_CHAR may have overlapped
        // w/ some of the key code. (Like character code of lowercase "q" is 0x71, it's overlapped w/ Keys.F2). This
        // may introduce problem when handling them.
        dataGridViewWantsInputKey = m.MsgInternal == PInvokeCore.WM_CHAR
            ? ke.KeyCode switch
            {
                Keys.Escape or Keys.Space => true,
                _ => false,
            }
            : ke.KeyCode switch
            {
                Keys.Delete
                    or Keys.Down
                    or Keys.End
                    or Keys.Enter
                    or Keys.Escape
                    or Keys.F2
                    or Keys.F3
                    or Keys.Home
                    or Keys.Left
                    or Keys.Next
                    or Keys.Prior
                    or Keys.Right
                    or Keys.Space
                    or Keys.Tab
                    or Keys.Up => true,
                _ => false,
            };

        if (EditingControl is not null && (m.MsgInternal == PInvokeCore.WM_KEYDOWN || m.MsgInternal == PInvokeCore.WM_SYSKEYDOWN))
        {
            _dataGridViewState2[State2_CurrentCellWantsInputKey] =
                ((IDataGridViewEditingControl)EditingControl).EditingControlWantsInputKey(ke.KeyData, dataGridViewWantsInputKey);
        }

        if (_dataGridViewState2[State2_CurrentCellWantsInputKey])
        {
            return base.ProcessKeyPreview(ref m);
        }

        if (dataGridViewWantsInputKey)
        {
            if (m.MsgInternal == PInvokeCore.WM_KEYDOWN || m.MsgInternal == PInvokeCore.WM_SYSKEYDOWN)
            {
                if (ProcessDataGridViewKey(ke))
                {
                    return true;
                    // Ctrl-Tab will be sent as a tab paired w/ a control on the KeyUp message
                }
                else
                {
                    _dataGridViewState2[State2_CurrentCellWantsInputKey] = true;
                }
            }
            else
            {
                return true;
            }
        }

        return base.ProcessKeyPreview(ref m);
    }

    private bool? ProcessColumnResize(Keys keyData, int step)
    {
        if ((keyData & Keys.Alt) == Keys.Alt && AllowUserToResizeColumns && _ptCurrentCell.X != -1)
        {
            if (_currentColSplitBar == -1)
            {
                DataGridViewColumn dataGridViewColumn = Columns[_ptCurrentCell.X];
                if (dataGridViewColumn is not null && dataGridViewColumn.Resizable == DataGridViewTriState.True
                    && (dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.None
                        || dataGridViewColumn.InheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill))
                {
                    BeginKeyboardColumnResize(_ptCurrentCell.X);
                    return true;
                }

                return false;
            }
            else
            {
                int x = _currentColSplitBar + step;
                if (_dataGridViewOper[OperationTrackKeyboardColResize] && _resizeClipRectangle.Contains(x, _resizeClipRectangle.Top))
                {
                    MoveRowHeadersOrColumnResize(x);
                    return true;
                }

                return false;
            }
        }

        return null;
    }

    /// <summary>
    ///  Activates keyboard tooltip.
    /// </summary>
    protected bool ProcessControlShiftF10Keys(Keys keyData)
    {
        if (CurrentCell is null || _ptCurrentCell.X == -1 || _ptCurrentCell.Y == -1)
        {
            return false;
        }

        DataGridViewCell dataGridViewCell = CurrentCell;

        ActivateToolTip(activate: false, toolTipText: string.Empty, dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex);
        if (KeyboardToolTip.IsActivatedByKeyboard)
        {
            KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(dataGridViewCell);
        }
        else
        {
            KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(dataGridViewCell);
        }

        return true;
    }

    protected bool ProcessLeftKey(Keys keyData) => RightToLeftInternal
        ? ProcessRightKeyPrivate(keyData)
        : ProcessLeftKeyPrivate(keyData);

    private bool ProcessLeftKeyPrivate(Keys keyData)
    {
        bool? resizeResult = ProcessColumnResize(keyData, -_keyboardResizeStep);
        if (resizeResult.HasValue)
        {
            return resizeResult.Value;
        }

        bool success;
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
        if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1)
        {
            return false;
        }

        int previousVisibleColumnIndex = -1;
        if (_ptCurrentCell.X != -1)
        {
            dataGridViewColumn = Columns.GetPreviousColumn(Columns[_ptCurrentCell.X],
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
            if (dataGridViewColumn is not null)
            {
                previousVisibleColumnIndex = dataGridViewColumn.Index;
            }
        }

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (previousVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(previousVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex))
                                {
                                    return true;
                                }

                                if (MultiSelect)
                                {
                                    Debug.Assert(_ptAnchorCell.X >= 0);
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return true;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        previousVisibleColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        oldEdgeRowIndex);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(previousVisibleColumnIndex, _ptCurrentCell.Y, true);
                                }

                                success = SetCurrentCellAddressCore(previousVisibleColumnIndex, _ptCurrentCell.Y, !MultiSelect, false, false);
                                Debug.Assert(success);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (previousVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(previousVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedCellCore(previousVisibleColumnIndex, _ptCurrentCell.Y, true);
                                success = SetCurrentCellAddressCore(previousVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedColumnCore(firstVisibleColumnIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (previousVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(previousVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                if (MultiSelect)
                                {
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return true;
                                    }

                                    if (Columns.DisplayInOrder(_ptAnchorCell.X, previousVisibleColumnIndex))
                                    {
                                        SelectColumnRange(_ptAnchorCell.X, previousVisibleColumnIndex, true);
                                    }
                                    else
                                    {
                                        SelectColumnRange(previousVisibleColumnIndex, _ptAnchorCell.X, true);
                                    }
                                }
                                else
                                {
                                    SetSelectedColumnCore(previousVisibleColumnIndex, true);
                                }

                                success = SetCurrentCellAddressCore(previousVisibleColumnIndex, _ptCurrentCell.Y, !MultiSelect, false, false);
                                Debug.Assert(success);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedColumnCore(firstVisibleColumnIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (previousVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(previousVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedColumnCore(previousVisibleColumnIndex, true);
                                success = SetCurrentCellAddressCore(previousVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (previousVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(previousVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex))
                                {
                                    return true;
                                }

                                if (Columns[_ptCurrentCell.X].Selected)
                                {
                                    ClearSelection();
                                    if (MultiSelect)
                                    {
                                        if (_ptAnchorCell.X == -1)
                                        {
                                            return true;
                                        }

                                        if (Columns.DisplayInOrder(_ptAnchorCell.X, previousVisibleColumnIndex))
                                        {
                                            SelectColumnRange(_ptAnchorCell.X, previousVisibleColumnIndex, true);
                                        }
                                        else
                                        {
                                            SelectColumnRange(previousVisibleColumnIndex, _ptAnchorCell.X, true);
                                        }
                                    }

                                    SetSelectedColumnCore(previousVisibleColumnIndex, true);
                                }
                                else
                                {
                                    if (MultiSelect)
                                    {
                                        int oldEdgeColumnIndex = _ptCurrentCell.X;
                                        int oldEdgeRowIndex = _ptCurrentCell.Y;
                                        if (_ptAnchorCell.X == -1)
                                        {
                                            return true;
                                        }

                                        UpdateSelectedCellsBlock(
                                            _ptAnchorCell.X,
                                            ref oldEdgeColumnIndex,
                                            previousVisibleColumnIndex,
                                            _ptAnchorCell.Y,
                                            ref oldEdgeRowIndex,
                                            oldEdgeRowIndex);
                                    }
                                    else
                                    {
                                        ClearSelection();
                                        SetSelectedCellCore(previousVisibleColumnIndex, _ptCurrentCell.Y, true);
                                    }
                                }

                                success = SetCurrentCellAddressCore(previousVisibleColumnIndex, _ptCurrentCell.Y, !MultiSelect, false, false);
                                Debug.Assert(success);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (previousVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(previousVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex))
                                {
                                    return true;
                                }

                                if (Columns[_ptCurrentCell.X].Selected)
                                {
                                    ClearSelection();
                                    SetSelectedColumnCore(previousVisibleColumnIndex, true);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(previousVisibleColumnIndex, _ptCurrentCell.Y, true);
                                }

                                success = SetCurrentCellAddressCore(previousVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        return ProcessLeftMost((keyData & Keys.Shift) == Keys.Shift, firstVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedRowCore(firstVisibleRowIndex, true);
                            success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (previousVisibleColumnIndex == -1)
                            {
                                return true;
                            }

                            if (!ScrollIntoView(previousVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(previousVisibleColumnIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(previousVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                            Debug.Assert(success);
                        }
                    }

                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    // Ctrl Left <==> Home
    // Shift Ctrl Left <==> Shift Home
    private bool ProcessLeftMost(bool shift, int firstVisibleColumnIndex, int firstVisibleRowIndex)
    {
        bool success;

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    if (shift)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                            success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (MultiSelect)
                            {
                                if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                Debug.Assert(_ptAnchorCell.X >= 0);
                                int oldEdgeColumnIndex = _ptCurrentCell.X;
                                int oldEdgeRowIndex = _ptCurrentCell.Y;
                                if (_ptAnchorCell.X == -1 || _ptCurrentCell.X == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                                {
                                    return true;
                                }

                                UpdateSelectedCellsBlock(
                                    _ptAnchorCell.X,
                                    ref oldEdgeColumnIndex,
                                    firstVisibleColumnIndex,
                                    _ptAnchorCell.Y,
                                    ref oldEdgeRowIndex,
                                    oldEdgeRowIndex);
                                success = SetCurrentCellAddressCore(
                                    firstVisibleColumnIndex,
                                    _ptCurrentCell.Y,
                                    setAnchorCellAddress: false,
                                    validateCurrentCell: false,
                                    throughMouseClick: false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, _ptCurrentCell.Y, true);
                                success = SetCurrentCellAddressCore(
                                    firstVisibleColumnIndex,
                                    _ptCurrentCell.Y,
                                    setAnchorCellAddress: true,
                                    validateCurrentCell: false,
                                    throughMouseClick: false);
                                Debug.Assert(success);
                            }
                        }
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, selected: true);
                            success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, forCurrentCellChange: false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(
                                firstVisibleColumnIndex,
                                firstVisibleRowIndex,
                                setAnchorCellAddress: true,
                                validateCurrentCell: false,
                                throughMouseClick: false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, forCurrentCellChange: true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                            {
                                return true;
                            }

                            ClearSelection();
                            SetSelectedCellCore(firstVisibleColumnIndex, _ptCurrentCell.Y, selected: true);
                            success = SetCurrentCellAddressCore(
                                firstVisibleColumnIndex,
                                _ptCurrentCell.Y,
                                setAnchorCellAddress: true,
                                validateCurrentCell: false,
                                throughMouseClick: false);
                            Debug.Assert(success);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if (shift)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedColumnCore(firstVisibleColumnIndex, selected: true);
                            success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, forCurrentCellChange: false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(
                                firstVisibleColumnIndex,
                                firstVisibleRowIndex,
                                setAnchorCellAddress: true,
                                validateCurrentCell: false,
                                throughMouseClick: false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (MultiSelect)
                            {
                                if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptAnchorCell.X == -1 || _ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                Debug.Assert(_ptAnchorCell.X >= 0);
                                SelectColumnRange(firstVisibleColumnIndex, _ptAnchorCell.X, true);
                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, _ptCurrentCell.Y, false, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                                {
                                    return true;
                                }

                                SetSelectedColumnCore(_ptCurrentCell.X, false);
                                SetSelectedColumnCore(firstVisibleColumnIndex, true);
                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                            {
                                return true;
                            }

                            ClearSelection();
                            SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                            Debug.Assert(success);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if (shift)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                            success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (MultiSelect)
                            {
                                if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptAnchorCell.X == -1 || _ptCurrentCell.X == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                                {
                                    return true;
                                }

                                Debug.Assert(_ptAnchorCell.X >= 0);
                                if (Columns[_ptCurrentCell.X].Selected)
                                {
                                    ClearSelection();
                                    SelectColumnRange(firstVisibleColumnIndex, _ptAnchorCell.X, true);
                                }
                                else
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        firstVisibleColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        oldEdgeRowIndex);
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, _ptCurrentCell.Y, false, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                if (Columns[_ptCurrentCell.X].Selected)
                                {
                                    SetSelectedColumnCore(_ptCurrentCell.X, false);
                                    SetSelectedColumnCore(firstVisibleColumnIndex, true);
                                }
                                else
                                {
                                    SetSelectedCellCore(_ptCurrentCell.X, _ptCurrentCell.Y, false);
                                    SetSelectedCellCore(firstVisibleColumnIndex, _ptCurrentCell.Y, true);
                                }

                                success = SetCurrentCellAddressCore(firstVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                            success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                            {
                                return true;
                            }

                            if (Columns[_ptCurrentCell.X].Selected)
                            {
                                ClearSelection();
                                SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, _ptCurrentCell.Y, true);
                            }

                            success = SetCurrentCellAddressCore(firstVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                            Debug.Assert(success);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if (_ptCurrentCell.X == -1)
                    {
                        ClearSelection();
                        SetSelectedRowCore(firstVisibleRowIndex, true);
                        success = ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                        Debug.Assert(success);
                        if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                        {
                            return true;
                        }

                        success = SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                        Debug.Assert(success);
                    }
                    else
                    {
                        if (!ScrollIntoView(firstVisibleColumnIndex, _ptCurrentCell.Y, true))
                        {
                            return true;
                        }

                        if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(firstVisibleColumnIndex))
                        {
                            return true;
                        }

                        success = SetCurrentCellAddressCore(firstVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                        Debug.Assert(success);
                    }

                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    protected bool ProcessNextKey(Keys keyData)
    {
        bool success;
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        if (firstVisibleColumnIndex == -1)
        {
            return false;
        }

        int nextScreenVisibleRowIndexTmp;
        int nextScreenVisibleRowIndex;
        int jumpRows = 0;

        if (_ptCurrentCell.Y == -1)
        {
            nextScreenVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
            if (nextScreenVisibleRowIndex == -1)
            {
                return false;
            }
        }
        else
        {
            nextScreenVisibleRowIndex = _ptCurrentCell.Y;
        }

        if ((Rows.GetRowState(nextScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0)
        {
            if (DisplayedBandsInfo.FirstDisplayedScrollingRow > 0)
            {
                int firstDisplayedScrollingRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
                Debug.Assert(firstDisplayedScrollingRowIndex != -1);
                if (!ScrollIntoView(_ptCurrentCell.X == -1 ? firstVisibleColumnIndex : _ptCurrentCell.X,
                    firstDisplayedScrollingRowIndex, true))
                {
                    return true;
                }

                jumpRows = Rows.GetRowCount(DataGridViewElementStates.Visible,
                    _ptCurrentCell.Y,
                    firstDisplayedScrollingRowIndex) - 1;
            }
            else
            {
                jumpRows = DisplayedBandsInfo.NumTotallyDisplayedFrozenRows;
            }
        }

        jumpRows += DisplayedBandsInfo.NumTotallyDisplayedScrollingRows;

        nextScreenVisibleRowIndexTmp = nextScreenVisibleRowIndex;
        Debug.Assert(nextScreenVisibleRowIndexTmp != -1);
        if (jumpRows == 0)
        {
            jumpRows = 1;
        }

        while (jumpRows > 0 && nextScreenVisibleRowIndexTmp != -1)
        {
            nextScreenVisibleRowIndexTmp = Rows.GetNextRow(nextScreenVisibleRowIndex, DataGridViewElementStates.Visible);
            if (nextScreenVisibleRowIndexTmp != -1)
            {
                nextScreenVisibleRowIndex = nextScreenVisibleRowIndexTmp;
                jumpRows--;
            }
        }

        _noSelectionChangeCount++;
        try
        {
            if (_ptCurrentCell.X == -1)
            {
                ClearSelection();
                switch (SelectionMode)
                {
                    case DataGridViewSelectionMode.CellSelect:
                    case DataGridViewSelectionMode.ColumnHeaderSelect:
                    case DataGridViewSelectionMode.RowHeaderSelect:
                        SetSelectedCellCore(firstVisibleColumnIndex, nextScreenVisibleRowIndex, true);
                        break;
                    case DataGridViewSelectionMode.FullRowSelect:
                        SetSelectedRowCore(nextScreenVisibleRowIndex, true);
                        break;
                    case DataGridViewSelectionMode.FullColumnSelect:
                        SetSelectedColumnCore(firstVisibleColumnIndex, true);
                        break;
                }

                success = ScrollIntoView(firstVisibleColumnIndex, nextScreenVisibleRowIndex, false);
                Debug.Assert(success);
                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, nextScreenVisibleRowIndex))
                {
                    return true;
                }

                SetCurrentCellAddressCore(firstVisibleColumnIndex, nextScreenVisibleRowIndex, true, false, false);
                return true;
            }

            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if (!ScrollIntoView(_ptCurrentCell.X, nextScreenVisibleRowIndex, true))
                    {
                        return true;
                    }

                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex))
                    {
                        return true;
                    }

                    if ((keyData & Keys.Shift) == Keys.Shift && MultiSelect)
                    {
                        Debug.Assert(_ptAnchorCell.Y >= 0);
                        int oldEdgeColumnIndex = _ptCurrentCell.X;
                        int oldEdgeRowIndex = _ptCurrentCell.Y;
                        if (_ptAnchorCell.Y == -1)
                        {
                            return true;
                        }

                        UpdateSelectedCellsBlock(
                            _ptAnchorCell.X,
                            ref oldEdgeColumnIndex,
                            oldEdgeColumnIndex,
                            _ptAnchorCell.Y,
                            ref oldEdgeRowIndex,
                            nextScreenVisibleRowIndex);
                    }
                    else
                    {
                        ClearSelection();
                        SetSelectedCellCore(_ptCurrentCell.X, nextScreenVisibleRowIndex, true);
                    }

                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextScreenVisibleRowIndex, false, false, false);
                    Debug.Assert(success);
                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if (!ScrollIntoView(_ptCurrentCell.X, nextScreenVisibleRowIndex, true))
                    {
                        return true;
                    }

                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex))
                    {
                        return true;
                    }

                    ClearSelection();
                    if ((keyData & Keys.Shift) == Keys.Shift && MultiSelect)
                    {
                        Debug.Assert(_ptAnchorCell.Y >= 0);
                        if (_ptAnchorCell.Y == -1)
                        {
                            return true;
                        }

                        if (_ptAnchorCell.Y < nextScreenVisibleRowIndex)
                        {
                            SelectRowRange(_ptAnchorCell.Y, nextScreenVisibleRowIndex, true);
                        }
                        else
                        {
                            SelectRowRange(nextScreenVisibleRowIndex, _ptAnchorCell.Y, true);
                        }
                    }
                    else
                    {
                        SetSelectedRowCore(nextScreenVisibleRowIndex, true);
                    }

                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextScreenVisibleRowIndex, false, false, false);
                    return true;

                case DataGridViewSelectionMode.RowHeaderSelect:
                    if (!ScrollIntoView(_ptCurrentCell.X, nextScreenVisibleRowIndex, true))
                    {
                        return true;
                    }

                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex))
                    {
                        return true;
                    }

                    if ((keyData & Keys.Shift) == Keys.Shift && MultiSelect)
                    {
                        Debug.Assert(_ptAnchorCell.Y >= 0);
                        if (_ptAnchorCell.Y == -1)
                        {
                            return true;
                        }

                        if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                        {
                            ClearSelection();
                            if (_ptAnchorCell.Y < nextScreenVisibleRowIndex)
                            {
                                SelectRowRange(_ptAnchorCell.Y, nextScreenVisibleRowIndex, true);
                            }
                            else
                            {
                                SelectRowRange(nextScreenVisibleRowIndex, _ptAnchorCell.Y, true);
                            }
                        }
                        else
                        {
                            int oldEdgeColumnIndex = _ptCurrentCell.X;
                            int oldEdgeRowIndex = _ptCurrentCell.Y;
                            UpdateSelectedCellsBlock(
                                _ptAnchorCell.X,
                                ref oldEdgeColumnIndex,
                                oldEdgeColumnIndex,
                                _ptAnchorCell.Y,
                                ref oldEdgeRowIndex,
                                nextScreenVisibleRowIndex);
                        }
                    }
                    else
                    {
                        if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                        {
                            ClearSelection();
                            SetSelectedRowCore(nextScreenVisibleRowIndex, true);
                        }
                        else
                        {
                            ClearSelection();
                            SetSelectedCellCore(_ptCurrentCell.X, nextScreenVisibleRowIndex, true);
                        }
                    }

                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextScreenVisibleRowIndex, !MultiSelect, false, false);
                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if (!ScrollIntoView(_ptCurrentCell.X, nextScreenVisibleRowIndex, true))
                    {
                        return true;
                    }

                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(nextScreenVisibleRowIndex))
                    {
                        return true;
                    }

                    success = SetCurrentCellAddressCore(_ptCurrentCell.X, nextScreenVisibleRowIndex, true, false, false);
                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    protected bool ProcessPriorKey(Keys keyData)
    {
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        if (firstVisibleColumnIndex == -1)
        {
            return false;
        }

        int previousScreenVisibleRowIndexTmp;
        int previousScreenVisibleRowIndex;

        if (_ptCurrentCell.Y == -1)
        {
            previousScreenVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
            if (previousScreenVisibleRowIndex == -1)
            {
                return false;
            }
        }
        else
        {
            previousScreenVisibleRowIndex = _ptCurrentCell.Y;
        }

        int jumpRows = (Rows.GetRowState(previousScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0
            ? DisplayedBandsInfo.NumTotallyDisplayedFrozenRows
            : DisplayedBandsInfo.NumTotallyDisplayedScrollingRows;

        if (jumpRows == 0)
        {
            jumpRows = 1;
        }

        previousScreenVisibleRowIndexTmp = previousScreenVisibleRowIndex;
        Debug.Assert(previousScreenVisibleRowIndexTmp != -1);
        while (jumpRows > 0 && previousScreenVisibleRowIndexTmp != -1)
        {
            previousScreenVisibleRowIndexTmp = Rows.GetPreviousRow(previousScreenVisibleRowIndex, DataGridViewElementStates.Visible);
            if (previousScreenVisibleRowIndexTmp != -1)
            {
                previousScreenVisibleRowIndex = previousScreenVisibleRowIndexTmp;
            }

            jumpRows--;
        }

        if ((Rows.GetRowState(previousScreenVisibleRowIndex) & DataGridViewElementStates.Frozen) != 0)
        {
            // Make sure the first scrolling row is visible if any
            int firstDisplayedScrollingRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
            if (firstDisplayedScrollingRowIndex != -1 && !ScrollIntoView(
                _ptCurrentCell.X == -1 ? firstVisibleColumnIndex : _ptCurrentCell.X,
                firstDisplayedScrollingRowIndex,
                forCurrentCellChange: true))
            {
                return true;
            }

            // Also, first visible frozen row should become current one - there is no reason to jump to another frozen row.
            previousScreenVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        }

        Debug.Assert(previousScreenVisibleRowIndex != -1);

        _noSelectionChangeCount++;
        try
        {
            if (_ptCurrentCell.X == -1)
            {
                ClearSelection();
                switch (SelectionMode)
                {
                    case DataGridViewSelectionMode.CellSelect:
                    case DataGridViewSelectionMode.ColumnHeaderSelect:
                    case DataGridViewSelectionMode.RowHeaderSelect:
                        SetSelectedCellCore(firstVisibleColumnIndex, previousScreenVisibleRowIndex, true);
                        break;
                    case DataGridViewSelectionMode.FullRowSelect:
                        SetSelectedRowCore(previousScreenVisibleRowIndex, true);
                        break;
                    case DataGridViewSelectionMode.FullColumnSelect:
                        SetSelectedColumnCore(firstVisibleColumnIndex, true);
                        break;
                }

                ScrollIntoView(firstVisibleColumnIndex, previousScreenVisibleRowIndex, false);
                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, previousScreenVisibleRowIndex))
                {
                    return true;
                }

                SetCurrentCellAddressCore(firstVisibleColumnIndex, previousScreenVisibleRowIndex, true, false, false);
                return true;
            }

            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if (!ScrollIntoView(_ptCurrentCell.X, previousScreenVisibleRowIndex, true))
                    {
                        return true;
                    }

                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex))
                    {
                        return true;
                    }

                    if ((keyData & Keys.Shift) == Keys.Shift && MultiSelect)
                    {
                        Debug.Assert(_ptAnchorCell.Y >= 0);
                        int oldEdgeColumnIndex = _ptCurrentCell.X;
                        int oldEdgeRowIndex = _ptCurrentCell.Y;
                        if (_ptAnchorCell.X == -1)
                        {
                            return true;
                        }

                        UpdateSelectedCellsBlock(
                            _ptAnchorCell.X,
                            ref oldEdgeColumnIndex,
                            oldEdgeColumnIndex,
                            _ptAnchorCell.Y,
                            ref oldEdgeRowIndex,
                            previousScreenVisibleRowIndex);
                    }
                    else
                    {
                        ClearSelection();
                        SetSelectedCellCore(_ptCurrentCell.X, previousScreenVisibleRowIndex, true);
                    }

                    SetCurrentCellAddressCore(_ptCurrentCell.X, previousScreenVisibleRowIndex, false, false, false);
                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if (!ScrollIntoView(_ptCurrentCell.X, previousScreenVisibleRowIndex, true))
                    {
                        return true;
                    }

                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex))
                    {
                        return true;
                    }

                    ClearSelection();
                    if ((keyData & Keys.Shift) == Keys.Shift && MultiSelect)
                    {
                        Debug.Assert(_ptAnchorCell.Y >= 0);
                        if (_ptAnchorCell.Y == -1)
                        {
                            return true;
                        }

                        if (_ptAnchorCell.Y < previousScreenVisibleRowIndex)
                        {
                            SelectRowRange(_ptAnchorCell.Y, previousScreenVisibleRowIndex, true);
                        }
                        else
                        {
                            SelectRowRange(previousScreenVisibleRowIndex, _ptAnchorCell.Y, true);
                        }
                    }
                    else
                    {
                        SetSelectedRowCore(previousScreenVisibleRowIndex, true);
                    }

                    SetCurrentCellAddressCore(_ptCurrentCell.X, previousScreenVisibleRowIndex, false, false, false);
                    return true;

                case DataGridViewSelectionMode.RowHeaderSelect:
                    if (!ScrollIntoView(_ptCurrentCell.X, previousScreenVisibleRowIndex, true))
                    {
                        return true;
                    }

                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex))
                    {
                        return true;
                    }

                    if ((keyData & Keys.Shift) == Keys.Shift && MultiSelect)
                    {
                        Debug.Assert(_ptAnchorCell.Y >= 0);
                        if (_ptAnchorCell.Y == -1)
                        {
                            return true;
                        }

                        if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                        {
                            ClearSelection();
                            if (_ptAnchorCell.Y < previousScreenVisibleRowIndex)
                            {
                                SelectRowRange(_ptAnchorCell.Y, previousScreenVisibleRowIndex, true);
                            }
                            else
                            {
                                SelectRowRange(previousScreenVisibleRowIndex, _ptAnchorCell.Y, true);
                            }
                        }
                        else
                        {
                            int oldEdgeColumnIndex = _ptCurrentCell.X;
                            int oldEdgeRowIndex = _ptCurrentCell.Y;
                            UpdateSelectedCellsBlock(
                                _ptAnchorCell.X,
                                ref oldEdgeColumnIndex,
                                oldEdgeColumnIndex,
                                _ptAnchorCell.Y,
                                ref oldEdgeRowIndex,
                                previousScreenVisibleRowIndex);
                        }
                    }
                    else
                    {
                        if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                        {
                            ClearSelection();
                            SetSelectedRowCore(previousScreenVisibleRowIndex, true);
                        }
                        else
                        {
                            ClearSelection();
                            SetSelectedCellCore(_ptCurrentCell.X, previousScreenVisibleRowIndex, true);
                        }
                    }

                    SetCurrentCellAddressCore(_ptCurrentCell.X, previousScreenVisibleRowIndex, !MultiSelect, false, false);
                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if (!ScrollIntoView(_ptCurrentCell.X, previousScreenVisibleRowIndex, true))
                    {
                        return true;
                    }

                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousScreenVisibleRowIndex))
                    {
                        return true;
                    }

                    SetCurrentCellAddressCore(_ptCurrentCell.X, previousScreenVisibleRowIndex, true, false, false);
                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    protected bool ProcessRightKey(Keys keyData) => RightToLeftInternal
        ? ProcessLeftKeyPrivate(keyData)
        : ProcessRightKeyPrivate(keyData);

    private bool ProcessRightKeyPrivate(Keys keyData)
    {
        bool? resizeResult = ProcessColumnResize(keyData, _keyboardResizeStep);
        if (resizeResult.HasValue)
        {
            return resizeResult.Value;
        }

        bool success;
        DataGridViewColumn? dataGridViewColumn = Columns.GetLastColumn(
            DataGridViewElementStates.Visible,
            DataGridViewElementStates.None);
        int lastVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
        if (lastVisibleColumnIndex == -1 || firstVisibleRowIndex == -1)
        {
            return false;
        }

        int nextVisibleColumnIndex = -1;
        if (_ptCurrentCell.X != -1)
        {
            dataGridViewColumn = Columns.GetNextColumn(Columns[_ptCurrentCell.X],
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
            if (dataGridViewColumn is not null)
            {
                nextVisibleColumnIndex = dataGridViewColumn.Index;
            }
        }

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true);
                                success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (nextVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(nextVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex))
                                {
                                    return true;
                                }

                                if (MultiSelect)
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return true;
                                    }

                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        nextVisibleColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        oldEdgeRowIndex);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(nextVisibleColumnIndex, _ptCurrentCell.Y, true);
                                }

                                success = SetCurrentCellAddressCore(nextVisibleColumnIndex, _ptCurrentCell.Y, !MultiSelect, false, false);
                                Debug.Assert(success);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true);
                                success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (nextVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(nextVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedCellCore(nextVisibleColumnIndex, _ptCurrentCell.Y, true);
                                success = SetCurrentCellAddressCore(nextVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedColumnCore(lastVisibleColumnIndex, true);
                                success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (nextVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(nextVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                if (MultiSelect)
                                {
                                    if (_ptAnchorCell.X == -1)
                                    {
                                        return true;
                                    }

                                    if (Columns.DisplayInOrder(_ptAnchorCell.X, nextVisibleColumnIndex))
                                    {
                                        SelectColumnRange(_ptAnchorCell.X, nextVisibleColumnIndex, true);
                                    }
                                    else
                                    {
                                        SelectColumnRange(nextVisibleColumnIndex, _ptAnchorCell.X, true);
                                    }
                                }
                                else
                                {
                                    SetSelectedColumnCore(nextVisibleColumnIndex, true);
                                }

                                success = SetCurrentCellAddressCore(nextVisibleColumnIndex, _ptCurrentCell.Y, !MultiSelect, false, false);
                                Debug.Assert(success);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedColumnCore(lastVisibleColumnIndex, true);
                                success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (nextVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(nextVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedColumnCore(nextVisibleColumnIndex, true);
                                success = SetCurrentCellAddressCore(nextVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true);
                                success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (nextVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(nextVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex))
                                {
                                    return true;
                                }

                                if (Columns[_ptCurrentCell.X].Selected)
                                {
                                    ClearSelection();
                                    if (MultiSelect)
                                    {
                                        if (_ptAnchorCell.X == -1)
                                        {
                                            return true;
                                        }

                                        if (Columns.DisplayInOrder(_ptAnchorCell.X, nextVisibleColumnIndex))
                                        {
                                            SelectColumnRange(_ptAnchorCell.X, nextVisibleColumnIndex, true);
                                        }
                                        else
                                        {
                                            SelectColumnRange(nextVisibleColumnIndex, _ptAnchorCell.X, true);
                                        }
                                    }
                                    else
                                    {
                                        SetSelectedColumnCore(nextVisibleColumnIndex, true);
                                    }
                                }
                                else
                                {
                                    if (MultiSelect)
                                    {
                                        if (_ptAnchorCell.X == -1)
                                        {
                                            return true;
                                        }

                                        int oldEdgeColumnIndex = _ptCurrentCell.X;
                                        int oldEdgeRowIndex = _ptCurrentCell.Y;
                                        UpdateSelectedCellsBlock(
                                            _ptAnchorCell.X,
                                            ref oldEdgeColumnIndex,
                                            nextVisibleColumnIndex,
                                            _ptAnchorCell.Y,
                                            ref oldEdgeRowIndex,
                                            oldEdgeRowIndex);
                                    }
                                    else
                                    {
                                        ClearSelection();
                                        SetSelectedCellCore(nextVisibleColumnIndex, _ptCurrentCell.Y, true);
                                    }
                                }

                                success = SetCurrentCellAddressCore(nextVisibleColumnIndex, _ptCurrentCell.Y, !MultiSelect, false, false);
                                Debug.Assert(success);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true);
                                success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                                Debug.Assert(success);
                                if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (nextVisibleColumnIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(nextVisibleColumnIndex, _ptCurrentCell.Y, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex))
                                {
                                    return true;
                                }

                                if (Columns[_ptCurrentCell.X].Selected)
                                {
                                    ClearSelection();
                                    SetSelectedColumnCore(nextVisibleColumnIndex, true);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(nextVisibleColumnIndex, _ptCurrentCell.Y, true);
                                }

                                success = SetCurrentCellAddressCore(nextVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        return ProcessRightMost((keyData & Keys.Shift) == Keys.Shift, lastVisibleColumnIndex, firstVisibleRowIndex);
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedRowCore(firstVisibleRowIndex, true);
                            success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (nextVisibleColumnIndex == -1)
                            {
                                return true;
                            }

                            if (!ScrollIntoView(nextVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(nextVisibleColumnIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(nextVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                            Debug.Assert(success);
                        }
                    }

                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    // Ctrl Right <==> End
    // Shift Ctrl Right <==> Shift End
    private bool ProcessRightMost(bool shift, int lastVisibleColumnIndex, int firstVisibleRowIndex)
    {
        bool success;

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    if (shift)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true);
                            success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(lastVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex))
                            {
                                return true;
                            }

                            if (MultiSelect)
                            {
                                Debug.Assert(_ptAnchorCell.X >= 0);
                                if (_ptAnchorCell.X == -1)
                                {
                                    return true;
                                }

                                int oldEdgeColumnIndex = _ptCurrentCell.X;
                                int oldEdgeRowIndex = _ptCurrentCell.Y;
                                UpdateSelectedCellsBlock(
                                    _ptAnchorCell.X,
                                    ref oldEdgeColumnIndex,
                                    lastVisibleColumnIndex,
                                    _ptAnchorCell.Y,
                                    ref oldEdgeRowIndex,
                                    oldEdgeRowIndex);
                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, false, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true);
                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true);
                            success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(lastVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex))
                            {
                                return true;
                            }

                            ClearSelection();
                            SetSelectedCellCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true);
                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                            Debug.Assert(success);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if (shift)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedColumnCore(lastVisibleColumnIndex, true);
                            success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(lastVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex))
                            {
                                return true;
                            }

                            if (MultiSelect)
                            {
                                if (_ptAnchorCell.X == -1)
                                {
                                    return true;
                                }

                                ClearSelection();
                                Debug.Assert(_ptAnchorCell.X >= 0);
                                SelectColumnRange(_ptAnchorCell.X, lastVisibleColumnIndex, true);
                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, false, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                SetSelectedColumnCore(_ptCurrentCell.X, false);
                                SetSelectedColumnCore(lastVisibleColumnIndex, true);
                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedColumnCore(lastVisibleColumnIndex, true);
                            success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(lastVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex))
                            {
                                return true;
                            }

                            ClearSelection();
                            SetSelectedColumnCore(lastVisibleColumnIndex, true);
                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                            Debug.Assert(success);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if (shift)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true);
                            success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(lastVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex))
                            {
                                return true;
                            }

                            if (MultiSelect)
                            {
                                Debug.Assert(_ptAnchorCell.X >= 0);
                                if (_ptAnchorCell.X == -1)
                                {
                                    return true;
                                }

                                if (Columns[_ptCurrentCell.X].Selected)
                                {
                                    ClearSelection();
                                    SelectColumnRange(_ptAnchorCell.X, lastVisibleColumnIndex, true);
                                }
                                else
                                {
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        lastVisibleColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        oldEdgeRowIndex);
                                }

                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, false, false, false);
                                Debug.Assert(success);
                            }
                            else
                            {
                                if (Columns[_ptCurrentCell.X].Selected)
                                {
                                    SetSelectedColumnCore(_ptCurrentCell.X, false);
                                    SetSelectedColumnCore(lastVisibleColumnIndex, true);
                                }
                                else
                                {
                                    SetSelectedCellCore(_ptCurrentCell.X, _ptCurrentCell.Y, false);
                                    SetSelectedCellCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true);
                                }

                                success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                                Debug.Assert(success);
                            }
                        }
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedCellCore(lastVisibleColumnIndex, firstVisibleRowIndex, true);
                            success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                            Debug.Assert(success);
                            if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            Debug.Assert(success);
                        }
                        else
                        {
                            if (!ScrollIntoView(lastVisibleColumnIndex, _ptCurrentCell.Y, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex))
                            {
                                return true;
                            }

                            if (Columns[_ptCurrentCell.X].Selected)
                            {
                                ClearSelection();
                                SetSelectedColumnCore(lastVisibleColumnIndex, true);
                            }
                            else
                            {
                                ClearSelection();
                                SetSelectedCellCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true);
                            }

                            success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                            Debug.Assert(success);
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if (_ptCurrentCell.X == -1)
                    {
                        ClearSelection();
                        SetSelectedRowCore(firstVisibleRowIndex, true);
                        success = ScrollIntoView(lastVisibleColumnIndex, firstVisibleRowIndex, false);
                        Debug.Assert(success);
                        if (IsInnerCellOutOfBounds(lastVisibleColumnIndex, firstVisibleRowIndex))
                        {
                            return true;
                        }

                        success = SetCurrentCellAddressCore(lastVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                        Debug.Assert(success);
                    }
                    else
                    {
                        if (!ScrollIntoView(lastVisibleColumnIndex, _ptCurrentCell.Y, true))
                        {
                            return true;
                        }

                        if (_ptCurrentCell.Y == -1 || IsColumnOutOfBounds(lastVisibleColumnIndex))
                        {
                            return true;
                        }

                        success = SetCurrentCellAddressCore(lastVisibleColumnIndex, _ptCurrentCell.Y, true, false, false);
                        Debug.Assert(success);
                    }

                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    protected bool ProcessSpaceKey(Keys keyData)
    {
        if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && _ptCurrentCell.X != -1)
        {
            _noSelectionChangeCount++;
            bool switchedToBulkPaint = false;
            if (_selectedBandIndexes.Count > BulkPaintThreshold)
            {
                _inBulkPaintCount++;
                switchedToBulkPaint = true;
            }

            try
            {
                if (SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)
                {
                    // Same as clicking the column header cell
                    Debug.Assert(MultiSelect || _selectedBandIndexes.Count <= 1);
                    int bandIndex = 0;
                    while (bandIndex < _selectedBandIndexes.Count)
                    {
                        if (_selectedBandIndexes[bandIndex] != _ptCurrentCell.X)
                        {
                            // deselect currently selected column
                            SetSelectedColumnCore(_selectedBandIndexes[bandIndex], false);
                        }
                        else
                        {
                            bandIndex++;
                        }
                    }

                    RemoveIndividuallySelectedCells();
                    if (!Columns[_ptCurrentCell.X].Selected)
                    {
                        Debug.Assert(!_selectedBandIndexes.Contains(_ptCurrentCell.X));
                        SetSelectedColumnCore(_ptCurrentCell.X, true);
                    }

                    return true;
                }
                else if (SelectionMode == DataGridViewSelectionMode.RowHeaderSelect)
                {
                    // Same as clicking the row header cell
                    Debug.Assert(MultiSelect || _selectedBandIndexes.Count <= 1);
                    int bandIndex = 0;
                    while (bandIndex < _selectedBandIndexes.Count)
                    {
                        if (_selectedBandIndexes[bandIndex] != _ptCurrentCell.Y)
                        {
                            // deselect currently selected row
                            SetSelectedRowCore(_selectedBandIndexes[bandIndex], false);
                        }
                        else
                        {
                            bandIndex++;
                        }
                    }

                    RemoveIndividuallySelectedCells();
                    if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) == 0)
                    {
                        Debug.Assert(!_selectedBandIndexes.Contains(_ptCurrentCell.Y));
                        SetSelectedRowCore(_ptCurrentCell.Y, true);
                    }

                    return true;
                }
            }
            finally
            {
                NoSelectionChangeCount--;
                if (switchedToBulkPaint)
                {
                    ExitBulkPaint(-1, -1);
                }
            }
        }

        return false;
    }

    /// <summary>
    ///  Gets a value indicating whether the Tab key should be processed.
    /// </summary>
    protected bool ProcessTabKey(Keys keyData)
    {
        if (StandardTab)
        {
            if ((keyData & Keys.Control) == Keys.Control)
            {
                if ((keyData & Keys.Shift) == Keys.Shift)
                {
                    if (!VisibleCellExists || CurrentCellIsFirstVisibleCell)
                    {
                        // Goto previous control
                        return false;
                    }
                    else
                    {
                        // Goto previous cell
                        return TabToPreviousCell();
                    }
                }
                else
                {
                    if (!VisibleCellExists || CurrentCellIsLastVisibleCell)
                    {
                        // Goto next control
                        return false;
                    }
                    else
                    {
                        // Goto next cell
                        return TabToNextCell();
                    }
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            if ((keyData & Keys.Control) == Keys.Control)
            {
                return false;
            }
            else
            {
                if ((keyData & Keys.Shift) == Keys.Shift)
                {
                    if (!VisibleCellExists || CurrentCellIsFirstVisibleCell)
                    {
                        // Goto previous control
                        return false;
                    }
                    else
                    {
                        // Goto previous cell
                        return TabToPreviousCell();
                    }
                }
                else
                {
                    if (!VisibleCellExists || CurrentCellIsLastVisibleCell)
                    {
                        // Goto next control
                        return false;
                    }
                    else
                    {
                        // Goto next cell
                        return TabToNextCell();
                    }
                }
            }
        }
    }

    /// <summary>
    ///  Processes keys for dataGridView navigation.
    /// </summary>
    protected virtual bool ProcessDataGridViewKey(KeyEventArgs e)
    {
        switch (e.KeyCode)
        {
            case Keys.Tab:
                {
                    return ProcessTabKey(e.KeyData);
                }

            case Keys.Up:
                {
                    return ProcessUpKey(e.KeyData);
                }

            case Keys.Down:
                {
                    return ProcessDownKey(e.KeyData);
                }

            case Keys.Next:
                {
                    return ProcessNextKey(e.KeyData);
                }

            case Keys.Prior:
                {
                    return ProcessPriorKey(e.KeyData);
                }

            case Keys.Left:
                {
                    return ProcessLeftKey(e.KeyData);
                }

            case Keys.Right:
                {
                    return ProcessRightKey(e.KeyData);
                }

            case Keys.F2:
                {
                    return ProcessF2Key(e.KeyData);
                }

            case Keys.F3:
                {
                    return ProcessF3Key(e.KeyData);
                }

            case Keys.F10:
                {
                    if (e.Control && e.Shift)
                    {
                        return ProcessControlShiftF10Keys(e.KeyData);
                    }

                    break;
                }

            case Keys.Home:
                {
                    return ProcessHomeKey(e.KeyData);
                }

            case Keys.D0:
            case Keys.NumPad0:
                {
                    return ProcessZeroKey(e.KeyData);
                }

            case Keys.Delete:
                {
                    return ProcessDeleteKey(e.KeyData);
                }

            case Keys.End:
                {
                    return ProcessEndKey(e.KeyData);
                }

            case Keys.Enter:
                {
                    return ProcessEnterKey(e.KeyData);
                }

            case Keys.Escape:
                {
                    return ProcessEscapeKey(e.KeyData);
                }

            case Keys.A:
                {
                    return ProcessAKey(e.KeyData);
                }

            case Keys.C:
            case Keys.Insert:
                {
                    return ProcessInsertKey(e.KeyData);
                }

            case Keys.Space:
                {
                    return ProcessSpaceKey(e.KeyData);
                }
        }

        return false;
    }

    protected bool ProcessUpKey(Keys keyData)
    {
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
        if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1)
        {
            return false;
        }

        int previousVisibleRowIndex = -1;
        if (_ptCurrentCell.Y != -1)
        {
            previousVisibleRowIndex = Rows.GetPreviousRow(_ptCurrentCell.Y, DataGridViewElementStates.Visible);
        }

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (MultiSelect)
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptAnchorCell.X == -1 || _ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                    {
                                        return true;
                                    }

                                    Debug.Assert(_ptAnchorCell.Y >= 0);
                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        oldEdgeColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        firstVisibleRowIndex);
                                    SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, false, false, false);
                                }
                                else
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                    {
                                        return true;
                                    }

                                    ClearSelection();
                                    SetSelectedCellCore(_ptCurrentCell.X, firstVisibleRowIndex, true);
                                    SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, true, false, false);
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedCellCore(_ptCurrentCell.X, firstVisibleRowIndex, true);
                                SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, true, false, false);
                            }
                        }
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (previousVisibleRowIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, previousVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex))
                                {
                                    return true;
                                }

                                if (MultiSelect)
                                {
                                    Debug.Assert(_ptAnchorCell.Y >= 0);
                                    if (_ptAnchorCell.Y == -1)
                                    {
                                        return true;
                                    }

                                    int oldEdgeColumnIndex = _ptCurrentCell.X;
                                    int oldEdgeRowIndex = _ptCurrentCell.Y;
                                    UpdateSelectedCellsBlock(
                                        _ptAnchorCell.X,
                                        ref oldEdgeColumnIndex,
                                        oldEdgeColumnIndex,
                                        _ptAnchorCell.Y,
                                        ref oldEdgeRowIndex,
                                        previousVisibleRowIndex);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(_ptCurrentCell.X, previousVisibleRowIndex, true);
                                }

                                SetCurrentCellAddressCore(_ptCurrentCell.X, previousVisibleRowIndex, !MultiSelect, false, false);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (previousVisibleRowIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, previousVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedCellCore(_ptCurrentCell.X, previousVisibleRowIndex, selected: true);
                                SetCurrentCellAddressCore(
                                    _ptCurrentCell.X,
                                    previousVisibleRowIndex,
                                    setAnchorCellAddress: true,
                                    validateCurrentCell: false,
                                    throughMouseClick: false);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullRowSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedRowCore(firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (MultiSelect)
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptAnchorCell.X == -1 || _ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                    {
                                        return true;
                                    }

                                    ClearSelection();
                                    Debug.Assert(_ptAnchorCell.Y >= 0);
                                    SelectRowRange(firstVisibleRowIndex, _ptAnchorCell.Y, true);
                                    SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, false, false, false);
                                }
                                else
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                    {
                                        return true;
                                    }

                                    SetSelectedRowCore(_ptCurrentCell.Y, false);
                                    SetSelectedRowCore(firstVisibleRowIndex, true);
                                    SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, true, false, false);
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedRowCore(firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedRowCore(firstVisibleRowIndex, true);
                                SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, true, false, false);
                            }
                        }
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedRowCore(firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (previousVisibleRowIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, previousVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                if (MultiSelect)
                                {
                                    if (_ptAnchorCell.Y == -1)
                                    {
                                        return true;
                                    }

                                    if (_ptAnchorCell.Y >= previousVisibleRowIndex)
                                    {
                                        SelectRowRange(previousVisibleRowIndex, _ptAnchorCell.Y, true);
                                    }
                                    else
                                    {
                                        SelectRowRange(_ptAnchorCell.Y, previousVisibleRowIndex, true);
                                    }
                                }
                                else
                                {
                                    SetSelectedRowCore(previousVisibleRowIndex, true);
                                }

                                SetCurrentCellAddressCore(_ptCurrentCell.X, previousVisibleRowIndex, !MultiSelect, false, false);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedRowCore(firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (previousVisibleRowIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, previousVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex))
                                {
                                    return true;
                                }

                                ClearSelection();
                                SetSelectedRowCore(previousVisibleRowIndex, true);
                                SetCurrentCellAddressCore(_ptCurrentCell.X, previousVisibleRowIndex, true, false, false);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.RowHeaderSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (MultiSelect)
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptAnchorCell.X == -1 || _ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                    {
                                        return true;
                                    }

                                    Debug.Assert(_ptAnchorCell.Y >= 0);
                                    if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                    {
                                        ClearSelection();
                                        SelectRowRange(firstVisibleRowIndex, _ptAnchorCell.Y, true);
                                    }
                                    else
                                    {
                                        int oldEdgeColumnIndex = _ptCurrentCell.X;
                                        int oldEdgeRowIndex = _ptCurrentCell.Y;
                                        UpdateSelectedCellsBlock(
                                            _ptAnchorCell.X,
                                            ref oldEdgeColumnIndex,
                                            oldEdgeColumnIndex,
                                            _ptAnchorCell.Y,
                                            ref oldEdgeRowIndex,
                                            firstVisibleRowIndex);
                                    }

                                    SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, false, false, false);
                                }
                                else
                                {
                                    if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                    {
                                        return true;
                                    }

                                    if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                    {
                                        return true;
                                    }

                                    if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                    {
                                        SetSelectedRowCore(_ptCurrentCell.Y, false);
                                        SetSelectedRowCore(firstVisibleRowIndex, true);
                                    }
                                    else
                                    {
                                        SetSelectedCellCore(_ptCurrentCell.X, _ptCurrentCell.Y, false);
                                        SetSelectedCellCore(_ptCurrentCell.X, firstVisibleRowIndex, true);
                                    }

                                    SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, true, false, false);
                                }
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    SetSelectedRowCore(firstVisibleRowIndex, true);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(_ptCurrentCell.X, firstVisibleRowIndex, true);
                                }

                                SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, true, false, false);
                            }
                        }
                    }
                    else
                    {
                        if ((keyData & Keys.Shift) == Keys.Shift)
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (previousVisibleRowIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, previousVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex))
                                {
                                    return true;
                                }

                                if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    if (MultiSelect)
                                    {
                                        if (_ptAnchorCell.Y == -1)
                                        {
                                            return true;
                                        }

                                        if (_ptAnchorCell.Y >= previousVisibleRowIndex)
                                        {
                                            SelectRowRange(previousVisibleRowIndex, _ptAnchorCell.Y, true);
                                        }
                                        else
                                        {
                                            SelectRowRange(_ptAnchorCell.Y, previousVisibleRowIndex, true);
                                        }
                                    }

                                    SetSelectedRowCore(previousVisibleRowIndex, true);
                                }
                                else
                                {
                                    if (MultiSelect)
                                    {
                                        int oldEdgeColumnIndex = _ptCurrentCell.X;
                                        int oldEdgeRowIndex = _ptCurrentCell.Y;
                                        if (_ptAnchorCell.Y == -1)
                                        {
                                            return true;
                                        }

                                        UpdateSelectedCellsBlock(
                                            _ptAnchorCell.X,
                                            ref oldEdgeColumnIndex,
                                            oldEdgeColumnIndex,
                                            _ptAnchorCell.Y,
                                            ref oldEdgeRowIndex,
                                            previousVisibleRowIndex);
                                    }
                                    else
                                    {
                                        ClearSelection();
                                        SetSelectedCellCore(_ptCurrentCell.X, previousVisibleRowIndex, true);
                                    }
                                }

                                SetCurrentCellAddressCore(_ptCurrentCell.X, previousVisibleRowIndex, !MultiSelect, false, false);
                            }
                        }
                        else
                        {
                            if (_ptCurrentCell.X == -1)
                            {
                                ClearSelection();
                                SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                                ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                                if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                                {
                                    return true;
                                }

                                SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                            }
                            else
                            {
                                if (previousVisibleRowIndex == -1)
                                {
                                    return true;
                                }

                                if (!ScrollIntoView(_ptCurrentCell.X, previousVisibleRowIndex, true))
                                {
                                    return true;
                                }

                                if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex))
                                {
                                    return true;
                                }

                                if ((Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Selected) != 0)
                                {
                                    ClearSelection();
                                    SetSelectedRowCore(previousVisibleRowIndex, true);
                                }
                                else
                                {
                                    ClearSelection();
                                    SetSelectedCellCore(_ptCurrentCell.X, previousVisibleRowIndex, true);
                                }

                                SetCurrentCellAddressCore(_ptCurrentCell.X, previousVisibleRowIndex, true, false, false);
                            }
                        }
                    }

                    return true;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if ((keyData & Keys.Control) == Keys.Control)
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedColumnCore(firstVisibleColumnIndex, selected: true);
                            ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, forCurrentCellChange: false);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            SetCurrentCellAddressCore(
                                firstVisibleColumnIndex,
                                firstVisibleRowIndex,
                                setAnchorCellAddress: true,
                                validateCurrentCell: false,
                                throughMouseClick: false);
                        }
                        else
                        {
                            if (!ScrollIntoView(_ptCurrentCell.X, firstVisibleRowIndex, forCurrentCellChange: true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(firstVisibleRowIndex))
                            {
                                return true;
                            }

                            SetCurrentCellAddressCore(_ptCurrentCell.X, firstVisibleRowIndex, true, false, false);
                        }
                    }
                    else
                    {
                        if (_ptCurrentCell.X == -1)
                        {
                            ClearSelection();
                            SetSelectedColumnCore(firstVisibleColumnIndex, true);
                            ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
                            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                            {
                                return true;
                            }

                            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
                        }
                        else
                        {
                            if (previousVisibleRowIndex == -1)
                            {
                                return true;
                            }

                            if (!ScrollIntoView(_ptCurrentCell.X, previousVisibleRowIndex, true))
                            {
                                return true;
                            }

                            if (_ptCurrentCell.X == -1 || IsRowOutOfBounds(previousVisibleRowIndex))
                            {
                                return true;
                            }

                            SetCurrentCellAddressCore(_ptCurrentCell.X, previousVisibleRowIndex, true, false, false);
                        }
                    }

                    return true;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        return true;
    }

    protected bool ProcessZeroKey(Keys keyData)
    {
        if (_ptCurrentCell.X != -1 && !IsCurrentCellInEditMode && ColumnEditable(_ptCurrentCell.X))
        {
            DataGridViewCell dataGridViewCell = CurrentCellInternal;
            ThrowInvalidOperationExceptionIfNull(dataGridViewCell);

            if (!IsSharedCellReadOnly(dataGridViewCell, _ptCurrentCell.Y)
                && (EditMode == DataGridViewEditMode.EditOnKeystroke || EditMode == DataGridViewEditMode.EditOnKeystrokeOrF2)
                && dataGridViewCell.EditType is not null)
            {
                bool success = ScrollIntoView(_ptCurrentCell.X, _ptCurrentCell.Y, false);
                Debug.Assert(success);
                if (!BeginEditInternal(selectAll: false))
                {
                    return false;
                }
            }
        }

        if ((keyData & (Keys.Alt | Keys.Shift | Keys.Control)) == Keys.Control && IsCurrentCellInEditMode)
        {
            DataGridViewCell dataGridViewCurrentCell = CurrentCellInternal;
            Debug.Assert(dataGridViewCurrentCell is not null);
            object? nullValue = dataGridViewCurrentCell.GetInheritedStyle(inheritedCellStyle: null, _ptCurrentCell.Y, includeColors: false).NullValue;
            if (nullValue is null
                || (dataGridViewCurrentCell.FormattedValueType is not null && dataGridViewCurrentCell.FormattedValueType.IsAssignableFrom(nullValue.GetType())))
            {
                if (EditingControl is IDataGridViewEditingControl dataGridViewEditingControl)
                {
                    dataGridViewEditingControl.EditingControlFormattedValue = nullValue;
                    dataGridViewEditingControl.EditingControlValueChanged = true;
                    dataGridViewEditingControl.PrepareEditingControlForEdit(selectAll: true);
                }
                else
                {
                    Debug.Assert(_dataGridViewState1[State1_CurrentCellInEditMode]);
                    IDataGridViewEditingCell? dataGridViewEditingCell = dataGridViewCurrentCell as IDataGridViewEditingCell;
                    Debug.Assert(dataGridViewEditingCell is not null);
                    dataGridViewEditingCell.EditingCellFormattedValue = nullValue;
                    dataGridViewEditingCell.EditingCellValueChanged = true;
                    dataGridViewEditingCell.PrepareEditingCellForEdit(selectAll: true);
                    InvalidateCellPrivate(_ptCurrentCell.X, _ptCurrentCell.Y);
                }

                NotifyCurrentCellDirty(true);
                return true;
            }

            return false;
        }

        return false;
    }

    private void PushAllowUserToAddRows()
    {
        if (AllowUserToAddRowsInternal)
        {
            if (Columns.Count > 0 && NewRowIndex == -1)
            {
                AddNewRow(false);
            }
        }
        else if (NewRowIndex != -1)
        {
            // Delete the 'new' row
            Debug.Assert(NewRowIndex == Rows.Count - 1);
            Rows.RemoveAtInternal(NewRowIndex, force: false);
        }
    }

    private bool PushFormattedValue(ref DataGridViewCell dataGridViewCurrentCell, object? formattedValue, out Exception? exception)
    {
        ArgumentNullException.ThrowIfNull(dataGridViewCurrentCell);

        Debug.Assert(IsCurrentCellInEditMode);

        exception = null;

        DataGridViewCellStyle? cellStyle = InheritedEditingCellStyle;

        DataGridViewCellParsingEventArgs dgvcpe = OnCellParsing(
            _ptCurrentCell.Y,
            _ptCurrentCell.X,
            formattedValue,
            dataGridViewCurrentCell.ValueType,
            cellStyle);

        if (dgvcpe.ParsingApplied
            && dgvcpe.Value is not null
            && dataGridViewCurrentCell.ValueType is not null
            && dataGridViewCurrentCell.ValueType.IsAssignableFrom(dgvcpe.Value.GetType()))
        {
            if (dataGridViewCurrentCell.RowIndex == -1)
            {
                // un-sharing the row before pushing the new value
                dataGridViewCurrentCell = Rows[_ptCurrentCell.Y].Cells[_ptCurrentCell.X];
            }

            return dataGridViewCurrentCell.SetValueInternal(_ptCurrentCell.Y, dgvcpe.Value);
        }

        object? val;
        try
        {
            // We already check if cellStyle is null in ParseFormattedValueInternal.
            val = dataGridViewCurrentCell.ParseFormattedValue(
                formattedValue,
                dgvcpe.InheritedCellStyle!,
                formattedValueTypeConverter: null,
                valueTypeConverter: null);
        }
        catch (Exception e) when (!e.IsCriticalException())
        {
            exception = e;
            return false;
        }

        if (dataGridViewCurrentCell.RowIndex == -1)
        {
            // un-sharing the row before pushing the new value
            dataGridViewCurrentCell = Rows[_ptCurrentCell.Y].Cells[_ptCurrentCell.X];
        }

        return dataGridViewCurrentCell.SetValueInternal(_ptCurrentCell.Y, val);
    }

    private void RecordCellMouseClick(DataGridViewCellMouseEventArgs dgvcme)
    {
        Debug.Assert(dgvcme.Clicks == 1);
        _lastMouseClickInfo.Button = dgvcme.Button;
        _lastMouseClickInfo.TimeStamp = DateTime.Now.Ticks;
        _lastMouseClickInfo.X = dgvcme.X;
        _lastMouseClickInfo.Y = dgvcme.Y;
        _lastMouseClickInfo.Col = dgvcme.ColumnIndex;
        _lastMouseClickInfo.Row = dgvcme.RowIndex;
    }

    private void RefreshColumnsAndRows()
    {
        Rows.ClearInternal(recreateNewRow: false);
        RefreshColumns();
        RefreshRows(scrollIntoView: true);
    }

    private void RefreshColumns()
    {
        // if AutoGenerateColumns == true then:
        // 1. remove the columns which were previously bound
        // 2. add new columns based on data source

        bool startUpdateInternal = Visible;
        if (startUpdateInternal)
        {
            BeginUpdateInternal();
        }

        _dataGridViewOper[OperationInRefreshColumns] = true;
        try
        {
            DataGridViewColumnCollection dataGridViewCols = Columns;

            if (AutoGenerateColumns)
            {
                AutoGenerateDataBoundColumns(DataConnection?.GetCollectionOfBoundDataGridViewColumns());
            }
            else
            {
                for (int j = 0; j < dataGridViewCols.Count; j++)
                {
                    // when we refresh the columns, always clear whatever binding information we had on the columns
                    dataGridViewCols[j].IsDataBoundInternal = false;
                    dataGridViewCols[j].BoundColumnIndex = -1;
                    dataGridViewCols[j].BoundColumnConverter = null;
                    // set up the columns which have DataPropertyName set to something
                    if (DataSource is not null && dataGridViewCols[j].DataPropertyName.Length != 0)
                    {
                        MapDataGridViewColumnToDataBoundField(dataGridViewCols[j]);
                    }
                }
            }

            if (DataSource is not null)
            {
                // We already check inside DataSource if DataConnection is null.
                DataConnection!.ApplySortingInformationFromBackEnd();
            }
        }
        finally
        {
            _dataGridViewOper[OperationInRefreshColumns] = false;

            if (startUpdateInternal)
            {
                EndUpdateInternal(false);
                Invalidate(true);
            }
        }
    }

    // Returns true for success, and false if an OnDataError event was raised and cancelled the operation (e.Cancel = true).
    public bool RefreshEdit()
    {
        if (_ptCurrentCell.X != -1 && IsCurrentCellInEditMode)
        {
            Debug.Assert(_ptCurrentCell.Y != -1);
            DataGridViewCell dataGridViewCurrentCell = CurrentCellInternal;
            DataGridViewCellStyle dataGridViewCellStyle = dataGridViewCurrentCell.GetInheritedStyle(inheritedCellStyle: null, _ptCurrentCell.Y, includeColors: true);
            if (EditingControl is not null)
            {
                if (InitializeEditingControlValue(ref dataGridViewCellStyle, dataGridViewCurrentCell))
                {
                    if (((IDataGridViewEditingControl)EditingControl).RepositionEditingControlOnValueChange)
                    {
                        PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
                    }

                    ((IDataGridViewEditingControl)EditingControl).PrepareEditingControlForEdit(selectAll: true);
                    ((IDataGridViewEditingControl)EditingControl).EditingControlValueChanged = false;
                    IsCurrentCellDirtyInternal = false;
                    return true;
                }

                return false;
            }
            else
            {
                Debug.Assert(_dataGridViewState1[State1_CurrentCellInEditMode]);
                if (InitializeEditingCellValue(ref dataGridViewCellStyle, ref dataGridViewCurrentCell))
                {
                    IDataGridViewEditingCell? dataGridViewEditingCell = dataGridViewCurrentCell as IDataGridViewEditingCell;
                    Debug.Assert(dataGridViewEditingCell is not null);
                    dataGridViewEditingCell.PrepareEditingCellForEdit(selectAll: true);
                    dataGridViewEditingCell.EditingCellValueChanged = false;
                    IsCurrentCellDirtyInternal = false;
                    return true;
                }

                return false;
            }
        }

        return true;
    }

    private void RefreshRows(bool scrollIntoView)
    {
        bool startBeginUpdate = Visible;
        if (startBeginUpdate)
        {
            BeginUpdateInternal();
        }

        try
        {
            if (_dataGridViewOper[OperationInCurrentCellChange])
            {
                _dataGridViewState2[State2_RowsCollectionClearedInSetCell] = true;
            }

            // Clear existing rows
            Rows.ClearInternal(recreateNewRow: true);

            // Add a row for each object in the data source
            if (DataConnection is not null && Columns.Count > 0)
            {
                IList? list = DataConnection.List;
                if (list is not null && list.Count > 0)
                {
                    int rowsCount = list.Count;
                    bool oldDoNotChangePositionInTheCurrencyManager = DataConnection.DoNotChangePositionInTheCurrencyManager;

                    // scroll into view when we have a non-empty list
                    // and the layout is not dirty and we are not in a sort operation.
                    bool matchPositionInCurrencyManagerAfterRecreatingRows = !_layout._dirty && !InSortOperation;

                    if (matchPositionInCurrencyManagerAfterRecreatingRows)
                    {
                        // Prevent the data connection from changing position in the currency manager.
                        // But only if the data grid view will set the position in the currency manager after recreating the rows collection.
                        DataConnection.DoNotChangePositionInTheCurrencyManager = true;
                    }

                    try
                    {
                        Rows.AddInternal(RowTemplateClone);
                        Debug.Assert(list.Count == rowsCount);
                        if (rowsCount > 1)
                        {
                            Rows.AddCopiesInternal(0, rowsCount - 1);
                        }

                        // Add selected Columns back in selectedBandIndexes
                        foreach (DataGridViewColumn column in Columns)
                        {
                            if (column.Selected && (!_selectedBandIndexes.Contains(column.Index)))
                            {
                                _selectedBandIndexes.Add(column.Index);
                            }
                        }
                    }
                    finally
                    {
                        DataConnection.DoNotChangePositionInTheCurrencyManager = oldDoNotChangePositionInTheCurrencyManager;
                    }

                    if (matchPositionInCurrencyManagerAfterRecreatingRows)
                    {
                        DataConnection.MatchCurrencyManagerPosition(scrollIntoView, clearSelection: true);
                    }
                }
            }
        }
        finally
        {
            if (startBeginUpdate)
            {
                EndUpdateInternal(false);
                Invalidate(true);
            }
        }
    }

    private void ReleaseMouse()
    {
        Cursor.Clip = Rectangle.Empty;
        Capture = false;
    }

    internal override void ReleaseUiaProvider(HWND handle)
    {
        if (!IsAccessibilityObjectCreated)
        {
            return;
        }

        if (OsVersion.IsWindows8OrGreater())
        {
            foreach (DataGridViewRow row in Rows)
            {
                foreach (DataGridViewCell cell in row.Cells)
                {
                    cell.ReleaseUiaProvider();
                }

                row.HeaderCell.ReleaseUiaProvider();
                row.ReleaseUiaProvider();
            }

            foreach (DataGridViewColumn column in Columns)
            {
                column.HeaderCell.ReleaseUiaProvider();
            }

            _editingPanel?.ReleaseUiaProvider(HWND.Null);
            _editingPanelAccessibleObject = null;
            _topLeftHeaderCell?.ReleaseUiaProvider();

            if (AccessibilityObject is DataGridViewAccessibleObject accessibleObject)
            {
                accessibleObject.ReleaseChildUiaProviders();
            }
        }

        base.ReleaseUiaProvider(handle);
    }

    private void RemoveIndividualReadOnlyCellsInColumn(int columnIndex)
    {
        int cellIndex = 0;
        while (cellIndex < _individualReadOnlyCells.Count)
        {
            DataGridViewCell dataGridViewCell = _individualReadOnlyCells[cellIndex];
            if (dataGridViewCell.ColumnIndex == columnIndex)
            {
                SetReadOnlyCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false);
            }
            else
            {
                cellIndex++;
            }
        }
    }

    private void RemoveIndividualReadOnlyCellsInRow(int rowIndex)
    {
        int cellIndex = 0;
        while (cellIndex < _individualReadOnlyCells.Count)
        {
            DataGridViewCell dataGridViewCell = _individualReadOnlyCells[cellIndex];
            if (dataGridViewCell.RowIndex == rowIndex)
            {
                SetReadOnlyCellCore(dataGridViewCell.ColumnIndex, rowIndex, false);
            }
            else
            {
                cellIndex++;
            }
        }
    }

    private void RemoveIndividuallySelectedCells()
    {
        Debug.Assert(_noSelectionChangeCount > 0);
        bool switchedToBulkPaint = false;
        if (_individualSelectedCells.Count > BulkPaintThreshold)
        {
            _inBulkPaintCount++;
            switchedToBulkPaint = true;
        }

        try
        {
            while (_individualSelectedCells.Count > 0)
            {
                DataGridViewCell dataGridViewCell = _individualSelectedCells.HeadCell;
                SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false);
            }
        }
        finally
        {
            if (switchedToBulkPaint)
            {
                ExitBulkPaint(-1, -1);
            }
        }
    }

    private void RemoveIndividuallySelectedCells(int columnIndexException, int rowIndexException)
    {
        Debug.Assert(_noSelectionChangeCount > 0);
        bool switchedToBulkPaint = false;
        if (_individualSelectedCells.Count > BulkPaintThreshold)
        {
            _inBulkPaintCount++;
            switchedToBulkPaint = true;
        }

        try
        {
            while (_individualSelectedCells.Count > 0)
            {
                DataGridViewCell dataGridViewCell = _individualSelectedCells.HeadCell;
                if (dataGridViewCell.ColumnIndex != columnIndexException || dataGridViewCell.RowIndex != rowIndexException)
                {
                    SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false);
                }
                else
                {
                    while (_individualSelectedCells.Count > 1)
                    {
                        dataGridViewCell = _individualSelectedCells[1];
                        Debug.Assert(dataGridViewCell.ColumnIndex != columnIndexException || dataGridViewCell.RowIndex != rowIndexException);
                        SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false);
                    }

                    break;
                }
            }
        }
        finally
        {
            if (switchedToBulkPaint)
            {
                ExitBulkPaint(-1, -1);
            }
        }
    }

    private void RemoveIndividuallySelectedCellsInColumn(int columnIndex)
    {
        Debug.Assert(_noSelectionChangeCount > 0);
        int cellIndex = 0;
        int cellCountInColumn = 0;
        bool switchToBulkOperation = false;
        DataGridViewCell dataGridViewCell;

        while (cellIndex < _individualSelectedCells.Count)
        {
            dataGridViewCell = _individualSelectedCells[cellIndex];
            if (dataGridViewCell.ColumnIndex == columnIndex)
            {
                SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false);
                cellCountInColumn++;
                if (cellCountInColumn > BulkPaintThreshold)
                {
                    switchToBulkOperation = true;
                    break;
                }
            }
            else
            {
                cellIndex++;
            }
        }

        if (switchToBulkOperation)
        {
            _inBulkPaintCount++;
            try
            {
                while (cellIndex < _individualSelectedCells.Count)
                {
                    dataGridViewCell = _individualSelectedCells[cellIndex];
                    if (dataGridViewCell.ColumnIndex == columnIndex)
                    {
                        SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false);
                    }
                    else
                    {
                        cellIndex++;
                    }
                }
            }
            finally
            {
                ExitBulkPaint(columnIndex, -1);
            }
        }
    }

    private void RemoveIndividuallySelectedCellsInRow(int rowIndex)
    {
        Debug.Assert(_noSelectionChangeCount > 0);
        // Since there are typically not many columns in a row, we don't try to switch into a bulk operation
        // as we do in RemoveIndividuallySelectedCellsInColumn.
        int cellIndex = 0;
        while (cellIndex < _individualSelectedCells.Count)
        {
            DataGridViewCell dataGridViewCell = _individualSelectedCells[cellIndex];
            if (dataGridViewCell.RowIndex == rowIndex)
            {
                SetSelectedCellCore(dataGridViewCell.ColumnIndex, dataGridViewCell.RowIndex, false);
            }
            else
            {
                cellIndex++;
            }
        }
    }

    // Required by the Designer
    private void ResetBackgroundColor() => BackgroundColor = s_defaultBackgroundColor;

    // Required by the Designer
    private void ResetGridColor() => GridColor = DefaultGridColor;

    [EditorBrowsable(EditorBrowsableState.Never)]
    public override void ResetText() => base.ResetText();

    /// <summary>
    ///  Re-initializes all tracking related state.
    /// </summary>
    private void ResetTrackingState()
    {
        if (IsKeyboardOperationActive())
        {
            return;
        }

        if (_horizScrollTimer is not null && _horizScrollTimer.Enabled)
        {
            _horizScrollTimer.Enabled = false;
        }

        if (_vertScrollTimer is not null && _vertScrollTimer.Enabled)
        {
            _vertScrollTimer.Enabled = false;
        }

        _dataGridViewOper[OperationMouseOperationMask] = false;
        _dataGridViewOper[OperationTrackColSelect] = false;
        _dataGridViewOper[OperationTrackRowSelect] = false;
        _dataGridViewOper[OperationTrackCellSelect] = false;
        _trackColumn = -1;
        _trackRow = -1;
        _ptMouseDownCell.X = -2;
        _ptMouseDownCell.Y = -2;

        if (_currentRowSplitBar != -1)
        {
            Invalidate(CalcRowResizeFeedbackRect(_currentRowSplitBar), true);
            _lastRowSplitBar = _currentRowSplitBar = -1;
        }

        if (_currentColSplitBar != -1)
        {
            Invalidate(CalcColResizeFeedbackRect(_currentColSplitBar), true);
            _lastColSplitBar = _currentColSplitBar = -1;
        }

        if (_lastHeaderShadow != -1)
        {
            _dataGridViewState2[State2_ShowColumnRelocationInsertion] = false;
            _trackColumnEdge = -1;
            _lastHeaderShadow = -1;
            Invalidate(Rectangle.Union(_layout.TopLeftHeader, _layout.ColumnHeaders));
        }

        ReleaseMouse();
    }

    // Re-initializes all state that is related to tracking keyboard operations
    private void ResetKeyboardTrackingState()
    {
        if (IsMouseOperationActive())
        {
            return;
        }

        _dataGridViewOper[OperationKeyboardOperationMask] = false;
        _trackColumn = -1;
        if (_currentColSplitBar != -1)
        {
            Invalidate(CalcColResizeFeedbackRect(_currentColSplitBar), true);
            _lastColSplitBar = _currentColSplitBar = -1;
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private bool IsMouseOperationActive()
    {
        return (_dataGridViewOper.Data & OperationMouseOperationMask) != 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private bool IsKeyboardOperationActive()
    {
        return (_dataGridViewOper.Data & OperationKeyboardOperationMask) != 0;
    }

    /// <summary>
    ///  Re-initializes all UI related state.
    /// </summary>
    internal void ResetUIState(bool useRowShortcut, bool computeVisibleRows)
    {
        PerformLayoutPrivate(
            useRowShortcut,
            computeVisibleRows,
            invalidInAdjustFillingColumns: true,
            repositionEditingControl: !useRowShortcut);
        if (!useRowShortcut)
        {
            Invalidate();
            InvalidateScrollBars();
        }
    }

    private void RestoreRowsCachedThickness()
    {
        // Switch to batch operation
        _inBulkPaintCount++;
        _inBulkLayoutCount++;
        try
        {
            // Only height of visible rows are restored since invisible rows are not autosized.
            for (int rowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                rowIndex != -1;
                rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible))
            {
                DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
                dataGridViewRow.GetHeightInfo(rowIndex, out int height, out int minimumHeight);
                if (height != dataGridViewRow.CachedThickness
                    && !OnRowHeightInfoPushed(rowIndex, dataGridViewRow.CachedThickness, minimumHeight))
                {
                    dataGridViewRow.ThicknessInternal = dataGridViewRow.CachedThickness;
                }
            }
        }
        finally
        {
            ExitBulkLayout(invalidInAdjustFillingColumns: true);
            ExitBulkPaint(-1, -1);
        }
    }

    // we need to access the GetRowState, otherwise we would un-share the row
    private bool RowIsResizable(int rowIndex)
    {
        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
        return (rowState & DataGridViewElementStates.ResizableSet) == DataGridViewElementStates.ResizableSet
            ? (rowState & DataGridViewElementStates.Resizable) == DataGridViewElementStates.Resizable
            : AllowUserToResizeRows;
    }

    private bool RowNeedsDisplayedState(int rowIndex, int lastDisplayedFrozenRowIndex, int lastDisplayedScrollingRowIndex)
    {
        Debug.Assert(rowIndex >= 0);
        Debug.Assert(rowIndex < Rows.Count);

        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);

        if ((rowState & DataGridViewElementStates.Visible) == 0)
        {
            return false;
        }

        if ((rowState & DataGridViewElementStates.Frozen) != 0)
        {
            return rowIndex <= lastDisplayedFrozenRowIndex;
        }
        else if (DisplayedBandsInfo.FirstDisplayedScrollingRow != -1
            && rowIndex >= DisplayedBandsInfo.FirstDisplayedScrollingRow
            && rowIndex <= lastDisplayedScrollingRowIndex)
        {
            return true;
        }

        return false;
    }

    private void ScrollBar_MouseEnter(object? sender, EventArgs e)
    {
        if (_dataGridViewState2[State2_MouseEnterExpected])
        {
            OnMouseEnter(EventArgs.Empty);
        }

        UpdateMouseEnteredCell(hti: null, e: null);
    }

    private void ScrollBar_MouseLeave(object? sender, EventArgs e)
    {
        UpdateMouseEnteredCell(hti: null, e: null);
        Point ptMouse = PointToClient(MousePosition);
        if (!ClientRectangle.Contains(ptMouse))
        {
            OnMouseLeave(EventArgs.Empty);
        }
    }

    private bool ScrollColumnIntoView(int columnIndex, int rowIndex, bool committed, bool forCurrentCellChange)
    {
        Debug.Assert(columnIndex >= 0 && columnIndex < Columns.Count);

        if (DisplayedBandsInfo.FirstDisplayedScrollingCol != -1
            && !Columns[columnIndex].Frozen
            && (columnIndex != DisplayedBandsInfo.FirstDisplayedScrollingCol || FirstDisplayedScrollingColumnHiddenWidth > 0))
        {
            int columnsToScroll;
            if (Columns.DisplayInOrder(columnIndex, DisplayedBandsInfo.FirstDisplayedScrollingCol))
            {
                if (!committed && _ptCurrentCell.X >= 0 && !CommitEdit(
                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll,
                    forCurrentCellChange: forCurrentCellChange && (_ptCurrentCell.X != columnIndex || _ptCurrentCell.Y != rowIndex),
                    forCurrentRowChange: _ptCurrentCell.Y != rowIndex))
                {
                    return false;
                }

                columnsToScroll = Columns.GetColumnCount(DataGridViewElementStates.Visible, columnIndex, DisplayedBandsInfo.FirstDisplayedScrollingCol);
                if (FirstDisplayedScrollingColumnHiddenWidth > 0)
                {
                    columnsToScroll++;
                }

                ScrollColumns(-columnsToScroll);
            }
            else if (columnIndex == DisplayedBandsInfo.FirstDisplayedScrollingCol && FirstDisplayedScrollingColumnHiddenWidth > 0)
            {
                if (!committed && _ptCurrentCell.X >= 0 && !CommitEdit(
                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll,
                    forCurrentCellChange: forCurrentCellChange && (_ptCurrentCell.X != columnIndex || _ptCurrentCell.Y != rowIndex),
                    forCurrentRowChange: _ptCurrentCell.Y != rowIndex))
                {
                    return false;
                }

                ScrollColumns(-1);
            }
            else if (DisplayedBandsInfo.LastTotallyDisplayedScrollingCol == -1
                || (DisplayedBandsInfo.LastTotallyDisplayedScrollingCol != columnIndex
                    && Columns.DisplayInOrder(DisplayedBandsInfo.LastTotallyDisplayedScrollingCol, columnIndex)))
            {
                if (!committed && _ptCurrentCell.X >= 0 && !CommitEdit(
                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll,
                    forCurrentCellChange: forCurrentCellChange && (_ptCurrentCell.X != columnIndex || _ptCurrentCell.Y != rowIndex),
                    forCurrentRowChange: _ptCurrentCell.Y != rowIndex))
                {
                    return false;
                }

                columnsToScroll = 0;
                int firstDisplayedScrollingColumn = DisplayedBandsInfo.FirstDisplayedScrollingCol;
                int xColumnRightEdge;

                if (RightToLeftInternal)
                {
                    xColumnRightEdge = GetColumnXFromIndex(columnIndex) - Columns[columnIndex].Width;
                    while (xColumnRightEdge < _layout.Data.X && Columns.DisplayInOrder(firstDisplayedScrollingColumn, columnIndex))
                    {
                        xColumnRightEdge += Columns[firstDisplayedScrollingColumn].Width;
                        if (firstDisplayedScrollingColumn == DisplayedBandsInfo.FirstDisplayedScrollingCol)
                        {
                            xColumnRightEdge -= FirstDisplayedScrollingColumnHiddenWidth;
                        }

                        columnsToScroll++;
                        if (xColumnRightEdge < _layout.Data.X)
                        {
                            firstDisplayedScrollingColumn = Columns.GetNextColumn(
                                Columns[firstDisplayedScrollingColumn],
                                DataGridViewElementStates.Visible,
                                DataGridViewElementStates.None)!.Index;
                        }
                    }
                }
                else
                {
                    xColumnRightEdge = GetColumnXFromIndex(columnIndex) + Columns[columnIndex].Width;
                    while (xColumnRightEdge > _layout.Data.Right && Columns.DisplayInOrder(firstDisplayedScrollingColumn, columnIndex))
                    {
                        xColumnRightEdge -= Columns[firstDisplayedScrollingColumn].Width;
                        if (firstDisplayedScrollingColumn == DisplayedBandsInfo.FirstDisplayedScrollingCol)
                        {
                            xColumnRightEdge += FirstDisplayedScrollingColumnHiddenWidth;
                        }

                        columnsToScroll++;
                        if (xColumnRightEdge > _layout.Data.Right)
                        {
                            firstDisplayedScrollingColumn = Columns.GetNextColumn(
                                Columns[firstDisplayedScrollingColumn],
                                DataGridViewElementStates.Visible,
                                DataGridViewElementStates.None)!.Index;
                        }
                    }
                }

                if (columnsToScroll != 0)
                {
                    ScrollColumns(columnsToScroll);
                }
            }
        }

        return true;
    }

    private void ScrollColumns(int columns)
    {
        DataGridViewColumn? newFirstVisibleScrollingCol = null;
        DataGridViewColumn? dataGridViewColumnTmp;
        int colCount = 0;
        if (columns > 0)
        {
            if (DisplayedBandsInfo.LastTotallyDisplayedScrollingCol >= 0)
            {
                dataGridViewColumnTmp = Columns[DisplayedBandsInfo.LastTotallyDisplayedScrollingCol];
                while (colCount < columns && dataGridViewColumnTmp is not null)
                {
                    dataGridViewColumnTmp = Columns.GetNextColumn(
                        dataGridViewColumnTmp,
                        DataGridViewElementStates.Visible,
                        DataGridViewElementStates.None);
                    colCount++;
                }

                if (dataGridViewColumnTmp is null)
                {
                    // no more column to display on the right of the last totally seen column
                    return;
                }
            }

            Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0);
            dataGridViewColumnTmp = Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol];
            colCount = 0;
            while (colCount < columns && dataGridViewColumnTmp is not null)
            {
                dataGridViewColumnTmp = Columns.GetNextColumn(
                    dataGridViewColumnTmp,
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.None);
                colCount++;
            }

            newFirstVisibleScrollingCol = dataGridViewColumnTmp;
        }

        if (columns < 0)
        {
            Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingCol >= 0);
            dataGridViewColumnTmp = Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol];
            if (FirstDisplayedScrollingColumnHiddenWidth > 0)
            {
                colCount++;
            }

            while (colCount < -columns && dataGridViewColumnTmp is not null)
            {
                dataGridViewColumnTmp = Columns.GetPreviousColumn(
                    dataGridViewColumnTmp,
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.Frozen);
                colCount++;
            }

            newFirstVisibleScrollingCol = dataGridViewColumnTmp;
            if (newFirstVisibleScrollingCol is null)
            {
                if (FirstDisplayedScrollingColumnHiddenWidth == 0)
                {
                    // no more column to display on the left of the first seen column
                    FlushDisplayedChanged();
                    return;
                }
                else
                {
                    newFirstVisibleScrollingCol = Columns[DisplayedBandsInfo.FirstDisplayedScrollingCol];
                }
            }
        }

        int newColOffset = 0;
        for (DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.Frozen);
            dataGridViewColumn != newFirstVisibleScrollingCol;
            dataGridViewColumn = Columns.GetNextColumn(
                dataGridViewColumn,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None))
        {
            newColOffset += dataGridViewColumn!.Thickness;
        }

        HorizontalOffset = newColOffset;
    }

    private bool ScrollIntoView(int columnIndex, int rowIndex, bool forCurrentCellChange)
    {
        Debug.Assert(columnIndex >= 0 && columnIndex < Columns.Count);
        Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingCol >= -1 && DisplayedBandsInfo.FirstDisplayedScrollingCol < Columns.Count);
        Debug.Assert(DisplayedBandsInfo.LastTotallyDisplayedScrollingCol >= -1 && DisplayedBandsInfo.LastTotallyDisplayedScrollingCol < Columns.Count);
        Debug.Assert(rowIndex >= 0 && rowIndex < Rows.Count);
        Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= -1 && DisplayedBandsInfo.FirstDisplayedScrollingRow < Rows.Count);
        Debug.Assert(Columns[columnIndex].Visible);
        Debug.Assert((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0);

        bool committed = false;
        if (_ptCurrentCell.X >= 0 && (_ptCurrentCell.X != columnIndex || _ptCurrentCell.Y != rowIndex))
        {
            if (!CommitEditForOperation(columnIndex, rowIndex, forCurrentCellChange))
            {
                return false;
            }

            committed = true;
            if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
            {
                return false;
            }
        }

        // scroll horizontally
        if (!ScrollColumnIntoView(columnIndex, rowIndex, committed, forCurrentCellChange))
        {
            return false;
        }

        if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
        {
            return false;
        }

        // scroll vertically
        return ScrollRowIntoView(columnIndex, rowIndex, committed, forCurrentCellChange);
    }

    private unsafe void ScrollRectangles(RECT[]? rects, int change)
    {
        if (rects is not null)
        {
            if (MouseButtons != MouseButtons.None)
            {
                _dataGridViewState1[State1_ScrolledSinceMouseDown] = true;
            }

            RECT scroll;
            for (int r = 0; r < rects.Length; r++)
            {
                scroll = rects[r];
                PInvoke.ScrollWindow(
                    this,
                    change,
                    0,
                    &scroll,
                    &scroll);
            }
        }
    }

    private bool ScrollRowIntoView(int columnIndex, int rowIndex, bool committed, bool forCurrentCellChange)
    {
        Debug.Assert(rowIndex >= 0 && rowIndex < Rows.Count);

        if ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Frozen) == 0)
        {
            int rowsToScroll;
            if (rowIndex < DisplayedBandsInfo.FirstDisplayedScrollingRow)
            {
                if (!committed && _ptCurrentCell.X >= 0 && !CommitEdit(
                    DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll,
                    forCurrentCellChange: forCurrentCellChange && (_ptCurrentCell.X != columnIndex || _ptCurrentCell.Y != rowIndex),
                    forCurrentRowChange: _ptCurrentCell.Y != rowIndex))
                {
                    return false;
                }

                rowsToScroll = Rows.GetRowCount(DataGridViewElementStates.Visible, rowIndex, DisplayedBandsInfo.FirstDisplayedScrollingRow);
                ScrollRowsByCount(-rowsToScroll, rowsToScroll > 1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement);
            }
            else if (DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0 && rowIndex > DisplayedBandsInfo.FirstDisplayedScrollingRow)
            {
                rowsToScroll = 0;
                int firstDisplayedScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                int yRowBottomEdge = GetRowYFromIndex(rowIndex) + Rows.SharedRow(rowIndex).GetHeight(rowIndex);
                while (yRowBottomEdge > _layout.Data.Bottom && rowIndex > firstDisplayedScrollingRow)
                {
                    yRowBottomEdge -= Rows.SharedRow(firstDisplayedScrollingRow).GetHeight(firstDisplayedScrollingRow);
                    rowsToScroll++;
                    if (yRowBottomEdge > _layout.Data.Bottom)
                    {
                        firstDisplayedScrollingRow = Rows.GetNextRow(firstDisplayedScrollingRow, DataGridViewElementStates.Visible);
                        Debug.Assert(firstDisplayedScrollingRow != -1);
                    }
                }

                if (rowsToScroll != 0)
                {
                    if (!committed && _ptCurrentCell.X >= 0 && !CommitEdit(
                        DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Scroll,
                        forCurrentCellChange: forCurrentCellChange && (_ptCurrentCell.X != columnIndex || _ptCurrentCell.Y != rowIndex),
                        forCurrentRowChange: _ptCurrentCell.Y != rowIndex))
                    {
                        return false;
                    }

                    ScrollRowsByCount(rowsToScroll, rowsToScroll > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement);
                }
            }
        }

        return true;
    }

    private unsafe void ScrollRows(int rowCount, int deltaY, ScrollEventType scrollEventType)
    {
        bool invalidateTopOfRowHeaders = false;
        Debug.Assert(rowCount != 0);
        Debug.Assert(deltaY != 0);
        Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen));
        VerticalScrollingOffset -= deltaY;
        if (_vertScrollBar.Enabled)
        {
            _vertScrollBar.Value = VerticalScrollingOffset;
        }

        ClearRegionCache();
        int frozenRowsThickness = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        Rectangle rowsRect = _layout.Data;
        if (_layout.RowHeadersVisible)
        {
            rowsRect = Rectangle.Union(rowsRect, _layout.RowHeaders);
            if (SingleHorizontalBorderAdded)
            {
                rowsRect.Y++;
                rowsRect.Height--;
                invalidateTopOfRowHeaders = true;
            }
        }
        else if (SingleVerticalBorderAdded)
        {
            Debug.Assert(rowsRect.X > 0);
            rowsRect.X--;
            rowsRect.Width++;
        }

        rowsRect.Y += frozenRowsThickness;
        rowsRect.Height -= frozenRowsThickness;
        Debug.Assert(rowsRect.Height >= 0);

        if (EditingControl is not null && (Rows.GetRowState(_ptCurrentCell.Y) & DataGridViewElementStates.Frozen) == 0)
        {
            Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow > -1);
            PositionEditingControl(setLocation: true, setSize: false, setFocus: false);
        }

        if (MouseButtons != MouseButtons.None)
        {
            _dataGridViewState1[State1_ScrolledSinceMouseDown] = true;
        }

        // The mouse probably is not over the same cell after the scroll.
        UpdateMouseEnteredCell(hti: null, e: null);

        RECT scrollArea = rowsRect;
        PInvoke.ScrollWindow(this, 0, deltaY, &scrollArea, &scrollArea);
        if (invalidateTopOfRowHeaders)
        {
            rowsRect.X = _layout.Inside.X;
            rowsRect.Y = _layout.Inside.Y;
            rowsRect.Width = _layout.RowHeaders.Width;
            rowsRect.Height = 1;
            Invalidate(rowsRect);
        }

        if (!_dataGridViewState2[State2_StopRaisingVerticalScroll])
        {
            Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
            int firstVisibleScrollingRow = Rows.GetFirstRow(DataGridViewElementStates.Visible, DataGridViewElementStates.Frozen);
            Debug.Assert(firstVisibleScrollingRow != -1);
            int newScrolledOffRowCount = Rows.GetRowCount(DataGridViewElementStates.Visible, firstVisibleScrollingRow, DisplayedBandsInfo.FirstDisplayedScrollingRow);
            Debug.Assert(newScrolledOffRowCount >= rowCount);
            OnScroll(scrollEventType, newScrolledOffRowCount - rowCount, newScrolledOffRowCount, ScrollOrientation.VerticalScroll);
        }
    }

    private void ScrollRowsByCount(int rows, ScrollEventType scrollEventType)
    {
        Debug.Assert(rows != 0);
        Debug.Assert((rows > 0 && (scrollEventType == ScrollEventType.SmallIncrement || scrollEventType == ScrollEventType.LargeIncrement))
            || (rows < 0 && (scrollEventType == ScrollEventType.SmallDecrement || scrollEventType == ScrollEventType.LargeDecrement)));

        int deltaY = 0;
        Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
        int newFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
        if (rows > 0)
        {
            for (int rowCount = rows; rowCount > 0; rowCount--)
            {
                deltaY -= Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow);
                newFirstVisibleScrollingRow = Rows.GetNextRow(
                    newFirstVisibleScrollingRow,
                    DataGridViewElementStates.Visible);

                Debug.Assert(newFirstVisibleScrollingRow != -1);
            }

            if (newFirstVisibleScrollingRow != -1)
            {
                int oldFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
                // Tentative target value for this.displayedBandsInfo.FirstDisplayedScrollingRow.
                DisplayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow;
                // This sets the actual value of this.displayedBandsInfo.FirstDisplayedScrollingRow
                ComputeVisibleRows();
                // Compute the actual deltaY given the new this.displayedBandsInfo.FirstDisplayedScrollingRow
                if (DisplayedBandsInfo.FirstDisplayedScrollingRow > oldFirstVisibleScrollingRow)
                {
                    deltaY = -Rows.GetRowsHeight(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, DisplayedBandsInfo.FirstDisplayedScrollingRow);
                    rows = Rows.GetRowCount(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, DisplayedBandsInfo.FirstDisplayedScrollingRow);
                }
                else
                {
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow == oldFirstVisibleScrollingRow);
                    rows = 0;
                }
            }
        }
        else
        {
            for (int rowCount = rows; rowCount < 0; rowCount++)
            {
                newFirstVisibleScrollingRow = Rows.GetPreviousRow(
                    newFirstVisibleScrollingRow,
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.Frozen);
                if (newFirstVisibleScrollingRow != -1)
                {
                    deltaY += Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow);
                }
            }

            if (newFirstVisibleScrollingRow != -1)
            {
                DisplayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow;
                ComputeVisibleRows();
                Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow == newFirstVisibleScrollingRow);
            }
        }

        if (newFirstVisibleScrollingRow != -1 && rows != 0)
        {
            ScrollRows(rows, deltaY, scrollEventType);
        }

        FlushDisplayedChanged();
    }

    private void ScrollRowsByHeight(int height)
    {
        Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
        int deltaY, scrollHeight = 0;
        int oldFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
        int newFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;
        if (height > 0)
        {
            deltaY = Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow);
            while (deltaY <= height)
            {
                newFirstVisibleScrollingRow = Rows.GetNextRow(newFirstVisibleScrollingRow, DataGridViewElementStates.Visible);
                if (newFirstVisibleScrollingRow == -1)
                {
                    throw new InvalidOperationException();
                }
                else
                {
                    deltaY += Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow);
                }
            }
        }
        else
        {
            newFirstVisibleScrollingRow = Rows.GetPreviousRow(
                newFirstVisibleScrollingRow,
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.Frozen);
            Debug.Assert(newFirstVisibleScrollingRow != -1);
            deltaY = -Rows.SharedRow(newFirstVisibleScrollingRow).GetHeight(newFirstVisibleScrollingRow);
            while (deltaY >= height)
            {
                int scrollingRowTmp = Rows.GetPreviousRow(newFirstVisibleScrollingRow,
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.Frozen);
                if (scrollingRowTmp != -1)
                {
                    deltaY -= Rows.SharedRow(scrollingRowTmp).GetHeight(scrollingRowTmp);
                    if (deltaY >= height)
                    {
                        newFirstVisibleScrollingRow = scrollingRowTmp;
                    }
                }
                else
                {
                    break;
                }
            }
        }

        // Tentative target value for this.displayedBandsInfo.FirstDisplayedScrollingRow.
        DisplayedBandsInfo.FirstDisplayedScrollingRow = newFirstVisibleScrollingRow;
        // This sets the actual value of this.displayedBandsInfo.FirstDisplayedScrollingRow
        ComputeVisibleRows();
        ScrollEventType scrollEventType = ScrollEventType.EndScroll;
        int rowCount = 0;
        // Compute the scrollHeight given the new this.displayedBandsInfo.FirstDisplayedScrollingRow
        if (DisplayedBandsInfo.FirstDisplayedScrollingRow > oldFirstVisibleScrollingRow)
        {
            scrollHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, DisplayedBandsInfo.FirstDisplayedScrollingRow);
            rowCount = Rows.GetRowCount(DataGridViewElementStates.Visible, oldFirstVisibleScrollingRow, DisplayedBandsInfo.FirstDisplayedScrollingRow);
            scrollEventType = rowCount > 1 ? ScrollEventType.LargeIncrement : ScrollEventType.SmallIncrement;
        }
        else if (DisplayedBandsInfo.FirstDisplayedScrollingRow < oldFirstVisibleScrollingRow)
        {
            scrollHeight = -Rows.GetRowsHeight(DataGridViewElementStates.Visible, DisplayedBandsInfo.FirstDisplayedScrollingRow, oldFirstVisibleScrollingRow);
            rowCount = -Rows.GetRowCount(DataGridViewElementStates.Visible, DisplayedBandsInfo.FirstDisplayedScrollingRow, oldFirstVisibleScrollingRow);
            scrollEventType = rowCount < -1 ? ScrollEventType.LargeDecrement : ScrollEventType.SmallDecrement;
        }

        if (scrollHeight != 0)
        {
            ScrollRows(rowCount, -scrollHeight, scrollEventType);
        }

        FlushDisplayedChanged();
    }

    public void SelectAll()
    {
        if (!MultiSelect)
        {
            return;
        }

        _inBulkPaintCount++;
        _noDimensionChangeCount++;
        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                    {
                        // Bonjour the scalability issues! We select each cell, one at the time.
                        int maxColumnIndex = Columns.Count;
                        int rowIndex = 0, maxRowIndex = Rows.Count;
                        while (rowIndex < maxRowIndex)
                        {
                            // Accessing the indexer unshares the row.
                            _ = Rows[rowIndex];
                            int columnIndex = 0;
                            while (columnIndex < maxColumnIndex)
                            {
                                SetSelectedCellCore(columnIndex, rowIndex, true);
                                columnIndex++;
                            }

                            rowIndex++;
                        }

                        break;
                    }

                case DataGridViewSelectionMode.FullRowSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                    {
                        int rowIndex = 0, maxRowIndex = Rows.Count;
                        while (rowIndex < maxRowIndex)
                        {
                            DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
                            if ((rowState & DataGridViewElementStates.Selected) == 0)
                            {
                                SetSelectedRowCore(rowIndex, true);
                            }

                            rowIndex++;
                        }

                        break;
                    }

                case DataGridViewSelectionMode.FullColumnSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    {
                        int columnIndex = 0, maxColumnIndex = Columns.Count;
                        while (columnIndex < maxColumnIndex)
                        {
                            if (!Columns[columnIndex].Selected)
                            {
                                SetSelectedColumnCore(columnIndex, true);
                            }

                            columnIndex++;
                        }

                        break;
                    }
            }
        }
        finally
        {
            _noDimensionChangeCount--;
            _noSelectionChangeCount--;
            Debug.Assert(_noDimensionChangeCount >= 0);
            Debug.Assert(_noSelectionChangeCount >= 0);
            ExitBulkPaint(-1, -1);
        }

        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
        if (dataGridViewColumn is not null && firstVisibleRowIndex != -1)
        {
            // This is the only place in the code outside of SetCurrentCellAddressCore where this.ptAnchorCell gets changed.
            // There is no way in SetCurrentCellAddressCore to just change the anchor cell.
            _ptAnchorCell.X = dataGridViewColumn.Index;
            _ptAnchorCell.Y = firstVisibleRowIndex;
        }
        else
        {
            _ptAnchorCell.X = -1;
            _ptAnchorCell.Y = -1;
        }

        if (_noSelectionChangeCount == 0)
        {
            FlushSelectionChanged();
        }
    }

    private DataGridViewCell? SelectedCell(int index)
    {
        Debug.Assert(index >= 0);
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.CellSelect:
                {
                    if (index < _individualSelectedCells.Count)
                    {
                        return _individualSelectedCells[index];
                    }

                    break;
                }

            case DataGridViewSelectionMode.FullColumnSelect:
            case DataGridViewSelectionMode.ColumnHeaderSelect:
                {
                    int selectedBand = 0, selectedBands = _selectedBandIndexes.Count;
                    while (selectedBand < selectedBands && index >= 0)
                    {
                        if (index >= Rows.Count)
                        {
                            index -= Rows.Count;
                            selectedBand++;
                        }
                        else
                        {
                            int columnIndex = _selectedBandIndexes[selectedBand];
                            return Rows.SharedRow(index).Cells[columnIndex];
                        }
                    }

                    if (SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect
                        && index < _individualSelectedCells.Count)
                    {
                        return _individualSelectedCells[index];
                    }

                    break;
                }

            case DataGridViewSelectionMode.FullRowSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
                {
                    int selectedBand = 0, selectedBands = _selectedBandIndexes.Count;
                    while (selectedBand < selectedBands && index >= 0)
                    {
                        if (index >= Columns.Count)
                        {
                            index -= Columns.Count;
                            selectedBand++;
                        }
                        else
                        {
                            int rowIndex = _selectedBandIndexes[selectedBand];
                            return Rows.SharedRow(rowIndex).Cells[index];
                        }
                    }

                    if (SelectionMode == DataGridViewSelectionMode.RowHeaderSelect && index < _individualSelectedCells.Count)
                    {
                        return _individualSelectedCells[index];
                    }

                    break;
                }
        }

        return null;
    }

    private void SetColumnHeadersHeightInternal(int columnHeadersHeight, bool invalidInAdjustFillingColumns)
    {
        using (LayoutTransaction.CreateTransactionIf(AutoSize, ParentInternal, this, PropertyNames.ColumnHeadersHeight))
        {
            Debug.Assert(_columnHeadersHeight != columnHeadersHeight);
            Debug.Assert(columnHeadersHeight >= MinimumColumnHeadersHeight);
            Debug.Assert(columnHeadersHeight <= MaxHeadersThickness);
            _columnHeadersHeight = columnHeadersHeight;
            if (AutoSize)
            {
                InvalidateInside();
            }
            else
            {
                if (_layout.ColumnHeadersVisible)
                {
                    PerformLayoutPrivate(
                        useRowShortcut: false,
                        computeVisibleRows: false,
                        invalidInAdjustFillingColumns,
                        repositionEditingControl: true);
                    InvalidateInside();
                }
            }

            OnColumnHeadersHeightChanged(EventArgs.Empty);
        }
    }

    protected virtual bool SetCurrentCellAddressCore(int columnIndex,
        int rowIndex,
        bool setAnchorCellAddress,
        bool validateCurrentCell,
        bool throughMouseClick)
    {
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        if (rowIndex == -1)
        {
            ArgumentOutOfRangeException.ThrowIfNotEqual(columnIndex, -1);
        }
        else if (columnIndex == -1)
        {
            ArgumentOutOfRangeException.ThrowIfNotEqual(rowIndex, -1);
        }
        else
        {
            ArgumentOutOfRangeException.ThrowIfNegative(columnIndex);
            ArgumentOutOfRangeException.ThrowIfNegative(rowIndex);
        }

        if (columnIndex > -1
            && rowIndex > -1
            && !IsSharedCellVisible(Rows.SharedRow(rowIndex).Cells[columnIndex], rowIndex))
        {
            throw new InvalidOperationException(SR.DataGridView_CurrentCellCannotBeInvisible);
        }

        if (_dataGridViewOper[OperationInCurrentCellChange] &&                      // Allow the code to be re-entrant only as a result of
            (DataConnection is null || !DataConnection.ProcessingListChangedEvent))   // underlying data changing.
        {
            throw new InvalidOperationException(SR.DataGridView_SetCurrentCellAddressCoreNotReentrant);
        }

        _dataGridViewOper[OperationInCurrentCellChange] = true;
        try
        {
            DataGridViewCell? dataGridViewCellTmp = null;

            if (columnIndex > -1)
            {
                Debug.Assert(rowIndex >= 0 && columnIndex < Columns.Count && rowIndex < Rows.Count);

                if (_ptCurrentCell.X != columnIndex || _ptCurrentCell.Y != rowIndex)
                {
                    if (_dataGridViewState1[State1_TemporarilyResetCurrentCell])
                    {
                        _dataGridViewState1[State1_TemporarilyResetCurrentCell] = false;
                        _ptCurrentCell.X = columnIndex;
                        _ptCurrentCell.Y = rowIndex;
                        if (_cachedEditingControl is not null)
                        {
                            EditingControl = _cachedEditingControl;
                            ((IDataGridViewEditingControl)EditingControl).EditingControlRowIndex = rowIndex;
                            _cachedEditingControl = null;
                            PositionEditingControl(true, true, false);
                        }

                        OnCurrentCellChanged(EventArgs.Empty);
                        return true;
                    }

                    DataGridViewCell currentCell;
                    int oldCurrentCellX = _ptCurrentCell.X;
                    int oldCurrentCellY = _ptCurrentCell.Y;
                    if (oldCurrentCellX >= 0)
                    {
                        currentCell = CurrentCellInternal;
                        if (!EndEdit(
                            DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange,
                            validateCell: validateCurrentCell ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.Never,
                            fireCellLeave: validateCurrentCell,
                            fireCellEnter: false,
                            fireRowLeave: validateCurrentCell && oldCurrentCellY != rowIndex,
                            fireRowEnter: false,
                            fireLeave: false,
                            keepFocus: EditMode != DataGridViewEditMode.EditOnEnter,
                            resetCurrentCell: false,
                            resetAnchorCell: false))
                        {
                            return false;
                        }

                        if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                        {
                            currentCell = Rows.SharedRow(oldCurrentCellY).Cells[oldCurrentCellX];
                            if (currentCell.LeaveUnsharesRowInternal(oldCurrentCellY, throughMouseClick))
                            {
                                // un-sharing the current row
                                currentCell = Rows[oldCurrentCellY].Cells[oldCurrentCellX];
                            }

                            currentCell.OnLeaveInternal(oldCurrentCellY, throughMouseClick);
                        }

                        if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
                        {
                            return false;
                        }

                        if (oldCurrentCellY != rowIndex)
                        {
                            if (validateCurrentCell)
                            {
                                if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY))
                                {
                                    if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                    {
                                        // Row validation was cancelled
                                        Debug.Assert(oldCurrentCellX == _ptCurrentCell.X);
                                        Debug.Assert(oldCurrentCellY == _ptCurrentCell.Y);
                                        OnRowEnter(
                                            ref dataGridViewCellTmp,
                                            oldCurrentCellX,
                                            oldCurrentCellY,
                                            canCreateNewRow: true,
                                            validationFailureOccurred: true);
                                        if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                        {
                                            currentCell.OnEnterInternal(oldCurrentCellY, throughMouseClick);
                                            OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY);
                                        }
                                    }

                                    return false;
                                }

                                if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                {
                                    OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY);
                                }
                            }
                        }
                    }

                    _dataGridViewState2[State2_RowsCollectionClearedInSetCell] = false;

                    try
                    {
                        if (oldCurrentCellY != rowIndex)
                        {
                            if (!IsInnerCellOutOfBounds(columnIndex, rowIndex))
                            {
                                OnRowEnter(
                                    ref dataGridViewCellTmp,
                                    columnIndex,
                                    rowIndex,
                                    canCreateNewRow: true,
                                    validationFailureOccurred: false);
                            }
                        }

                        // Force repainting of the current and previous columns` header cells to update highlighting
                        if (oldCurrentCellX != columnIndex && SelectionMode == DataGridViewSelectionMode.FullRowSelect)
                        {
                            if (oldCurrentCellX >= 0)
                            {
                                InvalidateCellPrivate(oldCurrentCellX, -1);
                            }

                            InvalidateCellPrivate(columnIndex, -1);
                        }

                        if (_dataGridViewState2[State2_RowsCollectionClearedInSetCell])
                        {
                            //
                            // The rows collection was cleared while processing OnRowEnter.
                            // If the new list is too small for rowIndex, fail the call to SetCurrentCellAddressCore.
                            //
                            if (rowIndex >= Rows.Count)
                            {
                                return false;
                            }

                            // rowIndex = Math.Min(rowIndex, this.Rows.GetRowCount(DataGridViewElementStates.Visible) - 1);
                        }

                        if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
                        {
                            return false;
                        }

                        _ptCurrentCell.X = columnIndex;
                        _ptCurrentCell.Y = rowIndex;
                        if (EditingControl is not null)
                        {
                            ((IDataGridViewEditingControl)EditingControl).EditingControlRowIndex = rowIndex;
                        }

                        OnCurrentCellChanged(EventArgs.Empty);
                        if (setAnchorCellAddress)
                        {
                            _ptAnchorCell.X = columnIndex;
                            _ptAnchorCell.Y = rowIndex;
                        }

                        currentCell = CurrentCellInternal;
                        if (currentCell.EnterUnsharesRowInternal(rowIndex, throughMouseClick))
                        {
                            // un-sharing the row
                            currentCell = Rows[rowIndex].Cells[columnIndex];
                        }

                        currentCell.OnEnterInternal(rowIndex, throughMouseClick);
                        OnCellEnter(ref dataGridViewCellTmp, _ptCurrentCell.X, _ptCurrentCell.Y);
                        if (oldCurrentCellX >= 0)
                        {
                            Debug.Assert(oldCurrentCellY >= 0);
                            if (oldCurrentCellX < Columns.Count && oldCurrentCellY < Rows.Count)
                            {
                                InvalidateCellPrivate(oldCurrentCellX, oldCurrentCellY);
                            }

                            if (oldCurrentCellY != _ptCurrentCell.Y && RowHeadersVisible && oldCurrentCellY < Rows.Count)
                            {
                                InvalidateCellPrivate(-1, oldCurrentCellY);
                            }
                        }

                        InvalidateCellPrivate(_ptCurrentCell.X, _ptCurrentCell.Y);
                        if (RowHeadersVisible && oldCurrentCellY != _ptCurrentCell.Y)
                        {
                            InvalidateCellPrivate(-1, _ptCurrentCell.Y);
                        }

                        if (Focused
                            && _ptCurrentCell.X != -1
                            && !IsCurrentCellInEditMode
                            // Don't edit if we're in the process of leaving the grid
                            && !_dataGridViewState1[State1_LeavingWithTabKey]
                            // Don't edit if the rows collection changed
                            && !_dataGridViewState2[State2_RowsCollectionClearedInSetCell]
                            && (EditMode == DataGridViewEditMode.EditOnEnter
                                || (EditMode != DataGridViewEditMode.EditProgrammatically && currentCell.EditType is null)))
                        {
                            BeginEditInternal(selectAll: true);
                        }
                    }
                    finally
                    {
                        _dataGridViewState2[State2_RowsCollectionClearedInSetCell] = false;
                    }

                    // Accessibility
                    if (_ptCurrentCell.X != -1)
                    {
                        AccessibilityNotifyCurrentCellChanged(new Point(_ptCurrentCell.X, _ptCurrentCell.Y));
                    }
                }
                else
                {
                    // Not trying to change the current cell, but may need to edit it.
                    if (setAnchorCellAddress)
                    {
                        _ptAnchorCell.X = columnIndex;
                        _ptAnchorCell.Y = rowIndex;
                    }

                    if (Focused
                        && !IsCurrentCellInEditMode
                        && (EditMode == DataGridViewEditMode.EditOnEnter
                            || (EditMode != DataGridViewEditMode.EditProgrammatically && CurrentCellInternal.EditType is null)))
                    {
                        BeginEditInternal(selectAll: true);
                    }
                    else
                    {
                        CorrectFocus(onlyIfGridHasFocus: false);
                    }
                }
            }
            else
            {
                int oldCurrentCellX = _ptCurrentCell.X;
                int oldCurrentCellY = _ptCurrentCell.Y;
                if (oldCurrentCellX >= 0
                    && !_dataGridViewState1[State1_TemporarilyResetCurrentCell]
                    && !_dataGridViewOper[OperationInDispose])
                {
                    DataGridViewCell currentCell = CurrentCellInternal;
                    if (!EndEdit(
                        DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.CurrentCellChange,
                        validateCurrentCell ? DataGridViewValidateCellInternal.Always : DataGridViewValidateCellInternal.Never,
                        fireCellLeave: validateCurrentCell,
                        fireCellEnter: false,
                        fireRowLeave: validateCurrentCell,
                        fireRowEnter: false,
                        fireLeave: false,
                        keepFocus: EditMode != DataGridViewEditMode.EditOnEnter,
                        resetCurrentCell: false,
                        resetAnchorCell: false))
                    {
                        return false;
                    }

                    if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                    {
                        currentCell = Rows.SharedRow(oldCurrentCellY).Cells[oldCurrentCellX];
                        if (currentCell.LeaveUnsharesRowInternal(oldCurrentCellY, throughMouseClick))
                        {
                            // un-sharing the current row
                            currentCell = Rows[oldCurrentCellY].Cells[oldCurrentCellX];
                        }

                        currentCell.OnLeaveInternal(oldCurrentCellY, throughMouseClick);
                    }

                    if (validateCurrentCell)
                    {
                        if (OnRowValidating(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY))
                        {
                            if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                            {
                                // Row validation was cancelled
                                Debug.Assert(oldCurrentCellX == _ptCurrentCell.X);
                                Debug.Assert(oldCurrentCellY == _ptCurrentCell.Y);
                                OnRowEnter(
                                    ref dataGridViewCellTmp,
                                    oldCurrentCellX,
                                    oldCurrentCellY,
                                    canCreateNewRow: true,
                                    validationFailureOccurred: true);
                                if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                                {
                                    currentCell.OnEnterInternal(oldCurrentCellY, throughMouseClick);
                                    OnCellEnter(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY);
                                }
                            }

                            return false;
                        }

                        if (!IsInnerCellOutOfBounds(oldCurrentCellX, oldCurrentCellY))
                        {
                            OnRowValidated(ref dataGridViewCellTmp, oldCurrentCellX, oldCurrentCellY);
                        }
                    }
                }

                if (_ptCurrentCell.X != -1)
                {
                    _ptCurrentCell.X = -1;
                    _ptCurrentCell.Y = -1;
                    OnCurrentCellChanged(EventArgs.Empty);
                }

                if (setAnchorCellAddress)
                {
                    _ptAnchorCell.X = -1;
                    _ptAnchorCell.Y = -1;
                }

                if (_dataGridViewState1[State1_TemporarilyResetCurrentCell])
                {
                    if (EditingControl is not null)
                    {
                        if (_dataGridViewState2[State2_DiscardEditingControl])
                        {
                            _dataGridViewState2[State2_DiscardEditingControl] = false;
                        }
                        else
                        {
                            _cachedEditingControl = EditingControl;
                        }

                        EditingControl = null;
                    }
                }
                else if (oldCurrentCellX >= 0 && !_dataGridViewOper[OperationInDispose])
                {
                    Debug.Assert(oldCurrentCellY >= 0);
                    if (oldCurrentCellX < Columns.Count && oldCurrentCellY < Rows.Count)
                    {
                        InvalidateCellPrivate(oldCurrentCellX, oldCurrentCellY);
                    }

                    if (RowHeadersVisible && oldCurrentCellY < Rows.Count)
                    {
                        InvalidateCellPrivate(-1, oldCurrentCellY);
                    }
                }
            }
        }
        finally
        {
            _dataGridViewOper[OperationInCurrentCellChange] = false;
        }

        return true;
    }

    internal void SetCurrentCellAddressCoreInternal(int columnIndex,
        int rowIndex,
        bool setAnchorCellAddress,
        bool validateCurrentCell,
        bool throughMouseClick)
    {
        SetCurrentCellAddressCore(columnIndex, rowIndex, setAnchorCellAddress, validateCurrentCell, throughMouseClick);
    }

    private void SelectCellRange(int columnIndexFrom, int rowIndexFrom, int columnIndexTo, int rowIndexTo, bool select)
    {
        Debug.Assert(columnIndexFrom >= 0 && columnIndexTo >= 0);
        Debug.Assert((Columns[columnIndexFrom]).DisplayIndex <= (Columns[columnIndexTo]).DisplayIndex);
        Debug.Assert(rowIndexFrom >= 0 && rowIndexTo >= 0);
        Debug.Assert(rowIndexFrom <= rowIndexTo);
        Debug.Assert(_noSelectionChangeCount > 0);

        bool switchedToBulkPaint = false;
        if (rowIndexTo - rowIndexFrom > BulkPaintThreshold)
        {
            // Switch to batch operation
            _inBulkPaintCount++;
            switchedToBulkPaint = true;
        }

        try
        {
            // Selection and deselection are done in reverse order for perf. reasons.
            if (select)
            {
                int columnIndex = columnIndexFrom;
                do
                {
                    for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++)
                    {
                        SetSelectedCellCore(columnIndex, rowIndex, true);
                    }

                    if (columnIndex != columnIndexTo)
                    {
                        DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[columnIndex],
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.None);
                        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                        columnIndex = dataGridViewColumn.Index;
                    }
                }
                while (columnIndex != columnIndexTo);

                if (columnIndexFrom != columnIndexTo)
                {
                    for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++)
                    {
                        SetSelectedCellCore(columnIndex, rowIndex, true);
                    }
                }
            }
            else
            {
                int columnIndex = columnIndexTo;
                do
                {
                    for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--)
                    {
                        SetSelectedCellCore(columnIndex, rowIndex, false);
                    }

                    if (columnIndex != columnIndexFrom)
                    {
                        DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[columnIndex],
                            DataGridViewElementStates.Visible,
                            DataGridViewElementStates.None);
                        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                        columnIndex = dataGridViewColumn.Index;
                    }
                }
                while (columnIndex != columnIndexFrom);

                if (columnIndexFrom != columnIndexTo)
                {
                    for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--)
                    {
                        SetSelectedCellCore(columnIndex, rowIndex, false);
                    }
                }
            }
        }
        finally
        {
            if (switchedToBulkPaint)
            {
                ExitBulkPaint(-1, -1);
            }
        }
    }

    private void SelectCellUnorderedRange(int columnIndexFrom, int rowIndexFrom, int columnIndexTo, int rowIndexTo, bool select)
    {
        Debug.Assert(_noSelectionChangeCount > 0);
        int columnIndexFromTmp, rowIndexFromTmp, columnIndexToTmp, rowIndexToTmp;

        if (Columns.DisplayInOrder(columnIndexFrom, columnIndexTo))
        {
            columnIndexFromTmp = columnIndexFrom;
            columnIndexToTmp = columnIndexTo;
        }
        else
        {
            columnIndexFromTmp = columnIndexTo;
            columnIndexToTmp = columnIndexFrom;
        }

        if (rowIndexFrom < rowIndexTo)
        {
            rowIndexFromTmp = rowIndexFrom;
            rowIndexToTmp = rowIndexTo;
        }
        else
        {
            rowIndexFromTmp = rowIndexTo;
            rowIndexToTmp = rowIndexFrom;
        }

        SelectCellRange(
            columnIndexFromTmp,
            rowIndexFromTmp,
            columnIndexToTmp,
            rowIndexToTmp,
            select);
    }

    private void SelectColumnRange(int columnIndexFrom, int columnIndexTo, bool select)
    {
        Debug.Assert(columnIndexFrom >= 0 && columnIndexTo >= 0);
        Debug.Assert((Columns[columnIndexFrom]).DisplayIndex <= (Columns[columnIndexTo]).DisplayIndex);
        Debug.Assert(_noSelectionChangeCount > 0);

        int columnIndex = columnIndexFrom;
        do
        {
            if (select)
            {
                if (!_selectedBandIndexes.Contains(columnIndex))
                {
                    SetSelectedColumnCore(columnIndex, true);
                }
            }
            else
            {
                Debug.Assert(_selectedBandIndexes.Contains(columnIndex));
                SetSelectedColumnCore(columnIndex, false);
            }

            if (columnIndex != columnIndexTo)
            {
                DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(
                    Columns[columnIndex],
                    DataGridViewElementStates.Visible,
                    DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                columnIndex = dataGridViewColumn.Index;
            }
        }
        while (columnIndex != columnIndexTo);

        if (columnIndexFrom != columnIndexTo)
        {
            if (select)
            {
                if (!_selectedBandIndexes.Contains(columnIndexTo))
                {
                    SetSelectedColumnCore(columnIndexTo, true);
                }
            }
            else
            {
                Debug.Assert(_selectedBandIndexes.Contains(columnIndexTo));
                SetSelectedColumnCore(columnIndexTo, false);
            }
        }
    }

    private void SelectRowRange(int rowIndexFrom, int rowIndexTo, bool select)
    {
        Debug.Assert(rowIndexFrom >= 0 && rowIndexTo >= 0);
        Debug.Assert(rowIndexFrom <= rowIndexTo);
        Debug.Assert(_noSelectionChangeCount > 0);

        bool switchedToBulkPaint = false;
        if (rowIndexTo - rowIndexFrom > BulkPaintThreshold)
        {
            // Switch to batch operation
            _inBulkPaintCount++;
            switchedToBulkPaint = true;
        }

        try
        {
            // Selecting and deselecting rows in reverse order for perf. reasons
            if (select)
            {
                for (int rowIndex = rowIndexFrom; rowIndex <= rowIndexTo; rowIndex++)
                {
                    if ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0)
                    {
                        Debug.Assert(!_selectedBandIndexes.Contains(rowIndex));
                        SetSelectedRowCore(rowIndex, true);
                    }
                }
            }
            else
            {
                for (int rowIndex = rowIndexTo; rowIndex >= rowIndexFrom; rowIndex--)
                {
                    Debug.Assert(_selectedBandIndexes.Contains(rowIndex));
                    SetSelectedRowCore(rowIndex, false);
                }
            }
        }
        finally
        {
            if (switchedToBulkPaint)
            {
                ExitBulkPaint(-1, -1);
            }
        }
    }

    internal void SetAccessibleObjectParent(AccessibleObject editingControlAccessibleObject)
    {
        if (CurrentCell is null)
        {
            return;
        }

        editingControlAccessibleObject.SetParent(CurrentCell.AccessibilityObject);

        CurrentCell.AccessibilityObject.SetDetachableChild(editingControlAccessibleObject);
        CurrentCell.AccessibilityObject.RaiseStructureChangedEvent(StructureChangeType.StructureChangeType_ChildAdded, editingControlAccessibleObject.RuntimeId);
    }

    private bool SetAndSelectCurrentCellAddress(int columnIndex,
        int rowIndex,
        bool setAnchorCellAddress,
        bool validateCurrentCell,
        bool throughMouseClick,
        bool clearSelection,
        bool forceCurrentCellSelection)
    {
        if (!SetCurrentCellAddressCore(columnIndex, rowIndex, setAnchorCellAddress, validateCurrentCell, throughMouseClick))
        {
            return false;
        }

        if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
        {
            return false;
        }

        if (clearSelection)
        {
            ClearSelection(columnIndex, rowIndex, selectExceptionElement: true);    // we always select the new current cell when clearSelection is true
        }
        else
        {
            if (forceCurrentCellSelection)
            {
                SetSelectedElementCore(columnIndex, rowIndex, true);
            }
            else
            {
                if (MultiSelect && (_individualSelectedCells.Count + _selectedBandIndexes.Count) > 1)
                {
                    return true;   // Do not discard the multi-selection
                }

                if (_individualSelectedCells.Count == 1)
                {
                    DataGridViewCell dataGridViewCell = _individualSelectedCells.HeadCell;
                    if (dataGridViewCell.ColumnIndex != columnIndex || dataGridViewCell.RowIndex != rowIndex)
                    {
                        return true;
                    }
                }
                else if (_selectedBandIndexes.Count == 1)
                {
                    switch (SelectionMode)
                    {
                        case DataGridViewSelectionMode.FullColumnSelect:
                        case DataGridViewSelectionMode.ColumnHeaderSelect:
                            {
                                if (_selectedBandIndexes.HeadInt != columnIndex)
                                {
                                    return true;  // Do not change a single selection that does not match the new current cell
                                }

                                break;
                            }

                        case DataGridViewSelectionMode.FullRowSelect:
                        case DataGridViewSelectionMode.RowHeaderSelect:
                            {
                                if (_selectedBandIndexes.HeadInt != rowIndex)
                                {
                                    return true;  // Do not change a single selection that does not match the new current cell
                                }

                                break;
                            }
                    }
                }

                SetSelectedElementCore(columnIndex, rowIndex, true);
            }
        }

        return true;
    }

    protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
    {
        if ((specified & BoundsSpecified.Width) == BoundsSpecified.Width && width > UpperSize)
        {
            throw new ArgumentOutOfRangeException(nameof(width), width, string.Format(SR.DataGridView_SizeTooLarge, (UpperSize).ToString(CultureInfo.CurrentCulture)));
        }

        if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height && height > UpperSize)
        {
            throw new ArgumentOutOfRangeException(nameof(height), height, string.Format(SR.DataGridView_SizeTooLarge, (UpperSize).ToString(CultureInfo.CurrentCulture)));
        }

        base.SetBoundsCore(x, y, width, height, specified);
    }

    internal void SetReadOnlyCellCore(int columnIndex, int rowIndex, bool readOnly)
    {
        Debug.Assert(columnIndex >= 0 && rowIndex >= 0 && columnIndex < Columns.Count && rowIndex < Rows.Count);

        // cell's readonly state changes
        DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
        if (IsSharedCellReadOnly(dataGridViewRow.Cells[columnIndex], rowIndex) != readOnly)
        {
            DataGridViewCell dataGridViewCell = Rows[rowIndex].Cells[columnIndex];
            if (readOnly)
            {
                if ((rowState & DataGridViewElementStates.ReadOnly) == 0 && !Columns[columnIndex].ReadOnly)
                {
                    _individualReadOnlyCells.Add(dataGridViewCell);
                    dataGridViewCell.ReadOnlyInternal = true;
                }
            }
            else
            {
                if (_individualReadOnlyCells.Contains(dataGridViewCell))
                {
                    _individualReadOnlyCells.Remove(dataGridViewCell);
                }
                else
                {
                    DataGridViewCell dataGridViewCellTmp;
                    if (Columns[columnIndex].ReadOnly)
                    {
                        Columns[columnIndex].ReadOnlyInternal = false;
                        // Perf Issue: this un-shares all rows!
                        for (int row = 0; row < rowIndex; row++)
                        {
                            dataGridViewCellTmp = Rows[row].Cells[columnIndex];
                            dataGridViewCellTmp.ReadOnlyInternal = true;
                            _individualReadOnlyCells.Add(dataGridViewCellTmp);
                        }

                        for (int row = rowIndex + 1; row < Rows.Count; row++)
                        {
                            dataGridViewCellTmp = Rows[row].Cells[columnIndex];
                            dataGridViewCellTmp.ReadOnlyInternal = true;
                            _individualReadOnlyCells.Add(dataGridViewCellTmp);
                        }
                    }

                    if ((rowState & DataGridViewElementStates.ReadOnly) != 0)
                    {
                        Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, false);
                        for (int column = 0; column < columnIndex; column++)
                        {
                            dataGridViewCellTmp = Rows[rowIndex].Cells[column];
                            dataGridViewCellTmp.ReadOnlyInternal = true;
                            _individualReadOnlyCells.Add(dataGridViewCellTmp);
                        }

                        for (int column = columnIndex + 1; column < Columns.Count; column++)
                        {
                            dataGridViewCellTmp = Rows[rowIndex].Cells[column];
                            dataGridViewCellTmp.ReadOnlyInternal = true;
                            _individualReadOnlyCells.Add(dataGridViewCellTmp);
                        }
                    }
                }

                if (dataGridViewCell.ReadOnly)
                {
                    dataGridViewCell.ReadOnlyInternal = false;
                }
            }
        }
    }

    internal void SetReadOnlyColumnCore(int columnIndex, bool readOnly)
    {
        Debug.Assert(columnIndex >= 0 && columnIndex < Columns.Count);

        if (Columns[columnIndex].ReadOnly != readOnly)
        {
            // ReadOnly state of entire column changes
            if (readOnly)
            {
                // column is made read-only
                // remove individual read-only cells of this column
                try
                {
                    _dataGridViewOper[OperationInReadOnlyChange] = true;
                    RemoveIndividualReadOnlyCellsInColumn(columnIndex);
                }
                finally
                {
                    _dataGridViewOper[OperationInReadOnlyChange] = false;
                }

                Columns[columnIndex].ReadOnlyInternal = true;
            }
            else
            {
                // column is made read-write
                Columns[columnIndex].ReadOnlyInternal = false;
            }
        }
        else if (!readOnly)
        {
            // remove any potentially individual read-only cells in the column
            RemoveIndividualReadOnlyCellsInColumn(columnIndex);
        }
    }

    internal void SetReadOnlyRowCore(int rowIndex, bool readOnly)
    {
        Debug.Assert(rowIndex >= 0 && rowIndex < Rows.Count);

        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
        if (((rowState & DataGridViewElementStates.ReadOnly) != 0) != readOnly)
        {
            // ReadOnly state of entire row changes
            if (readOnly)
            {
                // row is made read-only
                // first remove individual read-only cells of this row
                try
                {
                    _dataGridViewOper[OperationInReadOnlyChange] = true;
                    RemoveIndividualReadOnlyCellsInRow(rowIndex);
                }
                finally
                {
                    _dataGridViewOper[OperationInReadOnlyChange] = false;
                }

                Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, true);
            }
            else
            {
                // row is made read-write
                Rows.SetRowState(rowIndex, DataGridViewElementStates.ReadOnly, false);
            }
        }
        else if (!readOnly)
        {
            // remove any potentially individual read-only cells in the row
            RemoveIndividualReadOnlyCellsInRow(rowIndex);
        }
    }

    protected virtual void SetSelectedCellCore(int columnIndex, int rowIndex, bool selected)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(columnIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);

        ArgumentOutOfRangeException.ThrowIfNegative(rowIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        // cell selection changes
        DataGridViewRow dataGridViewRow = Rows.SharedRow(rowIndex);
        DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
        if (IsSharedCellSelected(dataGridViewRow.Cells[columnIndex], rowIndex) != selected)
        {
            DataGridViewCell dataGridViewCell = Rows[rowIndex].Cells[columnIndex];
            if (selected)
            {
                if ((rowState & DataGridViewElementStates.Selected) == 0 && !Columns[columnIndex].Selected)
                {
                    _individualSelectedCells.Add(dataGridViewCell);
                    dataGridViewCell.SelectedInternal = true;
                }
            }
            else
            {
                if ((dataGridViewCell.State & DataGridViewElementStates.Selected) != 0)
                {
                    Debug.Assert(_individualSelectedCells.Contains(dataGridViewCell));
                    _individualSelectedCells.Remove(dataGridViewCell);
                    KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(dataGridViewCell);
                }
                else
                {
                    DataGridViewCell dataGridViewCellTmp;
                    bool switchedToBulkPaint = false;
                    if (SelectionMode == DataGridViewSelectionMode.ColumnHeaderSelect)
                    {
                        if (Rows.Count > BulkPaintThreshold)
                        {
                            _inBulkPaintCount++;
                            switchedToBulkPaint = true;
                        }

                        try
                        {
                            Debug.Assert(_selectedBandIndexes.Contains(columnIndex));
                            _selectedBandIndexes.Remove(columnIndex);
                            Columns[columnIndex].SelectedInternal = false;
                            // Perf Issue: this un-shares all rows!
                            for (int row = 0; row < rowIndex; row++)
                            {
                                dataGridViewCellTmp = Rows[row].Cells[columnIndex];
                                dataGridViewCellTmp.SelectedInternal = true;
                                _individualSelectedCells.Add(dataGridViewCellTmp);
                            }

                            for (int row = rowIndex + 1; row < Rows.Count; row++)
                            {
                                dataGridViewCellTmp = Rows[row].Cells[columnIndex];
                                dataGridViewCellTmp.SelectedInternal = true;
                                _individualSelectedCells.Add(dataGridViewCellTmp);
                            }
                        }
                        finally
                        {
                            if (switchedToBulkPaint)
                            {
                                ExitBulkPaint(columnIndex, -1);
                            }
                        }
                    }
                    else if (SelectionMode == DataGridViewSelectionMode.RowHeaderSelect)
                    {
                        if (Columns.Count > BulkPaintThreshold)
                        {
                            _inBulkPaintCount++;
                            switchedToBulkPaint = true;
                        }

                        try
                        {
                            Debug.Assert(_selectedBandIndexes.Contains(rowIndex));
                            _selectedBandIndexes.Remove(rowIndex);
                            Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, false);
                            for (int column = 0; column < columnIndex; column++)
                            {
                                dataGridViewCellTmp = Rows[rowIndex].Cells[column];
                                dataGridViewCellTmp.SelectedInternal = true;
                                _individualSelectedCells.Add(dataGridViewCellTmp);
                            }

                            for (int column = columnIndex + 1; column < Columns.Count; column++)
                            {
                                dataGridViewCellTmp = Rows[rowIndex].Cells[column];
                                dataGridViewCellTmp.SelectedInternal = true;
                                _individualSelectedCells.Add(dataGridViewCellTmp);
                            }
                        }
                        finally
                        {
                            if (switchedToBulkPaint)
                            {
                                ExitBulkPaint(-1, rowIndex);
                            }
                        }
                    }
                }

                if (dataGridViewCell.Selected)
                {
                    dataGridViewCell.SelectedInternal = false;
                    KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(dataGridViewCell);
                }
            }
        }
    }

    internal void SetSelectedCellCoreInternal(int columnIndex, int rowIndex, bool selected)
    {
        if (selected && !MultiSelect)
        {
            if (!Columns[columnIndex].Visible || (Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) == 0)
            {
                throw new InvalidOperationException(SR.DataGridView_CurrentCellCannotBeInvisible);
            }

            if (!ScrollIntoView(columnIndex, rowIndex, true))
            {
                return;
            }

            if (IsInnerCellOutOfBounds(columnIndex, rowIndex))
            {
                return;
            }
        }

        _noSelectionChangeCount++;
        try
        {
            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                    {
                        if (selected && !MultiSelect)
                        {
                            Debug.Assert(_individualSelectedCells.Count <= 1);
                            RemoveIndividuallySelectedCells();
                        }

                        SetSelectedCellCore(columnIndex, rowIndex, selected);
                        break;
                    }

                case DataGridViewSelectionMode.FullColumnSelect:
                    {
                        if (selected)
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                int bandIndex = 0;
                                while (bandIndex < _selectedBandIndexes.Count)
                                {
                                    if (_selectedBandIndexes[bandIndex] != columnIndex)
                                    {
                                        // deselect currently selected column
                                        SetSelectedColumnCore(_selectedBandIndexes[bandIndex], false);
                                    }
                                    else
                                    {
                                        bandIndex++;
                                    }
                                }
                            }

                            if (!_selectedBandIndexes.Contains(columnIndex))
                            {
                                SetSelectedColumnCore(columnIndex, true);
                            }
                        }
                        else
                        {
                            if (_selectedBandIndexes.Contains(columnIndex))
                            {
                                SetSelectedColumnCore(columnIndex, false);
                            }
                        }

                        break;
                    }

                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    {
                        if (selected)
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                if (_selectedBandIndexes.Count > 0)
                                {
                                    SetSelectedColumnCore(_selectedBandIndexes.HeadInt, false);
                                }
                                else
                                {
                                    RemoveIndividuallySelectedCells();
                                }
                            }

                            SetSelectedCellCore(columnIndex, rowIndex, true);
                        }
                        else
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                if (_selectedBandIndexes.Count > 0)
                                {
                                    SetSelectedColumnCore(_selectedBandIndexes.HeadInt, false);
                                }
                                else
                                {
                                    SetSelectedCellCore(columnIndex, rowIndex, false);
                                }
                            }
                            else
                            {
                                SetSelectedCellCore(columnIndex, rowIndex, false);
                            }
                        }

                        break;
                    }

                case DataGridViewSelectionMode.FullRowSelect:
                    {
                        if (selected)
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                int bandIndex = 0;
                                while (bandIndex < _selectedBandIndexes.Count)
                                {
                                    if (_selectedBandIndexes[bandIndex] != rowIndex)
                                    {
                                        // deselect currently selected row
                                        SetSelectedRowCore(_selectedBandIndexes[bandIndex], false);
                                    }
                                    else
                                    {
                                        bandIndex++;
                                    }
                                }
                            }

                            if ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) == 0)
                            {
                                Debug.Assert(_selectedBandIndexes.Contains(rowIndex) ==
                                             ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0));
                                SetSelectedRowCore(rowIndex, true);
                            }
                        }
                        else
                        {
                            if ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0)
                            {
                                Debug.Assert(_selectedBandIndexes.Contains(rowIndex) ==
                                             ((Rows.GetRowState(rowIndex) & DataGridViewElementStates.Selected) != 0));
                                SetSelectedRowCore(rowIndex, false);
                            }
                        }

                        break;
                    }

                case DataGridViewSelectionMode.RowHeaderSelect:
                    {
                        if (selected)
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                if (_selectedBandIndexes.Count > 0)
                                {
                                    SetSelectedRowCore(_selectedBandIndexes.HeadInt, false);
                                }
                                else
                                {
                                    RemoveIndividuallySelectedCells();
                                }
                            }

                            SetSelectedCellCore(columnIndex, rowIndex, true);
                        }
                        else
                        {
                            if (!MultiSelect)
                            {
                                Debug.Assert(_selectedBandIndexes.Count <= 1);
                                if (_selectedBandIndexes.Count > 0)
                                {
                                    SetSelectedRowCore(_selectedBandIndexes.HeadInt, false);
                                }
                                else
                                {
                                    SetSelectedCellCore(columnIndex, rowIndex, false);
                                }
                            }
                            else
                            {
                                SetSelectedCellCore(columnIndex, rowIndex, false);
                            }
                        }

                        break;
                    }
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        if (selected && !MultiSelect)
        {
            bool success = SetCurrentCellAddressCore(columnIndex, rowIndex, true, false, true);
            Debug.Assert(success);
        }
    }

    protected virtual void SetSelectedColumnCore(int columnIndex, bool selected)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(columnIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);

        _noSelectionChangeCount++;
        try
        {
            if (Columns[columnIndex].Selected != selected)
            {
                // selection of entire column changes
                if (selected)
                {
                    // column is selected
                    // remove individually selected cells of this column
                    RemoveIndividuallySelectedCellsInColumn(columnIndex);
                    Columns[columnIndex].SelectedInternal = true;
                    Debug.Assert(!_selectedBandIndexes.Contains(columnIndex));
                    _selectedBandIndexes.Add(columnIndex);
                }
                else
                {
                    // column is deselected
                    Debug.Assert(_selectedBandIndexes.Contains(columnIndex));
                    Columns[columnIndex].SelectedInternal = false;
                    _selectedBandIndexes.Remove(columnIndex);
                }
            }
            else if (!selected)
            {
                // remove any potentially individually selected cells in the column
                RemoveIndividuallySelectedCellsInColumn(columnIndex);
            }
        }
        finally
        {
            Debug.Assert(MultiSelect || _selectedBandIndexes.Count <= 1);
            NoSelectionChangeCount--;
        }
    }

    internal void SetSelectedColumnCoreInternal(int columnIndex, bool selected)
    {
        _noSelectionChangeCount++;
        try
        {
            if (!MultiSelect)
            {
                Debug.Assert(_selectedBandIndexes.Count <= 1);
                if (_selectedBandIndexes.Count > 0)
                {
                    int columnIndexSelected = _selectedBandIndexes.HeadInt;
                    if (columnIndexSelected != columnIndex)
                    {
                        SetSelectedColumnCore(columnIndexSelected, false);
                    }
                }
            }

            SetSelectedColumnCore(columnIndex, selected);
        }
        finally
        {
            NoSelectionChangeCount--;
        }
    }

    private void SetSelectedElementCore(int columnIndex, int rowIndex, bool selected)
    {
        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.CellSelect:
                {
                    SetSelectedCellCore(columnIndex, rowIndex, selected);
                    break;
                }

            case DataGridViewSelectionMode.RowHeaderSelect:
                {
                    if (columnIndex == -1)
                    {
                        SetSelectedRowCore(rowIndex, selected);
                    }
                    else
                    {
                        SetSelectedCellCore(columnIndex, rowIndex, selected);
                    }

                    break;
                }

            case DataGridViewSelectionMode.ColumnHeaderSelect:
                {
                    if (rowIndex == -1)
                    {
                        SetSelectedColumnCore(columnIndex, selected);
                    }
                    else
                    {
                        SetSelectedCellCore(columnIndex, rowIndex, selected);
                    }

                    break;
                }

            case DataGridViewSelectionMode.FullRowSelect:
                {
                    SetSelectedRowCore(rowIndex, selected);
                    break;
                }

            case DataGridViewSelectionMode.FullColumnSelect:
                {
                    SetSelectedColumnCore(columnIndex, selected);
                    break;
                }
        }
    }

    protected virtual void SetSelectedRowCore(int rowIndex, bool selected)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rowIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        _noSelectionChangeCount++;
        try
        {
            DataGridViewElementStates rowState = Rows.GetRowState(rowIndex);
            if (((rowState & DataGridViewElementStates.Selected) != 0) != selected)
            {
                // selection of entire row changes
                if (selected)
                {
                    // row is selected
                    // first remove individually selected cells of this row
                    RemoveIndividuallySelectedCellsInRow(rowIndex);
                    Debug.Assert(!_selectedBandIndexes.Contains(rowIndex));
                    _selectedBandIndexes.Add(rowIndex);
                    Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, true);
                }
                else
                {
                    // row is deselected
                    Debug.Assert(_selectedBandIndexes.Contains(rowIndex));
                    _selectedBandIndexes.Remove(rowIndex);
                    Rows.SetRowState(rowIndex, DataGridViewElementStates.Selected, false);
                }
            }
            else if (!selected)
            {
                // remove any potentially individually selected cells in the row
                RemoveIndividuallySelectedCellsInRow(rowIndex);
            }
        }
        finally
        {
            Debug.Assert(MultiSelect || _selectedBandIndexes.Count <= 1);
            NoSelectionChangeCount--;
        }
    }

    internal void SetSelectedRowCoreInternal(int rowIndex, bool selected)
    {
        _noSelectionChangeCount++;
        try
        {
            if (!MultiSelect)
            {
                Debug.Assert(_selectedBandIndexes.Count <= 1);
                if (_selectedBandIndexes.Count > 0)
                {
                    int rowIndexSelected = _selectedBandIndexes.HeadInt;
                    if (rowIndexSelected != rowIndex)
                    {
                        SetSelectedRowCore(rowIndexSelected, false);
                    }
                }
            }

            SetSelectedRowCore(rowIndex, selected);
        }
        finally
        {
            NoSelectionChangeCount--;
        }
    }

    private bool ShouldSerializeAlternatingRowsDefaultCellStyle()
    {
        DataGridViewCellStyle defaultStyle = new();
        return !AlternatingRowsDefaultCellStyle.Equals(defaultStyle);
    }

    private bool ShouldSerializeColumnHeadersDefaultCellStyle()
    {
        return !ColumnHeadersDefaultCellStyle.Equals(DefaultColumnHeadersDefaultCellStyle);
    }

    private bool ShouldSerializeDefaultCellStyle()
    {
        return !DefaultCellStyle.Equals(DefaultDefaultCellStyle);
    }

    private bool ShouldSerializeRowHeadersDefaultCellStyle()
    {
        return !RowHeadersDefaultCellStyle.Equals(DefaultRowHeadersDefaultCellStyle);
    }

    private bool ShouldSerializeRowsDefaultCellStyle()
    {
        DataGridViewCellStyle defaultStyle = new();
        return !RowsDefaultCellStyle.Equals(defaultStyle);
    }

    public virtual void Sort(DataGridViewColumn dataGridViewColumn, ListSortDirection direction)
    {
        ArgumentNullException.ThrowIfNull(dataGridViewColumn);

        if (direction is not ListSortDirection.Ascending and not ListSortDirection.Descending)
        {
            throw new InvalidEnumArgumentException(nameof(direction), (int)direction, typeof(ListSortDirection));
        }

        if (dataGridViewColumn.DataGridView != this)
        {
            throw new ArgumentException(SR.DataGridView_ColumnDoesNotBelongToDataGridView);
        }

        if (VirtualMode && !dataGridViewColumn.IsDataBound)
        {
            throw new InvalidOperationException(SR.DataGridView_OperationDisabledInVirtualMode);
        }

        SortInternal(comparer: null, dataGridViewColumn, direction);
    }

    public virtual void Sort(IComparer comparer)
    {
        ArgumentNullException.ThrowIfNull(comparer);

        if (VirtualMode)
        {
            throw new InvalidOperationException(SR.DataGridView_OperationDisabledInVirtualMode);
        }

        // can't sort a data bound dataGridView control using a comparer
        if (DataSource is not null)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotUseAComparerToSortDataGridViewWhenDataBound);
        }

        SortInternal(comparer, dataGridViewColumn: null, ListSortDirection.Ascending);
    }

    private void SortDataBoundDataGridView_PerformCheck(DataGridViewColumn dataGridViewColumn)
    {
        if (DataConnection?.List is not IBindingList ibl)
        {
            throw new InvalidOperationException(SR.DataGridView_CannotSortDataBoundDataGridViewBoundToNonIBindingList);
        }

        if (!ibl.SupportsSorting)
        {
            throw new InvalidOperationException(SR.DataGridView_IBindingListNeedsToSupportSorting);
        }

        if (!dataGridViewColumn.IsDataBound)
        {
            throw new ArgumentException(SR.DataGridView_ColumnNeedsToBeDataBoundWhenSortingDataBoundDataGridView, nameof(dataGridViewColumn));
        }
    }

    private void SortInternal(IComparer? comparer, DataGridViewColumn? dataGridViewColumn, ListSortDirection direction)
    {
        Debug.Assert(!(comparer is not null && DataSource is not null));
        Debug.Assert(direction is ListSortDirection.Ascending or ListSortDirection.Descending);

        // Exit editing mode if needed
        _ptCurrentCellCache.X = _ptCurrentCell.X;
        _ptCurrentCellCache.Y = _ptCurrentCell.Y;
        _dataGridViewOper[OperationInSort] = true;
        try
        {
            if (CurrentCell is not null)
            {
                KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(CurrentCell);
            }

            if (!SetCurrentCellAddressCore(-1, -1, true, true, false))
            {
                // Just cancel operation silently instead of throwing InvalidOperationException
                // 'finally' below resets this.dataGridViewOper[OperationInSort] to false
                return;
            }

            int firstDisplayedScrollingRowCache = DisplayedBandsInfo.FirstDisplayedScrollingRow;
            int visibleFrozenRows = Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            if (visibleFrozenRows > 0 && DataSource is null)
            {
                int rowVFIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                Debug.Assert(rowVFIndex != -1);
                Rows.SetRowState(rowVFIndex, DataGridViewElementStates.Frozen, false);
                Debug.Assert(Rows.GetRowCount(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen) == 0);
            }

            if (SortedColumn is not null
                && SortedColumn.SortMode == DataGridViewColumnSortMode.Automatic
                && SortedColumn.HasHeaderCell)
            {
                SortedColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
            }

            if (comparer is null)
            {
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SortedColumn = dataGridViewColumn;
                SortOrder = (direction == ListSortDirection.Ascending) ? SortOrder.Ascending : SortOrder.Descending;
                if (dataGridViewColumn.SortMode == DataGridViewColumnSortMode.Automatic && dataGridViewColumn.HasHeaderCell)
                {
                    dataGridViewColumn.HeaderCell.SortGlyphDirection = SortOrder;
                }
            }
            else
            {
                SortedColumn = null;
                SortOrder = SortOrder.None;
            }

            if (DataSource is null)
            {
                // Displayed rows may end up all spread out in the final layout.
                // So we simply reset their displayed state before the sort.
                UpdateRowsDisplayedState(displayed: false);

                // Callers pass non-null comparer when DataSource is null.
                Rows.Sort(comparer!, direction == ListSortDirection.Ascending);
            }
            else
            {
                // Callers pass non-null dataGridViewColumn when DataConnection is non-null.
                SortDataBoundDataGridView_PerformCheck(dataGridViewColumn!);

                // The check passed, do the sorting.
                DataConnection!.Sort(dataGridViewColumn!, direction);
            }

            if (_ptCurrentCellCache.X != -1)
            {
                if (!IsInnerCellOutOfBounds(_ptCurrentCellCache.X, _ptCurrentCellCache.Y))
                {
                    SetAndSelectCurrentCellAddress(
                        _ptCurrentCellCache.X,
                        _ptCurrentCellCache.Y,
                        setAnchorCellAddress: true,
                        validateCurrentCell: false,
                        throughMouseClick: false,
                        clearSelection: false,
                        forceCurrentCellSelection: false);
                }
            }

            if (visibleFrozenRows > 0)
            {
                int rowVIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
                Debug.Assert(rowVIndex != -1);
                while (visibleFrozenRows > 1)
                {
                    rowVIndex = Rows.GetNextRow(rowVIndex, DataGridViewElementStates.Visible);
                    Debug.Assert(rowVIndex != -1);
                    visibleFrozenRows--;
                }

                Rows.SetRowState(rowVIndex, DataGridViewElementStates.Frozen, true);
            }

            DisplayedBandsInfo.FirstDisplayedScrollingRow = firstDisplayedScrollingRowCache;
        }
        finally
        {
            Debug.Assert(_dataGridViewOper[OperationInSort]);
            _dataGridViewOper[OperationInSort] = false;
        }

        // Same effect as changing a top level cell style
        OnGlobalAutoSize();
        if (DataSource is null)
        {
            // Ensure that the Displayed states get set properly because they were wiped out by UpdateRowsDisplayedState above.
            DisplayedBandsInfo.EnsureDirtyState();
        }

        ResetUIState(useRowShortcut: false, computeVisibleRows: false);
        OnSorted(EventArgs.Empty);

        // RS4 narrator does not catch this event even though event is indeed raised.
        AccessibilityNotifyClients(AccessibleEvents.Reorder, (int)OBJECT_IDENTIFIER.OBJID_CLIENT, (int)PInvoke.CHILDID_SELF);
    }

    internal void SwapSortedRows(int rowIndex1, int rowIndex2)
    {
        Debug.Assert(rowIndex1 != NewRowIndex);
        Debug.Assert(rowIndex2 != NewRowIndex);
        if (rowIndex1 == rowIndex2)
        {
            return;
        }

        // Follow the move of the old current cell
        if (rowIndex1 == _ptCurrentCellCache.Y)
        {
            _ptCurrentCellCache.Y = rowIndex2;
        }
        else if (rowIndex2 == _ptCurrentCellCache.Y)
        {
            _ptCurrentCellCache.Y = rowIndex1;
        }

        switch (SelectionMode)
        {
            case DataGridViewSelectionMode.FullRowSelect:
            case DataGridViewSelectionMode.RowHeaderSelect:
                int row1Selected = _selectedBandIndexes.IndexOf(rowIndex1);
                int row2Selected = _selectedBandIndexes.IndexOf(rowIndex2);
                if (row1Selected != -1 && row2Selected == -1)
                {
                    _selectedBandIndexes[row1Selected] = rowIndex2;
                }
                else if (row1Selected == -1 && row2Selected != -1)
                {
                    _selectedBandIndexes[row2Selected] = rowIndex1;
                }

                if (_selectedBandSnapshotIndexes is not null)
                {
                    row1Selected = _selectedBandSnapshotIndexes.IndexOf(rowIndex1);
                    row2Selected = _selectedBandSnapshotIndexes.IndexOf(rowIndex2);
                    if (row1Selected != -1 && row2Selected == -1)
                    {
                        _selectedBandSnapshotIndexes[row1Selected] = rowIndex2;
                    }
                    else if (row1Selected == -1 && row2Selected != -1)
                    {
                        _selectedBandSnapshotIndexes[row2Selected] = rowIndex1;
                    }
                }

                break;
        }
    }

    private void DataGridViewHScrolled(object? sender, ScrollEventArgs se)
    {
        if (!Enabled)
        {
            return;
        }

        if (se.Type is ScrollEventType.SmallIncrement or ScrollEventType.SmallDecrement)
        {
            int dCols = (se.Type == ScrollEventType.SmallIncrement) ? 1 : -1;
            ScrollColumns(dCols);
            se.NewValue = HorizontalOffset;
        }
        else if (se.Type != ScrollEventType.EndScroll)
        {
            HorizontalOffset = se.NewValue;
        }
    }

    private void DataGridViewVScrolled(object? sender, ScrollEventArgs se)
    {
        if (!Enabled)
        {
            return;
        }

        /* VS Whidbey

*/

        int totalVisibleFrozenHeight = Rows.GetRowsHeight(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
        switch (se.Type)
        {
            case ScrollEventType.SmallIncrement:
                {
                    // Making sure that when the last visible scrolling row is taller than the data area, it does not get scrolled off screen
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
                    if (_vertScrollBar.Value + Rows.SharedRow(DisplayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(DisplayedBandsInfo.FirstDisplayedScrollingRow) <=
                        _vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight))
                    {
                        ScrollRowsByCount(1, ScrollEventType.SmallIncrement);
                    }

                    se.NewValue = VerticalOffset;
                    break;
                }

            case ScrollEventType.SmallDecrement:
                {
                    if (_vertScrollBar.Value != _vertScrollBar.Minimum)
                    {
                        ScrollRowsByCount(-1, ScrollEventType.SmallDecrement);
                    }

                    se.NewValue = VerticalOffset;
                    break;
                }

            case ScrollEventType.LargeIncrement:
                {
                    Debug.Assert(DisplayedBandsInfo.FirstDisplayedScrollingRow >= 0);
                    int firstDisplayedScrollingRowHeight = Rows.SharedRow(DisplayedBandsInfo.FirstDisplayedScrollingRow).GetHeight(DisplayedBandsInfo.FirstDisplayedScrollingRow);
                    VerticalOffset += Math.Max(firstDisplayedScrollingRowHeight, _vertScrollBar.LargeChange);
                    se.NewValue = VerticalOffset;
                    break;
                }

            case ScrollEventType.LargeDecrement:
                {
                    VerticalOffset -= _vertScrollBar.LargeChange;
                    se.NewValue = VerticalOffset;
                    break;
                }

            case ScrollEventType.ThumbTrack:
            case ScrollEventType.First:
            case ScrollEventType.Last:
                {
                    if (se.NewValue >= _vertScrollBar.Maximum - _vertScrollBar.LargeChange)
                    {
                        // Need to display the last scrolling row
                        VerticalOffset = _vertScrollBar.Maximum - ComputeHeightOfFittingTrailingScrollingRows(totalVisibleFrozenHeight);
                    }
                    else
                    {
                        VerticalOffset = se.NewValue;
                    }

                    break;
                }
        }
    }

    private bool TabToNextCell()
    {
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
        if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1)
        {
            return false;
        }

        int nextVisibleColumnIndex = -1;
        if (_ptCurrentCell.X != -1)
        {
            dataGridViewColumn = Columns.GetNextColumn(Columns[_ptCurrentCell.X],
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
            if (dataGridViewColumn is not null)
            {
                nextVisibleColumnIndex = dataGridViewColumn.Index;
            }
        }

        int nextVisibleRowIndex = -1;
        if (_ptCurrentCell.Y != -1)
        {
            nextVisibleRowIndex = Rows.GetNextRow(_ptCurrentCell.Y, DataGridViewElementStates.Visible);
        }

        int targetRowIndex = -1, targetColumnIndex = -1;

        _noSelectionChangeCount++;
        try
        {
            if (_ptCurrentCell.X == -1)
            {
                ClearSelection();
            }
            else
            {
                if (nextVisibleColumnIndex == -1)
                {
                    targetRowIndex = (nextVisibleRowIndex == -1) ? firstVisibleRowIndex : nextVisibleRowIndex;
                    targetColumnIndex = firstVisibleColumnIndex;
                }
                else
                {
                    targetRowIndex = _ptCurrentCell.Y;
                    targetColumnIndex = nextVisibleColumnIndex;
                }

                if (!ScrollIntoView(targetColumnIndex, targetRowIndex, true))
                {
                    return true;
                }
            }

            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if (_ptCurrentCell.X == -1)
                    {
                        if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                        {
                            return true;
                        }

                        SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                    }
                    else
                    {
                        if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex))
                        {
                            return true;
                        }

                        ClearSelection();
                        SetSelectedCellCore(targetColumnIndex, targetRowIndex, true);
                    }

                    break;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if (_ptCurrentCell.X == -1)
                    {
                        if (IsColumnOutOfBounds(firstVisibleColumnIndex))
                        {
                            return true;
                        }

                        SetSelectedColumnCore(firstVisibleColumnIndex, true);
                    }
                    else
                    {
                        if (IsColumnOutOfBounds(targetColumnIndex))
                        {
                            return true;
                        }

                        ClearSelection();
                        SetSelectedColumnCore(targetColumnIndex, true);
                    }

                    break;

                case DataGridViewSelectionMode.FullRowSelect:
                    if (_ptCurrentCell.X == -1)
                    {
                        if (IsRowOutOfBounds(firstVisibleRowIndex))
                        {
                            return true;
                        }

                        SetSelectedRowCore(firstVisibleRowIndex, true);
                    }
                    else
                    {
                        if (targetRowIndex != _ptCurrentCell.Y || MultiSelect)
                        {
                            if (IsRowOutOfBounds(targetRowIndex))
                            {
                                return true;
                            }

                            ClearSelection();
                            SetSelectedRowCore(targetRowIndex, true);
                        }
                    }

                    break;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        if (_ptCurrentCell.X == -1)
        {
            ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
            {
                return true;
            }

            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
        }
        else
        {
            if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex))
            {
                return true;
            }

            SetCurrentCellAddressCore(targetColumnIndex, targetRowIndex, true, false, false);
        }

        return true;
    }

    private bool TabToPreviousCell()
    {
        DataGridViewColumn? dataGridViewColumn = Columns.GetFirstColumn(DataGridViewElementStates.Visible);
        int firstVisibleColumnIndex = (dataGridViewColumn is null) ? -1 : dataGridViewColumn.Index;
        int firstVisibleRowIndex = Rows.GetFirstRow(DataGridViewElementStates.Visible);
        if (firstVisibleColumnIndex == -1 || firstVisibleRowIndex == -1)
        {
            return false;
        }

        int previousVisibleColumnIndex = -1;
        if (_ptCurrentCell.X != -1)
        {
            dataGridViewColumn = Columns.GetPreviousColumn(Columns[_ptCurrentCell.X],
                DataGridViewElementStates.Visible,
                DataGridViewElementStates.None);
            if (dataGridViewColumn is not null)
            {
                previousVisibleColumnIndex = dataGridViewColumn.Index;
            }
        }

        int previousVisibleRowIndex = -1;
        if (_ptCurrentCell.Y != -1)
        {
            previousVisibleRowIndex = Rows.GetPreviousRow(_ptCurrentCell.Y, DataGridViewElementStates.Visible);
        }

        dataGridViewColumn = Columns.GetLastColumn(
            DataGridViewElementStates.Visible,
            DataGridViewElementStates.None);
        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
        int lastVisibleColumnIndex = dataGridViewColumn.Index;
        int lastVisibleRowIndex = Rows.GetLastRow(DataGridViewElementStates.Visible);
        Debug.Assert(lastVisibleRowIndex != -1);

        int targetRowIndex = -1, targetColumnIndex = -1;

        _noSelectionChangeCount++;
        try
        {
            if (_ptCurrentCell.X == -1)
            {
                ClearSelection();
            }
            else
            {
                if (previousVisibleColumnIndex == -1)
                {
                    targetRowIndex = (previousVisibleRowIndex == -1) ? lastVisibleRowIndex : previousVisibleRowIndex;
                    targetColumnIndex = lastVisibleColumnIndex;
                }
                else
                {
                    targetRowIndex = _ptCurrentCell.Y;
                    targetColumnIndex = previousVisibleColumnIndex;
                }

                if (!ScrollIntoView(targetColumnIndex, targetRowIndex, true))
                {
                    return true;
                }
            }

            switch (SelectionMode)
            {
                case DataGridViewSelectionMode.CellSelect:
                case DataGridViewSelectionMode.RowHeaderSelect:
                case DataGridViewSelectionMode.ColumnHeaderSelect:
                    if (_ptCurrentCell.X == -1)
                    {
                        if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
                        {
                            return true;
                        }

                        SetSelectedCellCore(firstVisibleColumnIndex, firstVisibleRowIndex, true);
                    }
                    else
                    {
                        if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex))
                        {
                            return true;
                        }

                        ClearSelection();
                        SetSelectedCellCore(targetColumnIndex, targetRowIndex, true);
                    }

                    break;

                case DataGridViewSelectionMode.FullColumnSelect:
                    if (_ptCurrentCell.X == -1)
                    {
                        if (IsColumnOutOfBounds(firstVisibleColumnIndex))
                        {
                            return true;
                        }

                        SetSelectedColumnCore(firstVisibleColumnIndex, true);
                    }
                    else
                    {
                        if (IsColumnOutOfBounds(targetColumnIndex))
                        {
                            return true;
                        }

                        ClearSelection();
                        SetSelectedColumnCore(targetColumnIndex, true);
                    }

                    break;

                case DataGridViewSelectionMode.FullRowSelect:
                    if (_ptCurrentCell.X == -1)
                    {
                        if (IsRowOutOfBounds(firstVisibleRowIndex))
                        {
                            return true;
                        }

                        SetSelectedRowCore(firstVisibleRowIndex, true);
                    }
                    else
                    {
                        if (targetRowIndex != _ptCurrentCell.Y || MultiSelect)
                        {
                            if (IsRowOutOfBounds(targetRowIndex))
                            {
                                return true;
                            }

                            ClearSelection();
                            SetSelectedRowCore(targetRowIndex, true);
                        }
                    }

                    break;
            }
        }
        finally
        {
            NoSelectionChangeCount--;
        }

        if (_ptCurrentCell.X == -1)
        {
            ScrollIntoView(firstVisibleColumnIndex, firstVisibleRowIndex, false);
            if (IsInnerCellOutOfBounds(firstVisibleColumnIndex, firstVisibleRowIndex))
            {
                return true;
            }

            SetCurrentCellAddressCore(firstVisibleColumnIndex, firstVisibleRowIndex, true, false, false);
        }
        else
        {
            if (IsInnerCellOutOfBounds(targetColumnIndex, targetRowIndex))
            {
                return true;
            }

            SetCurrentCellAddressCore(targetColumnIndex, targetRowIndex, true, false, false);
        }

        return true;
    }

    private void UnwireEditingControlEvents()
    {
        Debug.Assert(_editingPanel is not null);
        _editingPanel.Click -= EditingControls_Click;
        _editingPanel.DoubleClick -= EditingControls_DoubleClick;
        _editingPanel.MouseClick -= EditingControls_MouseClick;
        _editingPanel.MouseDoubleClick -= EditingControls_MouseDoubleClick;
        _editingPanel.MouseDown -= EditingControls_MouseDown;
        _editingPanel.MouseEnter -= EditingControls_MouseEnter;
        _editingPanel.MouseLeave -= EditingControls_MouseLeave;
        _editingPanel.MouseMove -= EditingControls_MouseMove;
        _editingPanel.MouseUp -= EditingControls_MouseUp;

        Debug.Assert(EditingControl is not null);
        EditingControl.Click -= EditingControls_Click;
        EditingControl.DoubleClick -= EditingControls_DoubleClick;
        EditingControl.MouseClick -= EditingControls_MouseClick;
        EditingControl.MouseDoubleClick -= EditingControls_MouseDoubleClick;
        EditingControl.MouseDown -= EditingControls_MouseDown;
        EditingControl.MouseEnter -= EditingControls_MouseEnter;
        EditingControl.MouseLeave -= EditingControls_MouseLeave;
        EditingControl.MouseMove -= EditingControls_MouseMove;
        EditingControl.MouseUp -= EditingControls_MouseUp;
    }

    private void UnwireScrollBarsEvents()
    {
        if (_horizScrollBar is not null)
        {
            _horizScrollBar.MouseEnter -= ScrollBar_MouseEnter;
            _horizScrollBar.MouseLeave -= ScrollBar_MouseLeave;
        }

        if (_vertScrollBar is not null)
        {
            _vertScrollBar.MouseEnter -= ScrollBar_MouseEnter;
            _vertScrollBar.MouseLeave -= ScrollBar_MouseLeave;
        }
    }

    public void UpdateCellErrorText(int columnIndex, int rowIndex)
    {
        ArgumentOutOfRangeException.ThrowIfLessThan(columnIndex, -1);
        ArgumentOutOfRangeException.ThrowIfLessThan(rowIndex, -1);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        if (IsHandleCreated)
        {
            InvalidateCellPrivate(columnIndex, rowIndex);
        }
    }

    public void UpdateCellValue(int columnIndex, int rowIndex)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(columnIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(columnIndex, Columns.Count);

        ArgumentOutOfRangeException.ThrowIfNegative(rowIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        if (IsHandleCreated)
        {
            OnCellCommonChange(columnIndex, rowIndex);
        }
    }

    private void UpdateColumnsDisplayedState(bool displayed)
    {
        // Make sure all displayed frozen columns have their Displayed state set to this.Visible
        DataGridViewColumn? dataGridViewColumnTmp;
        int numDisplayedFrozenCols = DisplayedBandsInfo.NumDisplayedFrozenCols;
        if (numDisplayedFrozenCols > 0)
        {
            dataGridViewColumnTmp = Columns.GetFirstColumn(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            while (numDisplayedFrozenCols > 0)
            {
                Debug.Assert(dataGridViewColumnTmp is not null);
                if (dataGridViewColumnTmp.Displayed != displayed)
                {
                    dataGridViewColumnTmp.Displayed = displayed;
                    Debug.Assert(ColumnNeedsDisplayedState(dataGridViewColumnTmp));
                }

                dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen, DataGridViewElementStates.None);
                numDisplayedFrozenCols--;
            }
        }

        // Make sure all displayed scrolling columns have the Displayed state set to this.Visible
        int columnIndexTmp = DisplayedBandsInfo.FirstDisplayedScrollingCol;
        if (columnIndexTmp != -1)
        {
            int numDisplayedScrollingCols = DisplayedBandsInfo.NumDisplayedScrollingCols;
            Debug.Assert(numDisplayedScrollingCols > 0);
            dataGridViewColumnTmp = Columns[columnIndexTmp];
            while (numDisplayedScrollingCols > 0)
            {
                Debug.Assert(dataGridViewColumnTmp is not null);
                if (dataGridViewColumnTmp.Displayed != displayed)
                {
                    dataGridViewColumnTmp.Displayed = displayed;
                    Debug.Assert(ColumnNeedsDisplayedState(dataGridViewColumnTmp));
                }

                dataGridViewColumnTmp = Columns.GetNextColumn(dataGridViewColumnTmp, DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                numDisplayedScrollingCols--;
            }
        }
    }

    public void UpdateRowErrorText(int rowIndex)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rowIndex);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        if (IsHandleCreated && _layout.RowHeadersVisible)
        {
            InvalidateCellPrivate(-1, rowIndex);
        }
    }

    public void UpdateRowErrorText(int rowIndexStart, int rowIndexEnd)
    {
        ArgumentOutOfRangeException.ThrowIfNegative(rowIndexStart);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndexStart, Rows.Count);

        ArgumentOutOfRangeException.ThrowIfNegative(rowIndexEnd);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndexEnd, Rows.Count);

        ArgumentOutOfRangeException.ThrowIfLessThan(rowIndexEnd, rowIndexStart);

        if (IsHandleCreated && _layout.RowHeadersVisible)
        {
            Rectangle rectUpper = GetCellAdjustedDisplayRectangle(-1, rowIndexStart, true);
            Rectangle rectLower = GetCellAdjustedDisplayRectangle(-1, rowIndexEnd, true);
            if (rectUpper.IsEmpty || rectLower.IsEmpty)
            {
                if (!rectUpper.IsEmpty || !rectLower.IsEmpty)
                {
                    Invalidate(_layout.RowHeaders);
                }
            }
            else
            {
                Invalidate(Rectangle.Union(rectUpper, rectLower));
            }
        }
    }

    public void UpdateRowHeightInfo(int rowIndex, bool updateToEnd)
    {
        UpdateRowHeightInfoPrivate(rowIndex, updateToEnd, invalidInAdjustFillingColumns: true);
    }

    private void UpdateRowHeightInfoPrivate(int rowIndex, bool updateToEnd, bool invalidInAdjustFillingColumns)
    {
        ArgumentOutOfRangeException.ThrowIfLessThan(rowIndex, updateToEnd ? 0 : -1);
        ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(rowIndex, Rows.Count);

        Rows.InvalidateCachedRowsHeights();

        bool rowVisible = (rowIndex >= 0 && (Rows.GetRowState(rowIndex) & DataGridViewElementStates.Visible) != 0);

        // don't do any layout logic if the handled was not created already
        if (IsHandleCreated && (rowIndex == -1 || rowVisible))
        {
            if (updateToEnd)
            {
                int oldFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;

                if (AutoSize)
                {
                    LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Rows);
                }
                else
                {
                    PerformLayoutPrivate(
                        useRowShortcut: false,
                        computeVisibleRows: false,
                        invalidInAdjustFillingColumns,
                        repositionEditingControl: false);
                }

                Rectangle bottomArea = _layout.Data;
                if (_layout.RowHeadersVisible)
                {
                    bottomArea = Rectangle.Union(bottomArea, _layout.RowHeaders);
                }
                else if (SingleVerticalBorderAdded)
                {
                    if (!RightToLeftInternal)
                    {
                        bottomArea.X--;
                    }

                    bottomArea.Width++;
                }

                if (!rowVisible)
                {
                    rowIndex = Rows.GetNextRow(rowIndex, DataGridViewElementStates.Visible);
                }

                if (rowIndex != -1)
                {
                    int topEdge = GetRowYFromIndex(oldFirstVisibleScrollingRow == DisplayedBandsInfo.FirstDisplayedScrollingRow ? rowIndex : DisplayedBandsInfo.FirstDisplayedScrollingRow);
                    bottomArea.Height -= bottomArea.Y - topEdge;
                    bottomArea.Y = topEdge;
                    Invalidate(bottomArea);
                }

                if (EditingControl is not null)
                {
                    PositionEditingControl(setLocation: true, setSize: true, setFocus: false);
                }
            }
            else
            {
                if (rowIndex == -1)
                {
                    if (AutoSize)
                    {
                        LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Rows);
                    }
                    else
                    {
                        PerformLayoutPrivate(
                            useRowShortcut: false,
                            computeVisibleRows: false,
                            invalidInAdjustFillingColumns,
                            repositionEditingControl: false);
                    }

                    Invalidate();
                }
                else
                {
                    Debug.Assert(rowVisible);
                    int oldFirstVisibleScrollingRow = DisplayedBandsInfo.FirstDisplayedScrollingRow;

                    if (_inBulkLayoutCount == 0)
                    {
                        if (AutoSize)
                        {
                            LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Rows);
                        }
                        else
                        {
                            PerformLayoutPrivate(
                                useRowShortcut: false,
                                computeVisibleRows: false,
                                invalidInAdjustFillingColumns,
                                repositionEditingControl: false);
                        }
                    }

                    if (_inBulkPaintCount == 0)
                    {
                        Rectangle bottomArea = _layout.Data;
                        if (_layout.RowHeadersVisible)
                        {
                            bottomArea = Rectangle.Union(bottomArea, _layout.RowHeaders);
                        }
                        else if (SingleVerticalBorderAdded)
                        {
                            if (!RightToLeftInternal)
                            {
                                bottomArea.X--;
                            }

                            bottomArea.Width++;
                        }

                        int topEdge = GetRowYFromIndex(oldFirstVisibleScrollingRow == DisplayedBandsInfo.FirstDisplayedScrollingRow ? rowIndex : DisplayedBandsInfo.FirstDisplayedScrollingRow);
                        bottomArea.Height -= bottomArea.Y - topEdge;
                        bottomArea.Y = topEdge;
                        Invalidate(bottomArea);
                    }
                }

                if (EditingControl is not null)
                {
                    PositionEditingControl(rowIndex == -1 || _ptCurrentCell.Y != rowIndex, true, false);
                }
            }

            UpdateMouseEnteredCell(hti: null, e: null);
        }
    }

    private void UpdateRowsDisplayedState(bool displayed)
    {
        // Make sure all displayed frozen rows have their Displayed state set to 'displayed'
        int rowIndexTmp, numDisplayedFrozenRows = DisplayedBandsInfo.NumDisplayedFrozenRows;
        if (numDisplayedFrozenRows > 0)
        {
            rowIndexTmp = Rows.GetFirstRow(DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
            while (numDisplayedFrozenRows > 0)
            {
                Debug.Assert(rowIndexTmp != -1);
                if (((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) == displayed)
                {
                    Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, displayed);
                }

                rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible | DataGridViewElementStates.Frozen);
                numDisplayedFrozenRows--;
            }
        }

        // Make sure all displayed scrolling rows have their Displayed state set to 'displayed'
        rowIndexTmp = DisplayedBandsInfo.FirstDisplayedScrollingRow;
        if (rowIndexTmp > -1)
        {
            int numDisplayedScrollingRows = DisplayedBandsInfo.NumDisplayedScrollingRows;
            Debug.Assert(numDisplayedScrollingRows > 0);
            while (numDisplayedScrollingRows > 0)
            {
                Debug.Assert(rowIndexTmp != -1);
                if (((Rows.GetRowState(rowIndexTmp) & DataGridViewElementStates.Displayed) == 0) == displayed)
                {
                    Rows.SetRowState(rowIndexTmp, DataGridViewElementStates.Displayed, displayed);
                }

                rowIndexTmp = Rows.GetNextRow(rowIndexTmp, DataGridViewElementStates.Visible);
                numDisplayedScrollingRows--;
            }
        }
    }

    private void UpdateMouseEnteredCell(HitTestInfo? hti, MouseEventArgs? e)
    {
        // Don't force handle creation.
        if (!IsHandleCreated)
        {
            return;
        }

        Point ptMouse = PointToClient(MousePosition);
        HitTestInfo htiToUse = hti ?? HitTest(ptMouse.X, ptMouse.Y);

        if (htiToUse.Type is not DataGridViewHitTestType.None
            and not DataGridViewHitTestType.HorizontalScrollBar
            and not DataGridViewHitTestType.VerticalScrollBar)
        {
            if (_ptMouseEnteredCell.X != htiToUse._col || _ptMouseEnteredCell.Y != htiToUse._row)
            {
                DataGridViewCellEventArgs dgvce;
                if (_ptMouseEnteredCell.X >= -1
                    && _ptMouseEnteredCell.X < Columns.Count
                    && _ptMouseEnteredCell.Y >= -1
                    && _ptMouseEnteredCell.Y < Rows.Count)
                {
                    dgvce = new DataGridViewCellEventArgs(_ptMouseEnteredCell.X, _ptMouseEnteredCell.Y);
                    OnCellMouseLeave(dgvce);
                }

                dgvce = new DataGridViewCellEventArgs(htiToUse._col, htiToUse._row);
                OnCellMouseEnter(dgvce);
            }

            if (e is not null)
            {
                int mouseX = e.X - htiToUse.ColumnX;
                if (RightToLeftInternal)
                {
                    mouseX += ((htiToUse._col == -1) ? RowHeadersWidth : Columns[htiToUse._col].Thickness);
                }

                DataGridViewCellMouseEventArgs dgvcme = new(htiToUse._col, htiToUse._row, mouseX, e.Y - htiToUse.RowY, e);
                OnCellMouseMove(dgvcme);
            }
        }
        else if (_ptMouseEnteredCell.X != -2)
        {
            if (_ptMouseEnteredCell.X >= -1
                && _ptMouseEnteredCell.X < Columns.Count
                && _ptMouseEnteredCell.Y >= -1
                && _ptMouseEnteredCell.Y < Rows.Count)
            {
                DataGridViewCellEventArgs dgvce = new(_ptMouseEnteredCell.X, _ptMouseEnteredCell.Y);
                OnCellMouseLeave(dgvce);
            }
            else
            {
                _ptMouseEnteredCell.X = _ptMouseEnteredCell.Y = -2;
            }
        }
    }

    private void UpdateSelectedCellsBlock(
        int anchorColumnIndex,
        ref int oldEdgeColumnIndex,
        int newEdgeColumnIndex,
        int anchorRowIndex,
        ref int oldEdgeRowIndex,
        int newEdgeRowIndex)
    {
        Debug.Assert(anchorColumnIndex >= 0);
        Debug.Assert(anchorRowIndex >= 0);
        Debug.Assert(newEdgeColumnIndex >= 0);
        Debug.Assert(newEdgeRowIndex >= 0);
        Debug.Assert(_noSelectionChangeCount > 0);

        if ((Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex) && Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex))
            || (Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex) && Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex))
            || (anchorRowIndex < oldEdgeRowIndex && newEdgeRowIndex < anchorRowIndex)
            || (oldEdgeRowIndex < anchorRowIndex && anchorRowIndex < newEdgeRowIndex))
        {
            // deselecting all selected block
            SelectCellUnorderedRange(anchorColumnIndex, anchorRowIndex, oldEdgeColumnIndex, oldEdgeRowIndex, false);
            // and reselecting new block
            SelectCellUnorderedRange(anchorColumnIndex, anchorRowIndex, newEdgeColumnIndex, newEdgeRowIndex, true);
            oldEdgeColumnIndex = newEdgeColumnIndex;
            oldEdgeRowIndex = newEdgeRowIndex;
            return;
        }

        if (Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex)
            && (Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex) || anchorColumnIndex == oldEdgeColumnIndex)
            && oldEdgeRowIndex == newEdgeRowIndex)
        {
            // h1
            DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
            if (anchorRowIndex <= newEdgeRowIndex)
            {
                SelectCellRange(
                    dataGridViewColumn.Index,
                    anchorRowIndex,
                    newEdgeColumnIndex,
                    newEdgeRowIndex,
                    select: true);
            }
            else
            {
                // newEdgeRowIndex < anchorRowIndex
                SelectCellRange(
                    dataGridViewColumn.Index,
                    newEdgeRowIndex,
                    newEdgeColumnIndex,
                    anchorRowIndex,
                    select: true);
            }
        }
        else if (Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex)
            && (Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex) || oldEdgeColumnIndex == anchorColumnIndex)
            && oldEdgeRowIndex == newEdgeRowIndex)
        {
            // h2
            DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
            if (anchorRowIndex <= newEdgeRowIndex)
            {
                SelectCellRange(
                    newEdgeColumnIndex,
                    anchorRowIndex,
                    dataGridViewColumn.Index,
                    newEdgeRowIndex,
                    select: true);
            }
            else
            {
                // newEdgeRowIndex < anchorRowIndex
                SelectCellRange(
                    newEdgeColumnIndex,
                    newEdgeRowIndex,
                    dataGridViewColumn.Index,
                    anchorRowIndex,
                    select: true);
            }
        }
        else if (newEdgeRowIndex > oldEdgeRowIndex && anchorRowIndex <= oldEdgeRowIndex && newEdgeColumnIndex == oldEdgeColumnIndex)
        {
            // h3
            if (Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || anchorColumnIndex == newEdgeColumnIndex)
            {
                SelectCellRange(
                    anchorColumnIndex,
                    Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                    newEdgeColumnIndex,
                    newEdgeRowIndex,
                    true);
            }
            else
            {
                // newEdgeColumnIndex before anchorColumnIndex
                SelectCellRange(
                    newEdgeColumnIndex,
                    Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                    anchorColumnIndex,
                    newEdgeRowIndex,
                    true);
            }
        }
        else if (newEdgeRowIndex < oldEdgeRowIndex && oldEdgeRowIndex <= anchorRowIndex && newEdgeColumnIndex == oldEdgeColumnIndex)
        {
            // h4
            if (Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || anchorColumnIndex == newEdgeColumnIndex)
            {
                SelectCellRange(
                    anchorColumnIndex,
                    newEdgeRowIndex,
                    newEdgeColumnIndex,
                    Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                    select: true);
            }
            else
            {
                // newEdgeColumnIndex before anchorColumnIndex
                SelectCellRange(
                    newEdgeColumnIndex,
                    newEdgeRowIndex,
                    anchorColumnIndex,
                    Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                    select: true);
            }
        }
        else if (Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex)
            && !Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex)
            && newEdgeRowIndex > oldEdgeRowIndex && anchorRowIndex <= oldEdgeRowIndex)
        {
            // h5
            DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
            SelectCellRange(
                dataGridViewColumn.Index,
                anchorRowIndex,
                newEdgeColumnIndex,
                oldEdgeRowIndex,
                select: true);
            SelectCellRange(
                anchorColumnIndex,
                Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                newEdgeColumnIndex,
                newEdgeRowIndex,
                select: true);
        }
        else if (Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex)
            && newEdgeRowIndex < oldEdgeRowIndex && oldEdgeRowIndex <= anchorRowIndex)
        {
            if (!Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex))
            {
                // h6
                DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectCellRange(
                    dataGridViewColumn.Index,
                    oldEdgeRowIndex,
                    newEdgeColumnIndex,
                    anchorRowIndex,
                    select: true);
                SelectCellRange(
                    anchorColumnIndex,
                    newEdgeRowIndex,
                    newEdgeColumnIndex,
                    Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                    select: true);
            }
            else
            {
                if (!Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex))
                {
                    if (anchorRowIndex == oldEdgeRowIndex)
                    {
                        // g2
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            anchorRowIndex,
                            anchorColumnIndex,
                            oldEdgeRowIndex,
                            select: false);
                        SelectCellRange(
                            newEdgeColumnIndex,
                            newEdgeRowIndex,
                            anchorColumnIndex,
                            anchorRowIndex,
                            select: true);
                    }
                    else
                    {
                        // b4
                        DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                        Debug.Assert(oldEdgeRowIndex < anchorRowIndex);
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            oldEdgeRowIndex,
                            dataGridViewColumn.Index,
                            anchorRowIndex,
                            select: false);
                        SelectCellRange(
                            newEdgeColumnIndex,
                            newEdgeRowIndex,
                            anchorColumnIndex,
                            Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                            select: true);
                    }
                }
            }
        }
        else if (Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex)
            && newEdgeRowIndex < oldEdgeRowIndex && anchorRowIndex >= oldEdgeRowIndex)
        {
            if (!Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex))
            {
                // h7
                DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                SelectCellRange(
                    newEdgeColumnIndex,
                    oldEdgeRowIndex,
                    dataGridViewColumn.Index,
                    anchorRowIndex,
                    select: true);
                SelectCellRange(
                    newEdgeColumnIndex,
                    newEdgeRowIndex,
                    anchorColumnIndex,
                    Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                    select: true);
            }
            else
            {
                if (Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex))
                {
                    // a4
                    DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                    ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                    Debug.Assert(oldEdgeRowIndex <= anchorRowIndex);
                    SelectCellRange(
                        dataGridViewColumn.Index,
                        oldEdgeRowIndex,
                        oldEdgeColumnIndex,
                        anchorRowIndex,
                        select: false);
                    SelectCellRange(
                        anchorColumnIndex,
                        newEdgeRowIndex,
                        newEdgeColumnIndex,
                        Rows.GetPreviousRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                        select: true);
                }
                else
                {
                    // g8
                    SelectCellRange(
                        anchorColumnIndex,
                        oldEdgeRowIndex,
                        oldEdgeColumnIndex,
                        anchorRowIndex,
                        select: false);
                    SelectCellRange(
                        newEdgeColumnIndex,
                        newEdgeRowIndex,
                        anchorColumnIndex,
                        anchorRowIndex,
                        select: true);
                }
            }
        }
        else if (Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex)
            && !Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex)
            && newEdgeRowIndex > oldEdgeRowIndex && anchorRowIndex <= oldEdgeRowIndex)
        {
            // h8
            DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
            ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
            SelectCellRange(
                newEdgeColumnIndex,
                anchorRowIndex,
                dataGridViewColumn.Index,
                oldEdgeRowIndex,
                select: true);
            SelectCellRange(
                newEdgeColumnIndex,
                Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                anchorColumnIndex,
                newEdgeRowIndex,
                select: true);
        }
        else if (Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex))
        {
            DataGridViewColumn? dataGridViewColumn = Columns.GetNextColumn(Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
            if (newEdgeRowIndex == oldEdgeRowIndex)
            {
                if (Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex))
                {
                    // a1
                    ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                    if (oldEdgeRowIndex > anchorRowIndex)
                    {
                        SelectCellRange(
                            dataGridViewColumn.Index,
                            anchorRowIndex,
                            oldEdgeColumnIndex,
                            oldEdgeRowIndex,
                            select: false);
                    }
                    else
                    {
                        SelectCellRange(
                            dataGridViewColumn.Index,
                            oldEdgeRowIndex,
                            oldEdgeColumnIndex,
                            anchorRowIndex,
                            select: false);
                    }
                }
            }
            else if (newEdgeRowIndex < oldEdgeRowIndex)
            {
                if (oldEdgeRowIndex > anchorRowIndex)
                {
                    if (Columns.DisplayInOrder(newEdgeColumnIndex, oldEdgeColumnIndex))
                    {
                        if (anchorRowIndex <= newEdgeRowIndex)
                        {
                            if (Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex))
                            {
                                if (!Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex))
                                {
                                    // a2
                                    ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                                    SelectCellRange(
                                        dataGridViewColumn.Index,
                                        anchorRowIndex,
                                        oldEdgeColumnIndex,
                                        oldEdgeRowIndex,
                                        select: false);
                                    SelectCellRange(
                                        anchorColumnIndex,
                                        Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                                        newEdgeColumnIndex,
                                        oldEdgeRowIndex,
                                        select: false);
                                }
                            }
                            else
                            {
                                // d3
                                dataGridViewColumn = Columns.GetPreviousColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                                SelectCellRange(
                                    oldEdgeColumnIndex,
                                    Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                                    anchorColumnIndex,
                                    oldEdgeRowIndex,
                                    select: false);
                                SelectCellRange(
                                    newEdgeColumnIndex,
                                    anchorRowIndex,
                                    dataGridViewColumn.Index,
                                    newEdgeRowIndex,
                                    select: true);
                            }
                        }
                    }
                }
            }
            else if (newEdgeRowIndex > oldEdgeRowIndex)
            {
                if (oldEdgeRowIndex < anchorRowIndex)
                {
                    if ((Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex))
                        && newEdgeRowIndex <= anchorRowIndex)
                    {
                        // a3
                        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                        SelectCellRange(
                            dataGridViewColumn.Index,
                            oldEdgeRowIndex,
                            oldEdgeColumnIndex,
                            anchorRowIndex,
                            select: false);
                        SelectCellRange(
                            anchorColumnIndex,
                            oldEdgeRowIndex,
                            newEdgeColumnIndex,
                            Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                            select: false);
                    }
                    else
                    {
                        if (!Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex))
                        {
                            if (newEdgeRowIndex <= anchorRowIndex)
                            {
                                // c3
                                dataGridViewColumn = Columns.GetPreviousColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                                SelectCellRange(
                                    oldEdgeColumnIndex,
                                    oldEdgeRowIndex,
                                    anchorColumnIndex,
                                    Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                                    select: false);
                                SelectCellRange(
                                    newEdgeColumnIndex,
                                    newEdgeRowIndex,
                                    dataGridViewColumn.Index,
                                    anchorRowIndex,
                                    select: true);
                            }
                        }
                    }
                }
                else
                {
                    if (Columns.DisplayInOrder(anchorColumnIndex, newEdgeColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex))
                    {
                        // a5
                        Debug.Assert(oldEdgeRowIndex >= anchorRowIndex);
                        ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                        SelectCellRange(
                            dataGridViewColumn.Index,
                            anchorRowIndex,
                            oldEdgeColumnIndex,
                            oldEdgeRowIndex,
                            select: false);
                        SelectCellRange(
                            anchorColumnIndex,
                            Rows.GetNextRow(anchorRowIndex, DataGridViewElementStates.Visible),
                            newEdgeColumnIndex,
                            newEdgeRowIndex,
                            select: true);
                    }
                }
            }
        }
        else if (Columns.DisplayInOrder(oldEdgeColumnIndex, newEdgeColumnIndex))
        {
            DataGridViewColumn? dataGridViewColumn = Columns.GetPreviousColumn(Columns[newEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
            if (newEdgeRowIndex == oldEdgeRowIndex)
            {
                if (Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (newEdgeColumnIndex == anchorColumnIndex))
                {
                    // b1
                    ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                    if (oldEdgeRowIndex > anchorRowIndex)
                    {
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            anchorRowIndex,
                            dataGridViewColumn.Index,
                            oldEdgeRowIndex,
                            select: false);
                    }
                    else
                    {
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            oldEdgeRowIndex,
                            dataGridViewColumn.Index,
                            anchorRowIndex,
                            select: false);
                    }
                }
            }
            else if (newEdgeRowIndex < oldEdgeRowIndex)
            {
                if (oldEdgeRowIndex > anchorRowIndex)
                {
                    if ((Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (newEdgeColumnIndex == anchorColumnIndex))
                        && newEdgeRowIndex >= anchorRowIndex
                        && dataGridViewColumn is not null)
                    {
                        // b2
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            anchorRowIndex,
                            dataGridViewColumn.Index,
                            oldEdgeRowIndex,
                            select: false);
                        SelectCellRange(
                            newEdgeColumnIndex,
                            Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                            anchorColumnIndex,
                            oldEdgeRowIndex,
                            select: false);
                    }
                    else
                    {
                        if (!Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex))
                        {
                            if (newEdgeRowIndex >= anchorRowIndex)
                            {
                                // d2
                                dataGridViewColumn = Columns.GetNextColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                                SelectCellRange(
                                    anchorColumnIndex,
                                    Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                                    oldEdgeColumnIndex,
                                    oldEdgeRowIndex,
                                    select: false);
                                SelectCellRange(
                                    dataGridViewColumn.Index,
                                    anchorRowIndex,
                                    newEdgeColumnIndex,
                                    newEdgeRowIndex,
                                    select: true);
                            }
                        }
                    }
                }
            }
            else if (newEdgeRowIndex > oldEdgeRowIndex)
            {
                if (oldEdgeRowIndex < anchorRowIndex)
                {
                    if ((Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex))
                        && newEdgeRowIndex <= anchorRowIndex
                        && dataGridViewColumn is not null)
                    {
                        // b3
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            oldEdgeRowIndex,
                            dataGridViewColumn.Index,
                            anchorRowIndex,
                            select: false);
                        SelectCellRange(
                            newEdgeColumnIndex,
                            oldEdgeRowIndex,
                            anchorColumnIndex,
                            Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                            select: false);
                    }
                    else
                    {
                        if (!Columns.DisplayInOrder(oldEdgeColumnIndex, anchorColumnIndex))
                        {
                            if (newEdgeRowIndex <= anchorRowIndex)
                            {
                                // c2
                                dataGridViewColumn = Columns.GetNextColumn(Columns[oldEdgeColumnIndex], DataGridViewElementStates.Visible, DataGridViewElementStates.None);
                                ThrowInvalidOperationExceptionIfNull(dataGridViewColumn);
                                SelectCellRange(
                                    anchorColumnIndex,
                                    oldEdgeRowIndex,
                                    oldEdgeColumnIndex,
                                    Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                                    select: false);
                                SelectCellRange(
                                    dataGridViewColumn.Index,
                                    newEdgeRowIndex,
                                    newEdgeColumnIndex,
                                    anchorRowIndex,
                                    select: true);
                            }
                        }
                    }
                }
                else
                {
                    if ((Columns.DisplayInOrder(newEdgeColumnIndex, anchorColumnIndex) || (anchorColumnIndex == newEdgeColumnIndex))
                        && dataGridViewColumn is not null)
                    {
                        // b5
                        Debug.Assert(oldEdgeRowIndex >= anchorRowIndex);
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            anchorRowIndex,
                            dataGridViewColumn.Index,
                            oldEdgeRowIndex,
                            select: false);
                        SelectCellRange(
                            newEdgeColumnIndex,
                            Rows.GetNextRow(oldEdgeRowIndex, DataGridViewElementStates.Visible),
                            anchorColumnIndex,
                            newEdgeRowIndex,
                            select: true);
                    }
                }
            }
        }
        else if (newEdgeRowIndex > oldEdgeRowIndex)
        {
            if (newEdgeColumnIndex == oldEdgeColumnIndex)
            {
                if (newEdgeRowIndex <= anchorRowIndex)
                {
                    // c1
                    if (Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex))
                    {
                        SelectCellRange(
                            anchorColumnIndex,
                            oldEdgeRowIndex,
                            oldEdgeColumnIndex,
                            Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                            select: false);
                    }
                    else
                    {
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            oldEdgeRowIndex,
                            anchorColumnIndex,
                            Rows.GetPreviousRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                            select: false);
                    }
                }
            }
        }
        else if (newEdgeRowIndex < oldEdgeRowIndex)
        {
            if (newEdgeColumnIndex == oldEdgeColumnIndex)
            {
                if (newEdgeRowIndex >= anchorRowIndex)
                {
                    // d1
                    if (Columns.DisplayInOrder(anchorColumnIndex, oldEdgeColumnIndex))
                    {
                        SelectCellRange(
                            anchorColumnIndex,
                            Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                            oldEdgeColumnIndex,
                            oldEdgeRowIndex,
                            select: false);
                    }
                    else
                    {
                        SelectCellRange(
                            oldEdgeColumnIndex,
                            Rows.GetNextRow(newEdgeRowIndex, DataGridViewElementStates.Visible),
                            anchorColumnIndex,
                            oldEdgeRowIndex,
                            select: false);
                    }
                }
            }
        }

        oldEdgeColumnIndex = newEdgeColumnIndex;
        oldEdgeRowIndex = newEdgeRowIndex;
    }

    private void VertScrollTimer_Tick(object? sender, EventArgs e)
    {
        BeginInvoke(new MethodInvoker(VertScrollTimerHandler));
    }

    private void VertScrollTimerHandler()
    {
        // Why would the vertical scroll timer be enabled when vertical selection is not occurring
        Debug.Assert(_dataGridViewOper[OperationTrackRowSelect] || _dataGridViewOper[OperationTrackCellSelect]);

        Point ptMouse = PointToClient(MousePosition);
        HitTestInfo hti = HitTest(ptMouse.X, ptMouse.Y);
        int mouseX = ptMouse.X, mouseY = ptMouse.Y;

        if (GetOutOfBoundCorrectedHitTestInfo(ref hti, ref mouseX, ref mouseY, out int xOffset, out int yOffset))
        {
            if (yOffset != 0)
            {
                int absYOffset = Math.Abs(yOffset), normOffset = yOffset / absYOffset;
                ScrollRowsByCount(normOffset, normOffset < 0 ? ScrollEventType.SmallDecrement : ScrollEventType.SmallIncrement);

                if (_vertScrollTimer is not null)
                {
                    _vertScrollTimer.Interval = GetRowScrollRate(absYOffset);
                }

                if (_dataGridViewOper[OperationTrackRowSelect])
                {
                    hti = HitTest(mouseX, ptMouse.Y - yOffset - normOffset);
                    if (hti._row >= 0)
                    {
                        OnRowSelectMouseMove(hti);
                    }
                }
                else if (_dataGridViewOper[OperationTrackCellSelect])
                {
                    hti = xOffset != 0
                        ? HitTest(ptMouse.X - xOffset - (xOffset / Math.Abs(xOffset)), ptMouse.Y - yOffset - normOffset)
                        : HitTest(mouseX, ptMouse.Y - yOffset - normOffset);

                    if (hti._col >= 0 && hti._row >= 0)
                    {
                        OnCellSelectMouseMove(hti);
                    }
                }
            }
            else
            {
                if (_dataGridViewOper[OperationTrackRowSelect] && hti._row >= 0)
                {
                    OnRowSelectMouseMove(hti);
                }
                else if (_dataGridViewOper[OperationTrackCellSelect] && hti._col >= 0 && hti._row >= 0)
                {
                    OnCellSelectMouseMove(hti);
                }

                VertScrollTimer.Enabled = false;
            }
        }
    }

    private void WireEditingControlEvents()
    {
        Debug.Assert(_editingPanel is not null);
        _editingPanel.Click += EditingControls_Click;
        _editingPanel.DoubleClick += EditingControls_DoubleClick;
        _editingPanel.MouseClick += EditingControls_MouseClick;
        _editingPanel.MouseDoubleClick += EditingControls_MouseDoubleClick;
        _editingPanel.MouseDown += EditingControls_MouseDown;
        _editingPanel.MouseEnter += EditingControls_MouseEnter;
        _editingPanel.MouseLeave += EditingControls_MouseLeave;
        _editingPanel.MouseMove += EditingControls_MouseMove;
        _editingPanel.MouseUp += EditingControls_MouseUp;

        Debug.Assert(EditingControl is not null);
        EditingControl.Click += EditingControls_Click;
        EditingControl.DoubleClick += EditingControls_DoubleClick;
        EditingControl.MouseClick += EditingControls_MouseClick;
        EditingControl.MouseDoubleClick += EditingControls_MouseDoubleClick;
        EditingControl.MouseDown += EditingControls_MouseDown;
        EditingControl.MouseEnter += EditingControls_MouseEnter;
        EditingControl.MouseLeave += EditingControls_MouseLeave;
        EditingControl.MouseMove += EditingControls_MouseMove;
        EditingControl.MouseUp += EditingControls_MouseUp;
    }

    private void WireScrollBarsEvents()
    {
        if (_horizScrollBar is not null)
        {
            _horizScrollBar.MouseEnter += ScrollBar_MouseEnter;
            _horizScrollBar.MouseLeave += ScrollBar_MouseLeave;
        }

        if (_vertScrollBar is not null)
        {
            _vertScrollBar.MouseEnter += ScrollBar_MouseEnter;
            _vertScrollBar.MouseLeave += ScrollBar_MouseLeave;
        }
    }

    /// <summary>
    ///  Handles the WM_CONTEXTMENU message
    /// </summary>
    internal override void WmContextMenu(ref Message m)
    {
        ContextMenuStrip? contextMenuStrip;
        Point client;
        bool keyboardActivated = false;

        // lparam will be -1 when the user invokes the context menu with the keyboard.
        if (m.LParamInternal == -1)
        {
            keyboardActivated = true;
            client = new Point(Width / 2, Height / 2);
            contextMenuStrip = ContextMenuStrip;
        }
        else
        {
            client = PointToClient(PARAM.ToPoint(m.LParamInternal));
            HitTestInfo hti = HitTest(client.X, client.Y);
            DataGridViewCell? dataGridViewCell = null;
            switch (hti.Type)
            {
                case DataGridViewHitTestType.Cell:
                    dataGridViewCell = Rows.SharedRow(hti._row).Cells[hti._col];
                    break;
                case DataGridViewHitTestType.ColumnHeader:
                    Debug.Assert(hti._row == -1);
                    dataGridViewCell = Columns[hti._col].HeaderCell;
                    break;
                case DataGridViewHitTestType.RowHeader:
                    Debug.Assert(hti._col == -1);
                    dataGridViewCell = Rows.SharedRow(hti._row).HeaderCell;
                    break;
                case DataGridViewHitTestType.TopLeftHeader:
                    Debug.Assert(hti._row == -1);
                    Debug.Assert(hti._col == -1);
                    dataGridViewCell = TopLeftHeaderCell;
                    break;
            }

            contextMenuStrip = dataGridViewCell is not null
                ? dataGridViewCell.GetInheritedContextMenuStrip(hti._row)
                : ContextMenuStrip;
        }

        // VisualStudio7 # 156, only show the context menu when clicked in the client area
        if (contextMenuStrip is not null && ClientRectangle.Contains(client))
        {
            contextMenuStrip.ShowInternal(this, client, keyboardActivated);
        }
        else
        {
            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_GETDLGCODE message
    /// </summary>
    private void WmGetDlgCode(ref Message m)
    {
        m.ResultInternal = (LRESULT)(m.ResultInternal | (nint)PInvoke.DLGC_WANTARROWS | (nint)PInvoke.DLGC_WANTCHARS);

        Keys modifierKeys = ModifierKeys;
        if (GetTabKeyEffective((modifierKeys & Keys.Shift) == Keys.Shift, (modifierKeys & Keys.Control) == Keys.Control))
        {
            m.ResultInternal = (LRESULT)(m.ResultInternal | (nint)PInvoke.DLGC_WANTTAB);
        }
    }

    private unsafe bool WmNotify(ref Message m)
    {
        if (m.LParamInternal == 0)
        {
            return false;
        }

        NMHDR* nmhdr = (NMHDR*)(nint)m.LParamInternal;
        if (nmhdr->code == PInvoke.TTN_GETDISPINFOW && !DesignMode)
        {
            string toolTip = ToolTipPrivate;

            if (!string.IsNullOrEmpty(toolTip))
            {
                // Setting the max width has the added benefit of enabling multiline tool tips
                PInvokeCore.SendMessage(nmhdr->hwndFrom, PInvoke.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width);

                NMTTDISPINFOW* ttt = (NMTTDISPINFOW*)(nint)m.LParamInternal;
                _toolTipBuffer.SetText(toolTip);
                ttt->lpszText = (char*)_toolTipBuffer.Buffer;
                ttt->hinst = HINSTANCE.Null;

                if (RightToLeft == RightToLeft.Yes)
                {
                    ttt->uFlags |= TOOLTIP_FLAGS.TTF_RTLREADING;
                }

                return true;
            }
        }

        return false;
    }

    protected override void WndProc(ref Message m)
    {
        switch (m.MsgInternal)
        {
            case PInvokeCore.WM_GETDLGCODE:
                WmGetDlgCode(ref m);
                return;
            case PInvokeCore.WM_LBUTTONDBLCLK:
            case PInvokeCore.WM_LBUTTONDOWN:
                // If the OnEnter procedure is called, it's because of a mouse down event, and not a TAB key.
                _dataGridViewOper[OperationInMouseDown] = true;
                try
                {
                    base.WndProc(ref m);
                }
                finally
                {
                    _dataGridViewOper[OperationInMouseDown] = false;
                }

                return;
            case PInvokeCore.WM_NOTIFY:
                if (WmNotify(ref m))
                {
                    // we are done - skip default handling
                    return;
                }

                break;

            case PInvokeCore.WM_IME_STARTCOMPOSITION:
            case PInvokeCore.WM_IME_COMPOSITION:
                if (EditingControl is not null)
                {
                    // Make sure that the first character is forwarded to the editing control.
                    PInvokeCore.SendMessage(EditingControl, m.MsgInternal, m.WParamInternal, m.LParamInternal);
                }

                break;
        }

        base.WndProc(ref m);
    }

    /// <summary>
    ///  Helper that  gets the specified event if the class is not disposing or disposed.
    /// </summary>
    private T? GetEvent<T>(object staticEvent)
        where T : class =>
        _dataGridViewOper[OperationInDispose] || IsDisposed ? null : Events[staticEvent] as T;

    private static void ThrowInvalidOperationExceptionIfNull<T>([NotNull] T? instance)
        where T : class
    {
        if (instance is null)
        {
            throw new InvalidOperationException();
        }
    }
}
