Drag and Drop with RadScheduler
RadScheduler uses DragDropManager to implement drag and drop of appointments. In order to add some custom logic for drag and drop, you can inherit the Telerik.Windows.Controls.SchedulerDragDropBehavior class. The class provides several methods that you can override to implement custom drag and drop functionality. This article will demonstrate all of the available methods.
important In order to drag an appointment the following file need to be merged in the App.xaml file:
Example
<Application x:Class="App1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<ResourceDictionary Source="ms-appx:///Telerik.WinUI.Controls/Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
DragDropState Parameter
Each of the methods provided in the SchedulerDragDropBehavior class receives an object of type DragDropState. The object contains all the needed information in order to help you achieve the required drag and drop and resize functionality. The object provides the following properties:
Appointment: Holds a reference to the appointment that is held with the mouse.
DestinationAppointmentsSource: Holds a reference to the AppointmentsSource collection of the drag destination.
DestinationSlots: Gets a collection containing the new slots of the dragged Appointments.
DraggedAppointments: Contains all appointments that are being dragged, including the appointment that is selected while the user clicks and holds the mouse button. When resizing this property is null.
IsControlPressed: Gets or sets a value indicating the control key is pressed.
ServiceProvider: Gets the IServiceProvider associated with the current instance.
SlotDuration: Gets the duration between two minor ticks.
SourceAppointmentsSource: Holds a reference to the AppointmentsSource collection of the drag source.
SourceResources: Gets a collection containing the original resources from which the dragging operation has started.
TargetedAppointment: Holds a reference to the targeted appointment on which the drop operation will be performed.
SchedulerDragDropBehavior class
The following section will provide some detailed information about the following methods and properties of the SchedulerDragDropBehavior class:
Methods
- CanDrop
- CanStartDrag
- Drop
- CanResize
- CanStartResize
- Resize
- ConvertDraggedData
- CoerceDraggedItems
- DragDropCompleted
- DragDropCanceled
Properties
CanDrop(DragDropState state)
The CanDrop(DragDropState state) method gets the value specifying whether the drag and drop operation can be finished or not. The method is called multiple times during the operation. On each call you can check if a requirement is fulfilled in order to prevent or allow the completion of the drop. For example, you can easily prevent copying Appointments when dragging them with the Control Key pressed by returning false if the key is pressed:
Example 1: Overriding the CanDrop method
public override bool CanDrop(DragDropState state)
{
if (state.IsControlPressed)
{
return false;
}
return base.CanDrop(state);
}
CanStartDrag(DragDropState state)
CanStartDrag(DragDropState state): Gets the value specifying whether the drag operation can be performed or not. The method is called at the moment when the operation is starting. For example, if you need to prevent dragging of more than two Appointments, you can override the method the following way:
Example 2: Overriding the CanStartDrag method
public override bool CanStartDrag(DragDropState state)
{
if (state.DraggedAppointments.Count() > 2)
{
return false;
}
return base.CanStartDrag(state);
}
Drop(DragDropState state)
Drop(DragDropState state): The method is called right after the Appointment is dropped and before the DragDropCompleted method is called. For example, if you want to add a Category to the just dropped Appointment, you can do it as shown below:
Example 3: Override the Drop method
public override void Drop(DragDropState state)
{
var appointment = state.Appointment as Appointment;
if (appointment.Category == null)
{
appointment.Category = new Category("Green Category", Brushes.Green);
}
base.Drop(state);
}
CanResize(DragDropState state)
CanResize(DragDropState state): Gets the value specifying whether the resize operation can be finished or not. The method is called multiple times during the Resize operation and you can easily stop the operation by returning false. The following example demonstrates how to stop the resizing when the duration becomes more than two hours and less than half an hour.
Example 4: Override the CanResize method
public override bool CanResize(DragDropState state)
{
var destinationSlot = state.DestinationSlots.First() as Slot;
var duration = destinationSlot.End - destinationSlot.Start;
if (duration <= new TimeSpan(0, 30, 0) || duration > new TimeSpan(2, 0, 0))
{
return false;
}
return base.CanResize(state);
}
CanStartResize(DragDropState state)
CanStartResize(DragDropState state): Gets the value specifying whether the resize operation can be performed or not. The method is called at the moment when the operation is starting. For example, if you need to prevent the resizing of an Appointment that is marked with the Busy TimeMarker, you will need to do it the following way:
Example 5: Override the CanStartResize method
public override bool CanStartResize(DragDropState state)
{
var appointment = state.Appointment as Appointment;
if (appointment.TimeMarker != null && appointment.TimeMarker.Equals(TimeMarker.Busy))
{
return false;
}
return base.CanStartResize(state);
}
Resize(DragDropState state)
Resize(DragDropState state): The method is called right after the resize operation is finished. You can find the initial Appointment in the Appointment property of the DragDropState and the new information in the DestinationSlot property. So, for example, you can easily modify the Appointment subject after resizing in order to include the new duration as shown below:
Example 6: Override the Resize method
public override void Resize(DragDropState state)
{
var appointment = state.Appointment as Appointment;
var destinationSlot = state.DestinationSlots.First() as Slot;
var duration = destinationSlot.End - destinationSlot.Start;
appointment.Subject = "New duration: " + duration.ToString("h\:mm\:ss");
base.Resize(state);
}
ConvertDraggedData(object data)
By default, the ConvertDraggedData method will be called constantly. This behavior can be modified through the CacheConvertedDragData property.
ConvertDraggedData(Object data): This method is fired when you drag appointments from a different source (another control or application) and is used to convert the data to an appointment. For example, if you are dragging from a ListBox to Scheduler and you have objects of type Customer as an ItemsSource of the ListBox, you can convert the dragged Customer object to an Appointment the following way:
Example 7: Override the ConvertDraggedData method
public override IEnumerable<IOccurrence> ConvertDraggedData(object data)
{
if (Telerik.Windows.DragDrop.Behaviors.DataObjectHelper.GetDataPresent(data, typeof(Meeting), false))
{
var customers = Telerik.Windows.DragDrop.Behaviors.DataObjectHelper.GetData(data, typeof(Customer), true) as IEnumerable;
if (customers != null)
{
var newApp = customers.OfType<Customer>().Select(c => new Appointment { Subject = c.Name });
return newApp;
}
}
return base.ConvertDraggedData(data);
}
CoerceDraggedItems(DragDropState state)
CoerceDraggedItems(DragDropState state): Initializes the drag operation. This method could be used to filter the selected appointments. This method allows adding or removing Appointments to the selection depending on a specific condition. For example, if you need to filter the dragged Appointments by the Resource in order to move all the Appointments with the current Resource, you do it the following way:
Example 8: Override the CoerceDraggedItems method
public override IEnumerable<IOccurrence> CoerceDraggedItems(DragDropState state)
{
var resource = (state.Appointment as Appointment).Resources.First();
var allAppointments = state.SourceAppointmentsSource.Cast<IOccurrence>();
var desiredAppointments = allAppointments.Where(a => (a as Appointment).Resources.Any(r => r == resource));
return desiredAppointments;
}
DragDropCompleted(DragDropState state)
DragDropCompleted( DragDropState state): This method is called when the drag-drop operation is finished.
DragDropCanceled(DragDropState state)
DragDropCanceled( DragDropState state): The method is called whenever the execution of the operation has failed for some reason.
Setting the CustomDragDropBehavior
After the CustomDragDropBehavior is implemented, all you need is to set it as RadScheduler.DragDropBehavior:
Example 9: Set the CustomDragDropBehavior
<telerik:RadScheduler.DragDropBehavior>
<local:CustomDragDropBehavior />
</telerik:RadScheduler.DragDropBehavior>
ResizeCursor
Using the ResizeCursor you can change the cursor that indicates a resize operation is happening. That could be done initially or while resizing an Appointment, for example, inside the CanResize method:
Example 10: Setting the ResizeCursor property
public override bool CanResize(DragDropState state)
{
var destinationSlot = state.DestinationSlots.First() as Slot;
var duration = destinationSlot.End - destinationSlot.Start;
if (duration <= new TimeSpan(0, 30, 0) || duration >= new TimeSpan(2, 0, 1))
{
this.ResizeCursor = Cursors.Wait;
return false;
}
this.ResizeCursor = Cursors.SizeNS;
return base.CanResize(state);
}
The default cursor is represented by setting the ResizeCursor property value to null.
CacheConvertedDragData
SchedulerDragDropBehavior exposes the CacheConvertedDragData property. Its default value is False, meaning that the ConvertDraggedData method will be called repeatedly. When the property is set to True, the method will be called only once and the converted data will be cached.