Completed
Last Updated: 08 Sep 2015 11:51 by ADMIN
ADMIN
Dess | Tech Support Engineer, Principal
Created on: 04 Aug 2015 14:36
Category: GridView
Type: Bug Report
0
FIX. RadGridView - ArgumentOutOfRangeException when multiple selection is enabled in hierarchical grid
To reproduce:

private void Form1_Load(object sender, EventArgs e)
{
    // TODO: This line of code loads data into the 'nwindDataSet.Products' table. You can move, or remove it, as needed.
    this.productsTableAdapter.Fill(this.nwindDataSet.Products);
    // TODO: This line of code loads data into the 'nwindDataSet.Order_Details' table. You can move, or remove it, as needed.
    this.order_DetailsTableAdapter.Fill(this.nwindDataSet.Order_Details);
    // TODO: This line of code loads data into the 'nwindDataSet.Categories' table. You can move, or remove it, as needed.
    this.categoriesTableAdapter.Fill(this.nwindDataSet.Categories);
   
    radGridView1.DataSource = nwindDataSet.Categories;

    GridViewTemplate firstChildtemplate = new GridViewTemplate();
    firstChildtemplate.DataSource = nwindDataSet.Products;
    firstChildtemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
    radGridView1.MasterTemplate.Templates.Add(firstChildtemplate);

    GridViewRelation relation = new GridViewRelation(radGridView1.MasterTemplate);
    relation.ChildTemplate = firstChildtemplate;
    relation.RelationName = "CategoriesProducts";
    relation.ParentColumnNames.Add("CategoryID");
    relation.ChildColumnNames.Add("CategoryID");
    radGridView1.Relations.Add(relation);

    GridViewTemplate secondChildtemplate = new GridViewTemplate();
    secondChildtemplate.DataSource = nwindDataSet.Order_Details;
    secondChildtemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
    firstChildtemplate.Templates.Add(secondChildtemplate);

    GridViewRelation relation2 = new GridViewRelation(firstChildtemplate);
    relation2.ChildTemplate = secondChildtemplate;
    relation2.RelationName = "ProductsOrderDetails";
    relation2.ParentColumnNames.Add("ProductID");
    relation2.ChildColumnNames.Add("ProductID");
    radGridView1.Relations.Add(relation2);
    this.radGridView1.AutoSizeColumnsMode = Telerik.WinControls.UI.GridViewAutoSizeColumnsMode.Fill;
    this.radGridView1.MultiSelect = true;
    this.radGridView1.SelectionMode = Telerik.WinControls.UI.GridViewSelectionMode.CellSelect ;
}

Please refer to the attached gif file illustrating the error when performing multiple selection.

1. Workaround: use Shift + arrow keys to perform multiple selection.

2. Workaround: custom row behavior:

 BaseGridBehavior behavior = (BaseGridBehavior)this.radGridView1.GridBehavior;
behavior.UnregisterBehavior(typeof(GridViewHierarchyRowInfo));
behavior.RegisterBehavior(typeof(GridViewHierarchyRowInfo), new MyGridRowBehavior());


public class MyGridRowBehavior : GridHierarchyRowBehavior
{
    protected override bool ProcessMouseSelection(Point mousePosition, GridCellElement currentCell)
    {
        if (this.RootGridBehavior.LockedBehavior != this)
        {
            this.GridControl.Capture = true;
            this.RootGridBehavior.LockBehavior(this);
        }

        bool result = this.DoMouseSelection(currentCell, mousePosition);

        return result;
    }

    private bool DoMouseSelection(GridCellElement currentCell, Point currentLocation)
    {
        if (!this.MasterTemplate.MultiSelect || this.GridViewElement.Template.AllowRowReorder)
        {
            return false;
        }

        Point mouseDownLocation = (Point)typeof(GridRowBehavior).GetField("mouseDownLocation", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
        Rectangle rect = new Rectangle(mouseDownLocation.X - SystemInformation.DragSize.Width / 2, mouseDownLocation.Y - SystemInformation.DragSize.Height / 2,
            SystemInformation.DragSize.Width, SystemInformation.DragSize.Height);

        if (rect.Contains(currentLocation))
        {
            return false;
        }

        GridTableElement tableElement = this.GridViewElement.CurrentView as GridTableElement;

        if (tableElement == null)
        {
            return false;
        }

        bool selectionStartedOnAPinnedColumn = (bool)typeof(GridRowBehavior).GetField("selectionStartedOnAPinnedColumn", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
        bool mouseDownOnLeftPinnedColumn = (bool)typeof(GridRowBehavior).GetField("mouseDownOnLeftPinnedColumn", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
        bool mouseDownOnRightPinnedColumn = (bool)typeof(GridRowBehavior).GetField("mouseDownOnRightPinnedColumn", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
        bool selectionStartedOnAPinnedRow = (bool)typeof(GridRowBehavior).GetField("selectionStartedOnAPinnedRow", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
        bool mouseDownOnTopPinnedRow = (bool)typeof(GridRowBehavior).GetField("mouseDownOnTopPinnedRow", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
        bool mouseDownOnBottomPinnedRow = (bool)typeof(GridRowBehavior).GetField("mouseDownOnBottomPinnedRow", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);

        if (selectionStartedOnAPinnedColumn && this.GetViewportBounds(tableElement).Contains(currentLocation))
        {
            if (mouseDownOnLeftPinnedColumn)
            {
                tableElement.HScrollBar.Value = tableElement.HScrollBar.Minimum;
                mouseDownOnLeftPinnedColumn = false;
            }

            if (mouseDownOnRightPinnedColumn)
            {
                tableElement.HScrollBar.Value = tableElement.HScrollBar.Maximum - tableElement.HScrollBar.LargeChange + 1;
                mouseDownOnRightPinnedColumn = false;
            }

            selectionStartedOnAPinnedColumn = false;
        }

        if (selectionStartedOnAPinnedRow && this.GetViewportBounds(tableElement).Contains(currentLocation))
        {
            if (mouseDownOnTopPinnedRow)
            {
                tableElement.VScrollBar.Value = tableElement.VScrollBar.Minimum;
                mouseDownOnTopPinnedRow = false;
            }

            if (mouseDownOnBottomPinnedRow)
            {
                tableElement.VScrollBar.Value = tableElement.VScrollBar.Maximum - tableElement.VScrollBar.LargeChange + 1;
                mouseDownOnBottomPinnedRow = false;
            }

            selectionStartedOnAPinnedRow = false;
        }

        if (currentCell.RowInfo is GridViewDataRowInfo)
        {
            if (this.MasterTemplate.SelectionMode == GridViewSelectionMode.FullRowSelect)
            {
                typeof(GridHierarchyRowBehavior).GetMethod("DoMultiFullRowSelect", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(this, new object[] { currentCell, currentLocation });
            }
            else
            {
                this.DoMultiCellSelect(currentCell, currentLocation);
            }
        }

        return true;
    }

    private GridRowElement GetFirstScrollableRowElement(GridTableElement tableElement)
    {
        if (tableElement.ViewElement.ScrollableRows.Children.Count < 1)
        {
            return null;
        }

        return (GridRowElement)tableElement.ViewElement.ScrollableRows.Children[0];
    }

    private GridViewColumn GetFirstScrollableColumn(GridTableElement tableElement)
    {
        GridRowElement rowElement = this.GetFirstScrollableRowElement(tableElement);

        if (rowElement == null)
        {
            return null;
        }

        int counter = 0;

        while (counter < rowElement.VisualCells.Count)
        {
            if (!rowElement.VisualCells[counter].IsPinned && rowElement.VisualCells[counter].ColumnInfo is GridViewDataColumn)
            {
                return rowElement.VisualCells[counter].ColumnInfo;
            }

            counter++;
        }

        return null;
    }

    private int GetRowIndex(GridViewRowInfo rowInfo)
    {
        List<GridViewRowInfo> orderedRows = typeof(GridRowBehavior).GetField("orderedRows", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this) as List<GridViewRowInfo>;

        return orderedRows.IndexOf(rowInfo);
    }

    private void DoMultiCellSelect(GridCellElement currentCell, Point currentLocation)
    {
        #region GridViewSelection

        int rowUnderMouseIndex = this.GetRowIndex(this.RootGridBehavior.RowAtPoint.RowInfo);
        int columnUnderMouseIndex = 0;

        GridTableElement tableElement = this.GridViewElement.CurrentView as GridTableElement;
        GridViewColumn col = this.RootGridBehavior.CellAtPoint.ColumnInfo;

        if (this.RootGridBehavior.CellAtPoint.ColumnInfo is GridViewRowHeaderColumn)
        {
            col = this.GetFirstScrollableColumn(tableElement);
        }

        List<GridViewRowInfo> orderedRows = typeof(GridRowBehavior).GetField("orderedRows", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this) as List<GridViewRowInfo>;
        List<GridViewColumn> orderedColumns = typeof(GridRowBehavior).GetField("orderedColumns", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this) as List<GridViewColumn>;

        if (col != null)
        {
            columnUnderMouseIndex = orderedColumns.IndexOf(col);
        }

        List<GridViewRowInfo> rows = new List<GridViewRowInfo>();

        int anchorRowIndex = (int)typeof(GridRowBehavior).GetField("anchorRowIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this);
        int startIndex = Math.Min(anchorRowIndex, rowUnderMouseIndex);
        int endIndex = Math.Max(anchorRowIndex, rowUnderMouseIndex);

        for (int i = startIndex; i < endIndex; i++)
        {
            if (i < 0)
            {
                continue;
            }
            if (this.GridViewElement.Template.ChildRows.Count > i)
            {
                rows.Add(orderedRows[i]);
            }
        }

        int anchorColumnIndex = (int)typeof(GridRowBehavior).GetField("anchorColumnIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this);
        int columnLeft = Math.Min(anchorColumnIndex, columnUnderMouseIndex);
        int columnRight = Math.Max(anchorColumnIndex, columnUnderMouseIndex);

        GridViewSelectionCancelEventArgs cancelArgs = new GridViewSelectionCancelEventArgs(rows, columnLeft, columnRight);
        this.MasterTemplate.EventDispatcher.RaiseEvent<GridViewSelectionCancelEventArgs>(EventDispatcher.SelectionChanging, this, cancelArgs);

        if (cancelArgs.Cancel)
        {
            return;
        }

        #endregion

        bool isProcessedShiftOrControl = this.IsPressedShift || this.IsPressedControl;

        ObservableCollection<GridViewCellInfo> SelectedCells = typeof(GridViewSelectedCellsCollection).GetProperty("ObservableItems",
            BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this.MasterTemplate.SelectedCells) as ObservableCollection<GridViewCellInfo>;
        
        SelectedCells.BeginUpdate();
        this.GridViewElement.CurrentView.BeginUpdate();

        int count = this.MasterTemplate.SelectedCells.Count;
        bool notifyUpdates = this.ProcessCellSelection(rowUnderMouseIndex, columnUnderMouseIndex);

        if (isProcessedShiftOrControl)
        {
            notifyUpdates = count != this.MasterTemplate.SelectedCells.Count;
        }

        this.GridViewElement.CurrentView.EndUpdate(false);
        SelectedCells.EndUpdate(notifyUpdates);
    }

    private bool ProcessCellSelection(int rowUnderMouseIndex, int columnUnderMouseIndex)
    {
        int currentRowIndex = (int)typeof(GridRowBehavior).GetField("currentRowIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this);
        int anchorRowIndex = (int)typeof(GridRowBehavior).GetField("anchorRowIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this);
        int currentColumnIndex = (int)typeof(GridRowBehavior).GetField("currentColumnIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this);
        int anchorColumnIndex = (int)typeof(GridRowBehavior).GetField("anchorColumnIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this);

        if ((rowUnderMouseIndex == currentRowIndex && columnUnderMouseIndex == currentColumnIndex) || (rowUnderMouseIndex < 0 || columnUnderMouseIndex < 0))
        {
            return false;
        }

        bool verticalDirectionChange = (rowUnderMouseIndex < currentRowIndex && currentRowIndex > anchorRowIndex && rowUnderMouseIndex < anchorRowIndex) ||
                                    (rowUnderMouseIndex > currentRowIndex && currentRowIndex < anchorRowIndex && rowUnderMouseIndex > anchorRowIndex);

        bool horizontalDirectionChange = (columnUnderMouseIndex < currentColumnIndex && currentColumnIndex > anchorColumnIndex && columnUnderMouseIndex < anchorColumnIndex) ||
                                        (columnUnderMouseIndex > currentColumnIndex && currentColumnIndex < anchorColumnIndex && columnUnderMouseIndex > anchorColumnIndex);

        List<GridViewRowInfo> orderedRows = typeof(GridRowBehavior).GetField("orderedRows", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this) as List<GridViewRowInfo>;
        List<GridViewColumn> orderedColumns = typeof(GridRowBehavior).GetField("orderedColumns", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this) as List<GridViewColumn>;

        if (verticalDirectionChange || horizontalDirectionChange)
        {
            int rowStartIndex = Math.Min(anchorRowIndex, currentRowIndex);
            int rowEndIndex = Math.Max(anchorRowIndex, currentRowIndex);
            int colStartIndex = Math.Min(anchorColumnIndex, currentColumnIndex);
            int colEndIndex = Math.Max(anchorColumnIndex, currentColumnIndex);

            for (int i = rowStartIndex; i <= rowEndIndex; i++)
            {
                for (int j = colStartIndex; j <= colEndIndex; j++)
                {
                    GridViewCellInfo cell = orderedRows[i].Cells[orderedColumns[j].Index];

                    if (cell != null && cell.IsSelected)
                    {
                        cell.IsSelected = false;
                    }
                }
            }
        }

        bool expandingSelectionUp = rowUnderMouseIndex < currentRowIndex && rowUnderMouseIndex < anchorRowIndex;
        bool expandingSelectionDown = rowUnderMouseIndex > currentRowIndex && rowUnderMouseIndex > anchorRowIndex;
        bool expandingSelectionLeft = columnUnderMouseIndex < currentColumnIndex && columnUnderMouseIndex < anchorColumnIndex;
        bool expandingSelectionRight = columnUnderMouseIndex > currentColumnIndex && columnUnderMouseIndex > anchorColumnIndex;

        if (expandingSelectionDown || expandingSelectionUp || expandingSelectionLeft || expandingSelectionRight)
        {
            int rowStartIndex = Math.Min(anchorRowIndex, rowUnderMouseIndex);
            int rowEndIndex = Math.Max(anchorRowIndex, rowUnderMouseIndex);
            int colStartIndex = Math.Min(anchorColumnIndex, columnUnderMouseIndex);
            int colEndIndex = Math.Max(anchorColumnIndex, columnUnderMouseIndex);

            for (int i = rowStartIndex; i <= rowEndIndex; i++)
            {
                for (int j = colStartIndex; j <= colEndIndex; j++)
                {
                    GridViewCellInfo cell = orderedRows[i].Cells[orderedColumns[j].Index];

                    if (cell != null && !cell.IsSelected)
                    {
                        cell.IsSelected = true;
                    }
                }
            }
        }
        else
        {
            bool shrinkingSelectionUp = rowUnderMouseIndex < currentRowIndex && rowUnderMouseIndex >= anchorRowIndex;
            bool shrinkingSelectionDown = rowUnderMouseIndex > currentRowIndex && rowUnderMouseIndex <= anchorRowIndex;
            bool shrinkingSelectionLeft = columnUnderMouseIndex < currentColumnIndex && columnUnderMouseIndex >= anchorColumnIndex;
            bool shrinkingSelectionRight = columnUnderMouseIndex > currentColumnIndex && columnUnderMouseIndex <= anchorColumnIndex;

            if (shrinkingSelectionUp || shrinkingSelectionDown)
            {
                int rowStartIndex = Math.Min(currentRowIndex, rowUnderMouseIndex);
                int rowEndIndex = Math.Max(currentRowIndex, rowUnderMouseIndex);
                int colStartIndex = Math.Min(anchorColumnIndex, columnUnderMouseIndex);
                int colEndIndex = Math.Max(anchorColumnIndex, columnUnderMouseIndex);

                if (shrinkingSelectionUp)
                {
                    rowStartIndex += 1;
                }

                if (shrinkingSelectionDown)
                {
                    rowEndIndex -= 1;
                }

                for (int i = rowStartIndex; i <= rowEndIndex; i++)
                {
                    if (i != anchorRowIndex)
                    {
                        for (int j = colStartIndex; j <= colEndIndex; j++)
                        {
                            GridViewCellInfo cell = orderedRows[i].Cells[orderedColumns[j].Index];

                            if (cell != null && cell.IsSelected)
                            {
                                cell.IsSelected = false;
                            }
                        }
                    }
                }
            }

            if (shrinkingSelectionLeft || shrinkingSelectionRight)
            {
                int rowStartIndex = Math.Min(anchorRowIndex, rowUnderMouseIndex);
                int rowEndIndex = Math.Max(anchorRowIndex, rowUnderMouseIndex);
                int colStartIndex = Math.Min(currentColumnIndex, columnUnderMouseIndex);
                int colEndIndex = Math.Max(currentColumnIndex, columnUnderMouseIndex);

                if (shrinkingSelectionLeft)
                {
                    colStartIndex += 1;
                }

                if (shrinkingSelectionRight)
                {
                    colEndIndex -= 1;
                }

                for (int i = rowStartIndex; i <= rowEndIndex; i++)
                {
                    for (int j = colStartIndex; j <= colEndIndex; j++)
                    {
                        if (j != anchorColumnIndex)
                        {
                            GridViewCellInfo cell = orderedRows[i].Cells[orderedColumns[j].Index];

                            if (cell != null && cell.IsSelected)
                            {
                                cell.IsSelected = false;
                            }
                        }
                    }
                }
            }
        }

        typeof(GridRowBehavior).GetField("currentRowIndex", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(this, rowUnderMouseIndex);
        typeof(GridRowBehavior).GetField("currentColumnIndex", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(this, columnUnderMouseIndex);

        return true;
    }

    private Rectangle GetViewportBounds(GridTableElement tableElement)
    {
        ScrollableRowsContainerElement scrollableRows = tableElement.ViewElement.ScrollableRows;
        Rectangle bounds = tableElement.ViewElement.ScrollableRows.ControlBoundingRectangle;

        for (int index = 0; index < scrollableRows.Children.Count; index++)
        {
            GridVirtualizedRowElement virtualizedRow = scrollableRows.Children[index] as GridVirtualizedRowElement;

            if (virtualizedRow != null)
            {
                VirtualizedColumnContainer scrollableColumns = virtualizedRow.ScrollableColumns;
                bounds.X = this.GridViewElement.RightToLeft ? virtualizedRow.RightPinnedColumns.ControlBoundingRectangle.Right
                           : virtualizedRow.LeftPinnedColumns.ControlBoundingRectangle.Right;
                bounds.Width = scrollableColumns.ControlBoundingRectangle.Width;

                break;
            }
        }

        return bounds;
    }
}
Attached Files:
0 comments