Completed
Last Updated: 09 Sep 2015 11:28 by ADMIN
ADMIN
Dess | Tech Support Engineer, Sr.
Created on: 10 Aug 2015 07:10
Category: GridView
Type: Bug Report
0
FIX. RadGridView - ArgumentOutOfRangeException when multiple selection is enabled in Self-reference grid
To reproduce:

public Form1()
        {
            InitializeComponent();
            
            this.radGridView1.ReadOnly = true;
            this.radGridView1.MasterTemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
            this.radGridView1.DataSource = CreateDataSource();
            this.radGridView1.Relations.AddSelfReference(this.radGridView1.MasterTemplate, "ID", "ParentID");

            this.radGridView1.Columns["ID"].IsVisible = false;
            this.radGridView1.Columns["ParentID"].IsVisible = false;

            this.radGridView1.MultiSelect = true;
            this.radGridView1.SelectionMode = GridViewSelectionMode.CellSelect;
        }

        private static DataTable CreateDataSource()
        {
            DataTable dataSource = new DataTable("fileSystem");
            dataSource.Columns.Add("ID", typeof(int));
            dataSource.Columns.Add("ParentID", typeof(int));
            dataSource.Columns.Add("Name", typeof(string));
            dataSource.Columns.Add("Date", typeof(DateTime));
            dataSource.Columns.Add("Type", typeof(string));
            dataSource.Columns.Add("Size", typeof(int));

            dataSource.Rows.Add(1, null, "Program Files", DateTime.Now.AddDays(-100), "Folder", 5120);
            dataSource.Rows.Add(2, 1, "Visual Studio 2010", DateTime.Now.AddDays(-100), "Folder", 3220);
            dataSource.Rows.Add(3, 2, "bin", DateTime.Now.AddDays(-100), "Folder", 3220);
            dataSource.Rows.Add(4, 2, "READEME.txt", DateTime.Now.AddDays(-100), "Text Document", 3);

            dataSource.Rows.Add(5, 1, "Telerik RadControls", DateTime.Now.AddDays(-10), "Folder", 3120);
            dataSource.Rows.Add(6, 5, "Telerik UI for Winforms", DateTime.Now.AddDays(-10), "Folder", 101);
            dataSource.Rows.Add(7, 5, "Telerik UI for Silverlight", DateTime.Now.AddDays(-10), "Folder", 123);
            dataSource.Rows.Add(8, 5, "Telerik UI for WPF", DateTime.Now.AddDays(-10), "Folder", 221);
            dataSource.Rows.Add(9, 5, "Telerik UI for ASP.NET AJAX", DateTime.Now.AddDays(-10), "Folder", 121);

            dataSource.Rows.Add(10, 1, "Microsoft Office 2010", DateTime.Now.AddDays(-120), "Folder", 1230);
            dataSource.Rows.Add(11, 10, "Microsoft Word 2010", DateTime.Now.AddDays(-120), "Folder", 1230);
            dataSource.Rows.Add(12, 10, "Microsoft Excel 2010", DateTime.Now.AddDays(-120), "Folder", 1230);
            dataSource.Rows.Add(13, 10, "Microsoft Powerpoint 2010", DateTime.Now.AddDays(-120), "Folder", 1230);

            dataSource.Rows.Add(14, 1, "Debug Diagnostic Tools v1.0", DateTime.Now.AddDays(-400), "Folder", 2120);
            dataSource.Rows.Add(15, 1, "Designer's 3D Tools", DateTime.Now.AddDays(-500), "Folder", 1120);
            dataSource.Rows.Add(16, 1, "Communication", DateTime.Now.AddDays(-700), "Folder", 120);

            dataSource.Rows.Add(17, null, "My Documents", DateTime.Now.AddDays(-200), "Folder", 1024);
            dataSource.Rows.Add(18, 17, "Salaries.xlsx", DateTime.Now.AddDays(-200), "Excel File", 1);
            dataSource.Rows.Add(19, 17, "RecessionAnalysis.xlsx", DateTime.Now.AddDays(-200), "Excel File", 1);

            dataSource.Rows.Add(20, null, "Windows", DateTime.Now.AddDays(-100), "Folder", 10240);

            dataSource.Rows.Add(21, 20, "System32", DateTime.Now.AddDays(-220), "Folder", 510);
            dataSource.Rows.Add(22, 20, "assembly", DateTime.Now.AddDays(-20), "Folder", 240);

            dataSource.Rows.Add(23, 22, "System.Data.dll", DateTime.Now.AddDays(-20), "Assembly File", 4);
            dataSource.Rows.Add(24, 22, "System.Core.dll", DateTime.Now.AddDays(-20), "Assembly File", 2);
            dataSource.Rows.Add(25, 22, "System.Drawings.dll", DateTime.Now.AddDays(-20), "Assembly File", 3);
            dataSource.Rows.Add(26, 22, "Telerik.WinControls.UI.dll", DateTime.Now.AddDays(-20), "Assembly File", 5);

            dataSource.Rows.Add(27, null, "Users", DateTime.Now.AddDays(-100), "Folder", 5512);
            dataSource.Rows.Add(28, 27, "Administrator", DateTime.Now.AddDays(-100), "Folder", 1512);
            dataSource.Rows.Add(29, 27, "Guest", DateTime.Now.AddDays(-100), "Folder", 2512);
            dataSource.Rows.Add(30, 27, "User1", DateTime.Now.AddDays(-100), "Folder", 3512);

            dataSource.Rows.Add(31, null, "Share", DateTime.Now.AddDays(-50), "Folder", 15360);
            dataSource.Rows.Add(32, 31, "Photos", DateTime.Now.AddDays(-50), "Folder", 360);
            dataSource.Rows.Add(33, 32, "Flowers.JPG", DateTime.Now.AddDays(-50), "JPEG File", 1);
            dataSource.Rows.Add(34, 32, "Panda.GIF", DateTime.Now.AddDays(-50), "GIF File", 3);
            dataSource.Rows.Add(35, 32, "Landscape.png", DateTime.Now.AddDays(-50), "PNG File", 4);

            dataSource.Rows.Add(36, null, "Music", DateTime.Now.AddDays(-2), "Folder", 0);
            dataSource.Rows.Add(37, 36, "Mozart", DateTime.Now.AddDays(-3), "Folder", 0);
            dataSource.Rows.Add(38, 36, "Pavarotti", DateTime.Now.AddDays(-40), "Folder", 0);
            dataSource.Rows.Add(39, 36, "AC/DC", DateTime.Now.AddDays(-50), "Folder", 0);
            dataSource.Rows.Add(40, 36, "Queen", DateTime.Now.AddDays(-8), "Folder", 0);

            dataSource.Rows.Add(33, null, "Boot.ini", DateTime.Now.AddDays(-10), "INI File", 0);

            return dataSource;
        }

Workaround: custom row behavior 

 //replace row behavior for hierarchical rows
 BaseGridBehavior behavior = (BaseGridBehavior)this.radGridView1.GridBehavior;
 behavior.UnregisterBehavior(typeof(GridViewHierarchyRowInfo));
 behavior.RegisterBehavior(typeof(GridViewHierarchyRowInfo), new MyGridHierarchyRowBehavior());
 //replace row behavior for data rows
 behavior.UnregisterBehavior(typeof(GridViewDataRowInfo));
 behavior.RegisterBehavior(typeof(GridViewDataRowInfo), new MyGridDataRowBehavior());

   public class MyGridHierarchyRowBehavior : 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 (orderedRows.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++)
                    {
                        if (i >= 0 && i < orderedRows.Count)
                        {
                            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 && i >= 0 && i < orderedRows.Count)
                            {
                                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;
        }
        
        public override bool OnMouseMove(MouseEventArgs e)
        {
            Point currentLocation = e.Location;
            
            GridRowElement currentRow = this.RootGridBehavior.RowAtPoint;
            
            if (currentRow == null)
            {
                this.GridViewElement.TableElement.GetType().GetProperty("HoveredRow", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this.GridViewElement.TableElement, null);
            }
            
            if (e.Button == MouseButtons.None)
            {
                return this.ShowSizeNSCursort(currentLocation);
            }
            
            if (e.Button != MouseButtons.Left)
            {
                this.ResetControlCursor();
                
                return false;
            }
            
            GridRowElement rowToResize = typeof(GridRowBehavior).GetField("rowToResize", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this) as GridRowElement;
            
            if (rowToResize != null)
            {
                this.ResizeRow(currentLocation);
                
                return true;
            }
            
            GridCellElement currentCell = this.RootGridBehavior.CellAtPoint;
            Point mouseDownLocation = (Point)typeof(GridRowBehavior).GetField("mouseDownLocation", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
            GridRowElement row = this.GetRowAtPoint(mouseDownLocation);
            bool result = false;
            
            if (currentCell != null && currentCell.ViewTemplate != null && !currentCell.ViewTemplate.AllowRowReorder && row != null)
            {
                result = this.ProcessMouseSelection(currentLocation, currentCell);
            }
            
            if ((currentCell == null || currentCell.ColumnInfo == null || currentCell.ColumnInfo.IsPinned || currentCell.RowInfo.IsPinned) &&
                this.MasterTemplate.MultiSelect && mouseDownLocation != currentLocation)
            {
                typeof(GridRowBehavior).GetField("mouseMoveLocation", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, currentLocation);
                System.Windows.Forms.Timer scrollTimer = typeof(GridRowBehavior).GetField("scrollTimer", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this) as System.Windows.Forms.Timer;
                
                if (!scrollTimer.Enabled)
                {
                    scrollTimer.Enabled = true;
                }
                
                result = false;
            }
            
            return result;
        }
    }
    
    public class MyGridDataRowBehavior : GridDataRowBehavior
    {
        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 (orderedRows.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;
        }
        
        public override bool OnMouseMove(MouseEventArgs e)
        {
            Point currentLocation = e.Location;

            GridRowElement currentRow = this.RootGridBehavior.RowAtPoint;
            
            if (currentRow == null)
            {
                this.GridViewElement.TableElement.GetType().GetProperty("HoveredRow", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this.GridViewElement.TableElement, null);
            }
            
            if (e.Button == MouseButtons.None)
            {
                return this.ShowSizeNSCursort(currentLocation);
            }
            
            if (e.Button != MouseButtons.Left)
            {
                this.ResetControlCursor();
            
                return false;
            }

            GridRowElement rowToResize = typeof(GridRowBehavior).GetField("rowToResize", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this) as GridRowElement;
            
            if (rowToResize != null)
            {
                this.ResizeRow(currentLocation);
            
                return true;
            }
            
            GridCellElement currentCell = this.RootGridBehavior.CellAtPoint;
            Point mouseDownLocation = (Point)typeof(GridRowBehavior).GetField("mouseDownLocation", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
            GridRowElement row = this.GetRowAtPoint(mouseDownLocation);
            bool result = false;
            
            if (currentCell != null && currentCell.ViewTemplate != null && !currentCell.ViewTemplate.AllowRowReorder && row != null)
            {
                result = this.ProcessMouseSelection(currentLocation, currentCell);
            }
                
            if ((currentCell == null || currentCell.ColumnInfo == null || currentCell.ColumnInfo.IsPinned || currentCell.RowInfo.IsPinned) &&
                this.MasterTemplate.MultiSelect && mouseDownLocation != currentLocation)
            {
                typeof(GridRowBehavior).GetField("mouseMoveLocation", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, currentLocation);
                System.Windows.Forms.Timer scrollTimer = typeof(GridRowBehavior).GetField("scrollTimer", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this) as System.Windows.Forms.Timer;
                
                if (!scrollTimer.Enabled)
                {
                    scrollTimer.Enabled = true;
                }
            
                result = false;
            }
        
            return result;
        }
}
0 comments