New to Telerik UI for WinForms? Download free 30-day trial

How to Scroll the Target Grid while Dragging a Row

Environment

Product Version Product Author
2022.3.913 RadGridView for WinForms Desislava Yordanova

Description

When you start the drag row operation, note that it is initiated by the source RadGridView. Hence, the whole drag operation is handled by the source grid control. The RadGridViewDragDropService.AllowAutoScrollRowsWhileDragging property is expected to enable the scrolling behavior only within the same grid. The RadGridViewDragDropService gets its GridTableElement and according to the mouse position and the GridTableElement's bounds, the scrolling is performed. However, when the target control is not the same RadGridView, this scrolling is not expected to be performed since the GridTableElement is different. This is expected and desired behavior.

This tutorial demonstrates a sample approach how to achieve such a scrolling behavior.

Solution

It is necessary to create a custom RadGridViewDragDropService whose drag and drop behavior is based on the demonstrated approach here. We will further extend the example and override the HandleMouseMove method where the target GridTableElement will be programmatically scrolled:

scroll-target-grid-while-dragging-a-row 001


public partial class RadForm1 : Telerik.WinControls.UI.RadForm
{
    public RadForm1()
    {
        InitializeComponent();

        this.radGridView1.GridViewElement.AllowDrag = true;
        var gridBehavior = this.radGridView1.GridBehavior as BaseGridBehavior;
        gridBehavior.UnregisterBehavior(typeof(GridViewDataRowInfo));
        gridBehavior.RegisterBehavior(typeof(GridViewDataRowInfo), new RowSelectionGridBehavior());

        CustomDragDropService customService = new CustomDragDropService(this.radGridView1.GridViewElement);
        this.radGridView1.GridViewElement.RegisterService(customService);

        this.radGridView1.DataSource = GenerateDataTable("LeftGrid");
        this.radGridView2.DataSource = GenerateDataTable("RightGrid");

    }

    public class RowSelectionGridBehavior : GridDataRowBehavior
    {
        protected override bool OnMouseDownLeft(MouseEventArgs e)
        {
            GridDataRowElement row = this.GetRowAtPoint(e.Location) as GridDataRowElement;
            if (row != null)
            {
                RadGridViewDragDropService svc = this.GridViewElement.GetService<RadGridViewDragDropService>();
                svc.AllowAutoScrollRowsWhileDragging = true;
                //start the drag process
                svc.Start(new SnapshotDragItem(row));
            }
            return base.OnMouseDownLeft(e);
        }
    }

    private DataTable GenerateDataTable(string gridName)
    { 
        DataTable table = new DataTable(); 
        table.Columns.Add("ID", typeof(int));
        table.Columns.Add("Name", typeof(string));
        for (int i = 1; i <= 20; i++)
        {
            table.Rows.Add(i, gridName + "Row " + i);
        }

        return table;
    }

    public class CustomDragDropService : RadGridViewDragDropService
    {
        private GridViewRowInfo draggedRow = null;

        public CustomDragDropService(RadGridViewElement gridViewElement) : base(gridViewElement)
        {
        }

        public override string Name
        {
            get
            {
                return typeof(RadGridViewDragDropService).Name;
            }
        }

        protected override void OnPreviewDragStart(PreviewDragStartEventArgs e)
        {
            base.OnPreviewDragStart(e);
            e.CanStart = true;

            SnapshotDragItem snapshot = e.DragInstance as SnapshotDragItem;
            GridDataRowElement draggedRowElement = snapshot.Item as GridDataRowElement;
            if (draggedRowElement != null)
            {
                draggedRow = draggedRowElement.RowInfo;
            }
        }

        protected override void OnPreviewDragOver(RadDragOverEventArgs e)
        {
            base.OnPreviewDragOver(e);
            SnapshotDragItem draggedSnapshot = e.DragInstance as SnapshotDragItem;
            if (draggedSnapshot != null && draggedSnapshot.Item is GridDataRowElement)
                e.CanDrop = e.HitTarget is GridDataRowElement || e.HitTarget is GridTableElement || e.HitTarget is GridSummaryRowElement;
            if (e.CanDrop == true)
            {
                lastDropTarget = e.HitTarget as RadElement;
            }
        }

        protected override void OnPreviewDragDrop(RadDropEventArgs e)
        {
            base.OnPreviewDragDrop(e);
            SnapshotDragItem draggedSnapshot = e.DragInstance as SnapshotDragItem;
            if (draggedSnapshot == null)
                return;
            GridDataRowElement rowElement = draggedSnapshot.Item as GridDataRowElement;
            e.Handled = true;
            var dropTarget = e.HitTarget as RadItem;
            var targetGrid = dropTarget.ElementTree.Control as RadGridView;
            if (targetGrid == null)
                return;

            RadGridView dragGrid = draggedRow.ViewTemplate.MasterTemplate.Owner;

            e.Handled = true;

            GridDataRowElement dropTargetRow = dropTarget as GridDataRowElement;
            GridViewRowInfo rowToDrag = dragGrid.SelectedRows[0];
            int index = dropTargetRow != null ? this.GetTargetRowIndex(dropTargetRow, e.DropLocation, rowToDrag) : targetGrid.RowCount - 1;

            List<GridViewRowInfo> rows = new List<GridViewRowInfo>();
            foreach (GridViewRowInfo row in dragGrid.SelectedRows)
                rows.Add(row);
            if (dragGrid.CurrentRow != null)
            {
                GridViewRowInfo row = dragGrid.CurrentRow;
                if ((!rows.Contains(row)))
                    rows.Add(row);
            }
            if (index == -1)
                index = 0;
            this.MoveRows(targetGrid, dragGrid, rows, index);

            targetGrid.Rows[index].IsCurrent = true;  
        }

        private int GetTargetRowIndex(GridDataRowElement row, Point dropLocation, GridViewRowInfo draggedRow)
        {
            int halfHeight = row.Size.Height / 2;
            int index = row.RowInfo.Index;

            if (dropLocation.Y > halfHeight)
                index += 1;

            if (draggedRow.Index < index)
                index -= 1;

            return index;
        }

        private void MoveRows(RadGridView targetGrid, RadGridView dragGrid, IList<GridViewRowInfo> dragRows, int index)
        {
            dragGrid.BeginUpdate();
            targetGrid.BeginUpdate();
            for (int i = dragRows.Count - 1; i >= 0; i += -1)
            {
                GridViewRowInfo row = dragRows[i];
                if (row is GridViewSummaryRowInfo)
                    continue;
                if (targetGrid.DataSource == null)
                {
                    // unbound scenario
                    GridViewRowInfo newRow = targetGrid.Rows.NewRow();
                    foreach (GridViewCellInfo cell in row.Cells)
                    {
                        if (newRow.Cells[cell.ColumnInfo.Name] != null)
                            newRow.Cells[cell.ColumnInfo.Name].Value = cell.Value;
                    }
                    targetGrid.Rows.Insert(index, newRow);
                    row.IsSelected = false;
                    dragGrid.Rows.Remove(row);
                }
                else if (typeof(DataSet).IsAssignableFrom(targetGrid.DataSource.GetType()))
                {
                    // bound to a dataset scenario
                    var sourceTable = ((DataSet)dragGrid.DataSource).Tables[0];
                    var targetTable = ((DataSet)targetGrid.DataSource).Tables[0];
                    var newRow = targetTable.NewRow();
                    foreach (GridViewCellInfo cell in row.Cells)
                        newRow[cell.ColumnInfo.Name] = cell.Value;
                    sourceTable.Rows.Remove(((DataRowView)row.DataBoundItem).Row);
                    targetTable.Rows.InsertAt(newRow, index);
                }
                else if (typeof(DataTable).IsAssignableFrom(targetGrid.DataSource.GetType()))
                {
                    var sourceTable = (DataTable)dragGrid.DataSource;
                    var targetTable = (DataTable)targetGrid.DataSource;
                    var newRow = targetTable.NewRow();
                    foreach (GridViewCellInfo cell in row.Cells)
                        newRow[cell.ColumnInfo.Name] = cell.Value;

                    sourceTable.Rows.Remove(((DataRowView)row.DataBoundItem).Row);
                    targetTable.Rows.InsertAt(newRow, index);
                }
                else if (typeof(IList).IsAssignableFrom(targetGrid.DataSource.GetType()))
                {
                    // bound to a list of objects scenario
                    var targetCollection = (IList)targetGrid.DataSource;
                    var sourceCollection = (IList)dragGrid.DataSource;
                    sourceCollection.Remove(row.DataBoundItem);
                    targetCollection.Add(row.DataBoundItem);
                }
                else
                    throw new ApplicationException("Unhandled Scenario");
                index += 1;
            }
            dragGrid.EndUpdate(true);
            targetGrid.EndUpdate(true);
        }

        private RadElement lastDropTarget = null;

        protected override void HandleMouseMove(System.Drawing.Point mousePosition)
        {
            base.HandleMouseMove(mousePosition);
            System.Drawing.Point location = this.GridViewElement.ElementTree.Control.PointToClient(Control.MousePosition);
            GridTableElement tableElement = this.GetTableElementAtPoint(location);
            ISupportDrag supportDrag = this.Context as ISupportDrag;
            object dataContext = supportDrag.GetDataContext();

            GridViewDataRowInfo rowContext = dataContext as GridViewDataRowInfo;
            if (lastDropTarget != null && lastDropTarget.ElementTree != null)
            {
                tableElement = (lastDropTarget.ElementTree.Control as RadGridView).TableElement;
            }

            if (this.AllowAutoScrollRowsWhileDragging)
                ScrollRows(tableElement, location);
        }

        private void ScrollRows(GridTableElement tableElement, System.Drawing.Point location)
        {
            ScrollableRowsContainerElement scrollableRows = tableElement.ViewElement.ScrollableRows;
            RadScrollBarElement vScrollbar = GetVeritcalScrollbar(tableElement);
            System.Drawing.Rectangle containerBounds = scrollableRows.ControlBoundingRectangle;

            int delta = 0;

            if (location.Y > containerBounds.Bottom)
                delta = location.Y - containerBounds.Bottom;
            else if (location.Y < containerBounds.Y)
                delta = location.Y - containerBounds.Y;

            if (delta != 0 && vScrollbar.Visibility == ElementVisibility.Visible)
                vScrollbar.Value = ClampValue(vScrollbar.Value + delta, vScrollbar.Minimum, vScrollbar.Maximum - vScrollbar.LargeChange + 1);
        }

        private int ClampValue(int value, int minimum, int maximum)
        {
            if (value < minimum)
                return minimum;

            if (maximum > 0 && value > maximum)
                return maximum;

            return value;
        }

        private RadScrollBarElement GetVeritcalScrollbar(GridTableElement tableElement)
        {
            if (GridViewElement.UseScrollbarsInHierarchy)
                return tableElement.VScrollBar;

            return tableElement.VScrollBar;
        }
    }
}

Please have in mind that this is just a sample approach and it may not cover all possible cases. Feel free to modify and extend it in a way that suits your requirements best.