Skip to main content
All docs
V24.1

Drag-and-Drop Grid Rows

  • 16 minutes to read

Users can drag and drop data items (rows, tiles, etc.) in the GridControl to move them from one position to another. They can also move data items from the grid to another control.

Enable drag-and-drop operations in one of the following ways:

Note

  • DevExpress drag-and-drop implementations do not use the standard .NET drag-and-drop engine. These approaches cannot be used together.

  • DevExpress implementations of drag-and-drop include built-in control capabilities, and a separate Drag-and-Drop Behavior.

  • Drag-and-drop activates when a user clicks a row. If there is a cell editor beneath the mouse pointer, it intercepts mouse events and cancels the drag-and-drop operation. To prevent this from happening, you can set the EditorShowMode to a value different from “Default” or “MouseDown”. Otherwise, use the Row Indicator to drag rows.

Drag-and-Drop Behavior

To allow users to move data items between two controls (from one GridControl to another, from the GridControl to the TreeList or ListBoxControl), attach the Behavior to both controls. If both grids/tree lists contain the same data fields, there are no more actions required. If the data structure differs, handle the Behavior’s drag events to convert data items. See the following help topic for more information: Drag-and-Drop Behavior.

Reorder Grid Rows and Tiles

Attach the Drag-and-Drop Behavior to the GridView or TileView.

The GridView supports the following drag-and-drop operations out of the box:

  • Move selected rows to another Grid control.
  • Reorder selected rows within the GridView (supported data sources: IList, DataTable, DataView).

Important

Drag and drop does not work with sorted/filtered Data Views.

Use the GridView.OptionsDragDrop property to configure drag-and-drop operations that users can perform within the GridView.

Drag and Drop Data Rows - WinForms Data Grid

Run Demo

You can also handle the following events to control drag-drop operations as needed:

View Example: How to handle drag-drop events

Drag Rows Between Detail Views

To allow users to move child rows between detail views in the GridControl, do the following:

  • Attach the Behavior to the master view in the Visual Studio Designer or in code.
  • Use the ViewRegistered event to attach the Behavior to the detail view. To detach the Behavior, use the ViewRemoved event.
  • Handle the DragDrop event of the Behavior attached to the master view to move the processed child row from the source detail view to the target detail view.

In this example, the Behavior Manager is placed on the component tray and the Drag-and-Drop Behavior is attached to the main view in the Visual Studio Designer.

image

using DevExpress.Utils.DragDrop;
using DevExpress.XtraGrid.Views.Grid;

gridView1.OptionsBehavior.Editable = false;
gridView1.OptionsSelection.MultiSelect = true;
gridControl1.DataSource = CreateDataTable();
gridControl1.ViewRegistered += GridControl1_ViewRegistered;

private void dragDropEvents1_DragDrop(object sender, DragDropEventArgs e) {
    GridView masterView = e.Source as GridView;
    // Cast the event arguments to the DragDropGridEventArgs type
    // or call the static (Shared in VB) DragDropGridEventArgs.GetDragDropGridEventArgs method
    // to get grid-specific event arguments.
    DragDropGridEventArgs realArgs = (DragDropGridEventArgs)e;
    GridView sourceView = realArgs.Source as GridView;
    GridView targetView = realArgs.Target as GridView;

    var view1 = gridControl1.GetViewAt(gridControl1.PointToClient(e.Location));
    if(sourceView != null && targetView != null) {
        // Get the processed child row's parent ID.
        var newParentId = masterView.GetRowCellValue(targetView.SourceRowHandle, "Id");
        foreach(DataRowView dataRow in realArgs.DataRows) {
            // Update the processed child row's parent ID.
            dataRow.Row["ParentId"] = newParentId;
        }
        e.Handled = true;
    }
}

private void GridControl1_ViewRegistered(object sender, DevExpress.XtraGrid.ViewOperationEventArgs e) {
    if(e.View.IsDetailView) {
        // It is assumed that the Behavior Manager is placed
        // to the component tray in the Visual Studio Designer.
        behaviorManager1.Attach<DragDropBehavior>(e.View);
    }
}

public DataTable CreateDataTable() {
    masterTable = new DataTable();
    masterTable.Columns.Add("Id", typeof(int));
    masterTable.Columns.Add("Name");
    masterTable.Columns.Add("IsActive", typeof(bool));
    masterTable.Columns.Add("OrderCount", typeof(int));
    masterTable.Columns.Add("RegistrationDate", typeof(DateTime));

    for(int i = 0; i < 10; i++) {
        masterTable.Rows.Add(i, "Name" + i, i % 2 == 0, i * 10, DateTime.Now.AddDays(i));
    }

    DataTable childTable = new DataTable();
    childTable.Columns.Add("ParentId", typeof(int));
    childTable.Columns.Add("Id", typeof(int));
    childTable.Columns.Add("Name");

    for(int i = 0; i < 20; i++) {
        childTable.Rows.Add(i % 10, i, "Name" + i);
    }

    DataSet set = new DataSet();
    set.Tables.Add(masterTable);
    set.Tables.Add(childTable);
    set.Relations.Add(masterTable.Columns["Id"], childTable.Columns["ParentId"]);

    return masterTable;
}

Built-In Drag-and-Drop Engine in Tile View

Enable the AllowDrag option to support drag-and-drop operations within the TileView.

Tile View - Drag-and-Drop Operations

To access options that specify drag-and-drop operations, use the TileView.OptionsDragDrop property. For example, you can disable the ShowDropIndicators option to hide indicators that show where a tile will be inserted.

tileView1.OptionsDragDrop.AllowDrag = true;
tileView1.OptionsDragDrop.ShowDropIndicators = false;

The Kanban Board arranges tiles into groups (see Groups). You can use the following options to enable drag-and-drop operations within and between tile groups:

  • AllowItemDrag — Gets or sets whether to disable tile drag-and-drop operations within and from the current group.
  • DropTargetGroups — The collection of groups that can accept tiles from the current group during drag-and-drop operations.

Customize Drag-and-Drop Operations

Use the TileView‘s following events to customize drag-and-drop operations:

  • BeforeItemDrag — Fires when a drag operation is about to be started. In an event handler, you can specify the dragged tile image or cancel the operation.
  • ItemDrag — Fires repeatedly when a user drags a tile. In an event handler, you can prevent a tile from being dropped at a specific position.
  • BeforeItemDrop — Fires before a tile is dropped.
  • ItemDrop — Fires when a tile is dropped.

The code below cancels drag operations if a user starts to drag a specific tile.

private void tileView1_BeforeItemDrag(object sender, BeforeItemDragEventArgs e) {
    DevExpress.XtraGrid.Views.Tile.TileView tileView = sender as Views.Tile.TileView;
    bool sold = (int)tileView.GetRowCellValue(e.RowHandle, tileView.Columns["Status"]) == 1;
    if (sold)
        e.Cancel = true;
}

Standard .NET Drag-and-Drop Engine

To implement drag-and-drop operations between the GridControl and any other control, use the standard .NET drag-and-drop engine. See the DoDragDrop method for more information.

Reorder Grid Rows

To enable drag-and-drop operations within the GridControl, do the following:

  • Handle the MouseDown event to obtain the clicked row.
  • Handle the MouseMove event and call the DoDragDrop method to initiate a drag operation.
  • Handle the DragOver event to update the mouse pointer based on its location within the grid.
  • Handle the DragDrop event to move the dragged row.

View Example

The example’s 17.2.4+ branch uses the Drag-and-Drop Behavior. Checkout the 13.1.4+ branch to see the example that uses the standard .NET engine (to do this, you can call git checkout 13.1.4+ in Git Bash).

GridHitInfo downHitInfo = null;

private void gridView1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) {
    GridView view = sender as GridView;
    downHitInfo = null;

    GridHitInfo hitInfo = view.CalcHitInfo(new Point(e.X, e.Y));
    if ( Control.ModifierKeys != Keys.None )
        return;
    if ( e.Button == MouseButtons.Left && hitInfo.InRow && hitInfo.RowHandle != GridControl.NewItemRowHandle )
        downHitInfo = hitInfo;
}

private void gridView1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) {
    GridView view = sender as GridView;
    if ( e.Button == MouseButtons.Left && downHitInfo != null ) {
        Size dragSize = SystemInformation.DragSize;
        Rectangle dragRect = new Rectangle(new Point(downHitInfo.HitPoint.X - dragSize.Width / 2,
            downHitInfo.HitPoint.Y - dragSize.Height / 2), dragSize);

        if ( !dragRect.Contains(new Point(e.X, e.Y)) ) {
            view.GridControl.DoDragDrop(downHitInfo, DragDropEffects.All);
            downHitInfo = null;
        }
    }
}

private void gridControl1_DragOver(object sender, DragEventArgs e) {
    if ( e.Data.GetDataPresent(typeof(GridHitInfo)) ) {
        GridHitInfo downHitInfo = e.Data.GetData(typeof(GridHitInfo)) as GridHitInfo;
        if ( downHitInfo == null )
            return;

        GridControl grid = sender as GridControl;
        GridView view = grid.MainView as GridView;
        GridHitInfo hitInfo = view.CalcHitInfo(grid.PointToClient(new Point(e.X, e.Y)));
        if ( hitInfo.InRow && hitInfo.RowHandle != downHitInfo.RowHandle && hitInfo.RowHandle != GridControl.NewItemRowHandle )
            e.Effect = DragDropEffects.Move;
        else
            e.Effect = DragDropEffects.None;
    }
}

private void gridControl1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) {
    GridControl grid = sender as GridControl;
    GridView view = grid.MainView as GridView;
    GridHitInfo srcHitInfo = e.Data.GetData(typeof(GridHitInfo)) as GridHitInfo;
    GridHitInfo hitInfo = view.CalcHitInfo(grid.PointToClient(new Point(e.X, e.Y)));
    int sourceRow = srcHitInfo.RowHandle;
    int targetRow = hitInfo.RowHandle;
    MoveRow(sourceRow, targetRow);
}

private void MoveRow(int sourceRow, int targetRow) {
    if ( sourceRow == targetRow || sourceRow == targetRow + 1 )
        return;

    GridView view = gridView1;
    DataRow row1 = view.GetDataRow(targetRow);
    DataRow row2 = view.GetDataRow(targetRow + 1);
    DataRow dragRow = view.GetDataRow(sourceRow);
    decimal val1 = (decimal)row1[OrderFieldName];
    if ( row2 == null )
        dragRow[OrderFieldName] = val1 + 1;
    else {
        decimal val2 = (decimal)row2[OrderFieldName];
        dragRow[OrderFieldName] = (val1 + val2) / 2;
    }
}

Drag Data from Grid to Scheduler

You can enable users to drag data from the GridControl to the SchedulerControl and create appointments based on that dragged data.

image

View Example

The example below shows how to create an application that allows users to drag a data row from a data grid to a scheduler. Users can also drag data from/to another application. The application automatically creates a new appointment based on the dragged data.

The code below handles the following events:

  • The grid view’s MouseDown event — the event handler retrieves the clicked visual element. If the user clicks a row, a GridHitInfo object that contains information about this row is assigned to a property on the form for later use.
  • The grid view’s MouseMove event — the event handler calls the grid control’s DoDragDrop to initiate a drag-and-drop operation if the left mouse button is not released within the DragSize rectangle.

    The event handler creates a DataObject instance object that contains dragged rows. This object is then passed to the grid control’s DoDragDrop method and can be retrieved in the target control.

  • The scheduler’s PrepareDragData event — the event handler creates a SchedulerDragData object that contains new appointments created based on the IDataObject instance received from the source control.
  • The scheduler’s AppointmentDrop event — the event handler displays confirmation.

See the following help topic for more information: Drag-and-Drop Operations in Scheduler.

using DevExpress.XtraScheduler;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraGrid.Views.Grid.ViewInfo;

GridHitInfo DownHitInfo { get; set; }

void OnGridViewTasksMouseDown(object sender, MouseEventArgs e) {
    GridView view = sender as GridView;
    this.DownHitInfo = null;

    GridHitInfo hitInfo = view.CalcHitInfo(new Point(e.X, e.Y));
    if (Control.ModifierKeys != Keys.None)
        return;
    if (e.Button == MouseButtons.Left && hitInfo.InRow && hitInfo.HitTest != GridHitTest.RowIndicator)
        this.DownHitInfo = hitInfo;
}

void OnGridViewTasksMouseMove(object sender, MouseEventArgs e) {
    GridView view = sender as GridView;
    if (e.Button == MouseButtons.Left && this.DownHitInfo != null) {
        Size dragSize = SystemInformation.DragSize;
        Rectangle dragRect = new Rectangle(new Point(this.DownHitInfo.HitPoint.X - dragSize.Width / 2,
            this.DownHitInfo.HitPoint.Y - dragSize.Height / 2), dragSize);

        if (!dragRect.Contains(new Point(e.X, e.Y))) {
            view.GridControl.DoDragDrop(GetDragData(view), DragDropEffects.All);
            this.DownHitInfo = null;
        }
    }
}

IDataObject GetDragData(GridView view) {
    int[] selection = view.GetSelectedRows();
    if (selection == null)
        return null;

    List<AppointmentExchangeData> exchangeList = new List<AppointmentExchangeData>();
    int count = selection.Length;
    for (int i = 0; i < count; i++) {
        int rowIndex = selection[i];
        exchangeList.Add(new AppointmentExchangeData() {
            Subject = (string)view.GetRowCellValue(rowIndex, "Subject"),
            LabelKey = (int)view.GetRowCellValue(rowIndex, "Severity"),
            StatusKey = (int)view.GetRowCellValue(rowIndex, "Priority"),
            Start = DateTime.MinValue,
            Duration = TimeSpan.FromHours((int)view.GetRowCellValue(rowIndex, "Duration")),
            Description = (string)view.GetRowCellValue(rowIndex, "Description"),
        });
    }

    return new DataObject(DataFormats.Serializable, exchangeList);
}

void OnSchedulerControlPrepareDragData(object sender, PrepareDragDataEventArgs e) {
    object data = e.DataObject.GetData(DataFormats.Serializable);
    AppointmentBaseCollection appointments = new AppointmentBaseCollection();
    foreach (AppointmentExchangeData item in (IList)data) {
        var apt = this.schedulerStorage.CreateAppointment(AppointmentType.Normal);
        apt.Subject = item.Subject;
        apt.Description = item.Description;
        apt.Start = item.Start;
        apt.Duration = item.Duration;
        apt.LabelKey = item.LabelKey;
        apt.StatusKey = item.StatusKey;
        appointments.Add(apt);
    }
    SchedulerDragData schedulerDragData = new SchedulerDragData(appointments);
    e.DragData = schedulerDragData;
}

void OnSchedulerControlAppointmentDrop(object sender, AppointmentDragEventArgs e) {
    string createEventMsg = "Creating an event at {0} on {1}.";
    string moveEventMsg = "Moving the event from {0} on {1} to {2} on {3}.";

    DateTime srcStart = e.SourceAppointment.Start;
    DateTime newStart = e.EditedAppointment.Start;

    string msg = (srcStart == DateTime.MinValue) ? String.Format(createEventMsg, newStart.ToShortTimeString(), newStart.ToShortDateString()) :
        String.Format(moveEventMsg, srcStart.ToShortTimeString(), srcStart.ToShortDateString(), newStart.ToShortTimeString(), newStart.ToShortDateString());

    if (XtraMessageBox.Show(msg + "\r\nProceed?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) {
        e.Allow = false;
    }
}

Drag Data from Scheduler to Grid

You can enable users to drag appointments from a SchedulerControl to GridControl and create grid rows based on the dragged appointments.

image

The code below handles the following events:

  • The data grid’s DragEnter event — the event handler notifies a user that they can drop data.
  • The data grid’s DragDrop event — the event handler creates grid rows based on the dragged appointment.
using DevExpress.XtraGrid;
using DevExpress.XtraScheduler;
using System.Data;

gridControl1.AllowDrop = true;
gridControl1.DragDrop += GridControl1_DragDrop;
gridControl1.DragEnter += GridControl1_DragEnter;

private void GridControl1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) {
    e.Effect = System.Windows.Forms.DragDropEffects.All;
}
private void GridControl1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e) {
    GridControl gridControl = sender as GridControl;
    SchedulerDragData data = e.Data.GetData(typeof(SchedulerDragData)) as SchedulerDragData;
    if (data != null) {
        foreach (var apt in data.Appointments) {
            var row = apt.GetRow(schedulerControl.DataStorage) as DataRowView;
            gridDataTable.Rows.Add(row.Row.ItemArray);
        }
    }
}

Cheat Sheets and Best Practices

Read the following quick-reference guide for general information and examples:

How to Drag-and-Drop Within/Between UI Controls