We have some great news for those who asked us for advanced drag&drop customization features in the WPF Scheduler. We had requests for the following functionality:
- To provide control over an ongoing drag&drop operation
- To simplify event data and make it more consistent across different operations
- To customize visual feedback during a drag&drop operation
- To implement dragging to or from an external control or application
To facilitate all this, we redesigned our drag&drop engine for v19.1. New events are now available that give you control over drag&drop operations at all stages.
Note: The new events logically replace SchedulerControl.AppointmentDrag and SchedulerControl.AppointmentDrop. These two old events will remain in place for backwards compatibility. However, you cannot use a mix of old and new events and you will see exceptions thrown by the SchedulerControl if you do.
Here is a diagram of the new event flow:
Internal Drag&Drop
An ongoing scheduler-internal drag operation is controlled by three events, each suited to specific tasks.
- The event
QueryContinueAppointmentDrag
lets you handle user actions - The event
DragAppointmentOver
is used to process appointments under the cursor, to detect and resolve conflicts - Finally, the event
GiveAppointmentDragFeedback
allows you to change the cursor style for visual feedback
Each event has its own set of parameters. This structure makes it easier to develop and test customizations for drag&drop operations.
Here is an example implementation. With the help of the new events, the code detects when dragged appointments conflict with others and indicates the problem by assigning a label.
private void Scheduler_DragAppointmentOver(object sender, DragAppointmentOverEventArgs e) { // if a dragged appointment intersects with another appointment, // paint it red to indicate a conflict for (int i = 0; i < e.ConflictedAppointments.Count; i++) if (e.ConflictedAppointments[i].Count > 0) e.DragAppointments[i].LabelId = 1; // reset the label if no conflicts are detected for (int j = 0; j < e.ConflictedAppointments.Count; j++) if (e.ConflictedAppointments[j].Count == 0) e.DragAppointments[j].LabelId = e.SourceAppointments[j].LabelId; } private void Scheduler_DropAppointment(object sender, DropAppointmentEventArgs e) { for (int i = 0; i < e.DragAppointments.Count; i++) { // if a dragged appointment has no conflicts if (e.ConflictedAppointments[i].Count == 0) { // it can be moved to the target position e.SourceAppointments[i].Start = e.DragAppointments[i].Start; e.SourceAppointments[i].End = e.DragAppointments[i].End; } } }
External Drag&Drop
As you can also see in the diagram above, external drag operations have their own event flow – whether they originate from other controls in the same application or even from other applications.
The following code implements the required logic to accept drags from a GridControl
instance, as well as others that support a string
format.
private void Scheduler_StartAppointmentDragFromOutside(object sender, StartAppointmentDragFromOutsideEventArgs e) { // if the user is dragging GridControl data if (e.Data.GetDataPresent(typeof(RecordDragDropData))) { var rddData = (RecordDragDropData) e.Data.GetData(typeof(RecordDragDropData)); foreach (var appData in rddData.Records.Cast<AppointmentData>()) { var appointment = new AppointmentItem { Subject = appData.Subject, Location = appData.Location }; appointment.End = appointment.Start + appData.Length; e.DragAppointments.Add(appointment); } } // if the user is dragging text if (e.Data.GetDataPresent(typeof(string))) { var subject = (string) e.Data.GetData(typeof(string)); var appointment = new AppointmentItem { Subject = subject }; e.DragAppointments.Add(appointment); } }
Your Thoughts Count
The new drag&drop engine makes it much more convenient for us to add new events or expand existing ones. Please let us know what you think about the new features, or if you have any additional requests.