Back to Devexpress

Drag-and-Drop Appointments

windowsforms-401540-controls-and-libraries-scheduler-drag-and-drop-operations.md

latest17.8 KB
Original Source

Drag-and-Drop Appointments

  • Sep 03, 2023
  • 8 minutes to read

The control allows users to drag an appointment within the control bounds to reschedule the appointment and drag data from another control or application to create a new appointment based on the dragged data.

Note

Run the XtraScheduler demo and try to drag an appointment to a new time slot. Click Open Solution in the ribbon for the source codes.

Drag Specific Appointments Only

The control allows users to drag any appointment. Use the SchedulerControl.OptionsCustomization.AllowAppointmentDrag property to disable drag-and-drop operations or enable them for non-recurring/recurring/specific appointments only:

The code below shows how to handle the AllowAppointmentDrag event to prevent all-day appointments from being dragged.

csharp
schedulerControl1.OptionsCustomization.AllowAppointmentDrag = UsedAppointmentType.Custom;

private void SchedulerControl1_AllowAppointmentDrag(object sender, AppointmentOperationEventArgs e) {
    e.Allow = !e.Appointment.AllDay;
}
vb
schedulerControl1.OptionsCustomization.AllowAppointmentDrag = UsedAppointmentType.Custom

Private Sub SchedulerControl1_AllowAppointmentDrag(ByVal sender As Object, ByVal e As AppointmentOperationEventArgs) _
    Handles schedulerControl1.AllowAppointmentDrag
    e.Allow = Not e.Appointment.AllDay
End Sub

Note

Even if the SchedulerControl.OptionsCustomization.AllowAppointmentDrag option is enabled, users cannot drag appointments if the SchedulerControl.OptionsCustomization.AllowAppointmentEdit option is disabled.

Prevent Appointments from Being Dragged Between Resources

An appointment can have an associated resource. For example, in a car-sharing service — a resource is a vehicle. Resources are only displayed when appointments are grouped by resource or date. Use the SchedulerControl.GroupType property to group appointments.

Users can drag an appointment from one resource to another. Use the SchedulerControl.OptionsCustomization.AllowAppointmentDragBetweenResources property to disable this feature or enable it for non-recurring/recurring/specific appointments only. To allow users to drag a specific appointment, handle the SchedulerControl.AllowAppointmentDragBetweenResources event.

The figure below demonstrates a car rent scheduler that allows the user to change a rent time but not to change the rented vehicle.

Options and Events

The SchedulerControl.OptionsDragDrop property provides access to options that allow you to customize drag-and-drop operations. For example, you can specify whether the control is automatically scrolled when a user drags an appointment beyond the control’s top or bottom bound.

You can also handle the following events to customize drag-and-drop operations:

  • AppointmentDrag — fires repeatedly when an appointment is being dragged within the control bounds. Provides access to the previous appointment and the appointment that is about to be created. Allows you can cancel the operation.

  • AppointmentsDrag — fires after the last AppointmentDrag event was raised.

  • AppointmentDrop — fires when a user drops an appointment.

  • AppointmentsDrag — fires after all AppointmentDrag events.

  • PrepareDragData — fires when a user drags data into the control from another control and allows you to create appointments based on the dragged data.

Drag Data from Other Controls

You can allow users to drag data from other controls or applications and create appointments based on dragged data.

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 clicked a row, a GridHitInfo object that contains information about this row is assigned to a property on the form for the subsequent 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 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 shows a confirmation.

  • C#

  • VB.NET

csharp
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;
    }
}
vb
Imports DevExpress.XtraScheduler
Imports DevExpress.XtraGrid.Views.Grid
Imports DevExpress.XtraGrid.Views.Grid.ViewInfo

Private Property DownHitInfo() As GridHitInfo

Private Sub OnGridViewTasksMouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles gridViewTasks.MouseDown
    Dim view As GridView = TryCast(sender, GridView)
    Me.DownHitInfo = Nothing

    Dim hitInfo As GridHitInfo = view.CalcHitInfo(New Point(e.X, e.Y))
    If Control.ModifierKeys <> Keys.None Then
        Return
    End If
    If e.Button = MouseButtons.Left AndAlso hitInfo.InRow AndAlso hitInfo.HitTest <> GridHitTest.RowIndicator Then
        Me.DownHitInfo = hitInfo
    End If
End Sub

Private Sub OnGridViewTasksMouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles gridViewTasks.MouseMove
    Dim view As GridView = TryCast(sender, GridView)
    If e.Button = MouseButtons.Left AndAlso Me.DownHitInfo IsNot Nothing Then
        Dim dragSize As Size = SystemInformation.DragSize
        Dim dragRect As New Rectangle(New Point(Me.DownHitInfo.HitPoint.X - dragSize.Width \ 2, Me.DownHitInfo.HitPoint.Y - dragSize.Height \ 2), dragSize)

        If Not dragRect.Contains(New Point(e.X, e.Y)) Then
            view.GridControl.DoDragDrop(GetDragData(view), DragDropEffects.All)
            Me.DownHitInfo = Nothing
        End If
    End If
End Sub

Private Function GetDragData(ByVal view As GridView) As IDataObject
    Dim selection() As Integer = view.GetSelectedRows()
    If selection Is Nothing Then
        Return Nothing
    End If

    Dim exchangeList As New List(Of AppointmentExchangeData)()
    Dim count As Integer = selection.Length
    For i As Integer = 0 To count - 1
        Dim rowIndex As Integer = selection(i)
        exchangeList.Add(New AppointmentExchangeData() With {
            .Subject = CStr(view.GetRowCellValue(rowIndex, "Subject")),
            .LabelKey = CInt(Math.Truncate(view.GetRowCellValue(rowIndex, "Severity"))),
            .StatusKey = CInt(Math.Truncate(view.GetRowCellValue(rowIndex, "Priority"))),
            .Start = Date.MinValue,
            .Duration = TimeSpan.FromHours(CInt(Math.Truncate(view.GetRowCellValue(rowIndex, "Duration")))),
            .Description = CStr(view.GetRowCellValue(rowIndex, "Description"))
        })
    Next i

    Return New DataObject(DataFormats.Serializable, exchangeList)
End Function

Private Sub OnSchedulerControlPrepareDragData(ByVal sender As Object, ByVal e As PrepareDragDataEventArgs) Handles schedulerControl.PrepareDragData
    Dim data As Object = e.DataObject.GetData(DataFormats.Serializable)
    Dim appointments As New AppointmentBaseCollection()
    For Each item As AppointmentExchangeData In DirectCast(data, IList)
        Dim apt = Me.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)
    Next item
    Dim schedulerDragData As New SchedulerDragData(appointments)
    e.DragData = schedulerDragData
End Sub

Private Sub OnSchedulerControlAppointmentDrop(ByVal sender As Object, ByVal e As AppointmentDragEventArgs) Handles schedulerControl.AppointmentDrop
    Dim createEventMsg As String = "Creating an event at {0} on {1}."
    Dim moveEventMsg As String = "Moving the event from {0} on {1} to {2} on {3}."

    Dim srcStart As Date = e.SourceAppointment.Start
    Dim newStart As Date = e.EditedAppointment.Start

    Dim msg As String = If(srcStart = Date.MinValue, String.Format(createEventMsg, newStart.ToShortTimeString(), newStart.ToShortDateString()), String.Format(moveEventMsg, srcStart.ToShortTimeString(), srcStart.ToShortDateString(), newStart.ToShortTimeString(), newStart.ToShortDateString()))

    If XtraMessageBox.Show(msg & vbCrLf & "Proceed?", Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.No Then
        e.Allow = False
    End If
End Sub

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.

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.

  • C#

  • VB.NET

csharp
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);
        }
    }
}
vb
Imports DevExpress.XtraGrid
Imports DevExpress.XtraScheduler
Imports System.Data

gridControl1.AllowDrop = True
AddHandler gridControl1.DragDrop, AddressOf GridControl1_DragDrop
AddHandler gridControl1.DragEnter, AddressOf GridControl1_DragEnter

Private Sub GridControl1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs)
    e.Effect = System.Windows.Forms.DragDropEffects.All
End Sub

Private Sub GridControl1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs)
    Dim gridControl As GridControl = TryCast(sender, GridControl)
    Dim data As SchedulerDragData = TryCast(e.Data.GetData(GetType(SchedulerDragData)), SchedulerDragData)

    If data IsNot Nothing Then
        For Each apt In data.Appointments
            Dim row = TryCast(apt.GetRow(schedulerControl.DataStorage), DataRowView)
            gridDataTable.Rows.Add(row.Row.ItemArray)
        Next
    End If
End Sub