Custom Appointment
A very common scenario when using RadScheduleView is the usage of custom appointments. When you create a Custom Appointment class you gain the ability to append additional properties to the base Appointment class, to add them to your custom AppointmentItem and optionally to its ToolTip, display them in the EditAppointment dialog while supporting the cancelation of editing.
In this article we will explore the process of using custom appointments in RadScheduleView. We will go through the following steps:
Create a custom appointment class and populate the RadScheduleView control with custom appointments
Customize the AppointmentItem and its ToolTip to display the new data
Creating a Custom Appointment Class
To create a custom appointment class you can start off with either of the following approaches : you can implement the IAppointment interface or you can inherit from one of the classes that already implement this interface – AppointmentBase (the base implementation of the interface) or Appointment (an extended implementation). It is very important to provide your own implementations for the Copy and CopyFrom methods as they are used intensively in the editing process of the ScheduleView control. If you choose to implement the interface, the Copy and CopyFrom methods will be automatically implemented for you, but if you take the second approach and inherit from one of the base classes, you should keep this in mind.
Let's create a simple task tracking system. For our Custom Appointment class we will inherit from Appointment. Our tracking system will need to show an additional field for the task progress – an indication of whether the task has finished or not. In order to enable editing in transactions of the new property we need to use the Storage method of the AppointmentBase class to access the instance which owns the fields. We will name our custom appointment class Task. Example 1 shows the creation of the Custom Appointment class:
When inheriting the AppointmentBase class it is required to create a parameter-less constructor for the the custom class.
Example 1: Create Custom Appointment
public class Task:Appointment
{
private bool isDone;
public bool IsDone
{
get
{
return this.Storage<Task>().isDone;
}
set
{
var storage = this.Storage<Task>();
if (storage.isDone != value)
{
storage.isDone = value;
this.OnPropertyChanged(() => this.IsDone);
}
}
}
public override IAppointment Copy()
{
var newAppointment = new Task();
newAppointment.CopyFrom(this);
return newAppointment;
}
public override void CopyFrom(IAppointment other)
{
var task = other as Task;
if (task != null)
{
this.IsDone = task.IsDone;
}
base.CopyFrom(other);
}
}
Public Class Task
Inherits Appointment
Private m_isDone As Boolean
Public Property IsDone() As Boolean
Get
Return Me.Storage(Of Task)().m_isDone
End Get
Set
Dim storage = Me.Storage(Of Task)()
If storage.m_isDone <> value Then
storage.m_isDone = value
Me.OnPropertyChanged("IsDone")
End If
End Set
End Property
Public Overrides Function Copy() As IAppointment
Dim newAppointment = New Task()
newAppointment.CopyFrom(Me)
Return newAppointment
End Function
Public Overrides Sub CopyFrom(other As IAppointment)
Dim task = TryCast(other, Task)
If task IsNot Nothing Then
Me.IsDone = task.IsDone
End If
MyBase.CopyFrom(other)
End Sub
End Class
For the next step, it is important to set the AppointmentsSource of RadScheduleView to be of type IList
Example 2: Create the TasksCollection
public MainWindow()
{
InitializeComponent();
// "this.scheduleView" refers to the RadScheduleView instance that we are targetting
this.scheduleView.AppointmentsSource = new TasksCollection();
}
public class TasksCollection : ObservableCollection<Task>
{
public TasksCollection()
{
DateTime today = DateTime.Today;
foreach (Task t in Enumerable.Range(9, 14).Select(i =>
new Task
{
Start = today.AddMinutes(i * 60 + 15),
End = today.AddMinutes((i + 1) * 60),
Subject = string.Format("Task num. {0}",i),
IsDone = today.AddMinutes((i + 1) * 60) < DateTime.Now
}))
{
this.Add(t);
}
}
}
Public Sub New()
InitializeComponent()
' "this.scheduleView" refers to the RadScheduleView instance that we are targetting
Me.scheduleView.AppointmentsSource = New TasksCollection()
End Sub
Public Class TasksCollection
Inherits ObservableCollection(Of Task)
Public Sub New()
Dim today As Date = Date.Today
For Each t As Task In Enumerable.Range(9, 14).Select(Function(i) New Task With {
.Start = today.AddMinutes(i * 60 + 15),
.End = today.AddMinutes((i + 1) * 60),
.Subject = String.Format("Task num. {0}",i),
.IsDone = today.AddMinutes((i + 1) * 60) < Date.Now
})
Me.Add(t)
Next t
End Sub
End Class
Figure 1: Result from Example 2
Creating a Custom Appointment Dialog
In order to create a custom appointment dialog we are going to modify the EditAppointmentDialogStyle property of RadScheduleView control. The DataContext of the SchedulerDialog, which is the TargetType of this style is an AppointmentDialogViewModel object. This class contains all needed data for editing an appointment including the Appointment itself. It can be reached by using the Occurrence property of the ViewModel and subsequently the Appointment property of Occurrence.
Now that we have our custom IsDone property, let's add a CheckBox for it and bind to it. In order to do that, you need to extract and modify the default ControlTemplate of the EditAppointmentDialog with x:Key="EditAppointmentTemplate". You can locate it in the "Themes.Implicit\"your theme"\Themes\" directory inside the UI for WPF installation folder. The template along with the style through which it can be applied with x:Key="EditAppointmentDialogStyle", can be found in the "Telerik.Windows.Controls.ScheduleView.xaml" file. For more information about extracting and modifying the dialog ControlTemplates, check out the How to extract the default styles of the dialogs section in our documentation.
After you have extracted the default EditAppointmentTemplate, you can add a CheckBox as demonstrated in Example 3 which can replace the CheckBox with x:Name="AllDayEventCheckbox".
Example 3: Bind the IsDone property
<CheckBox Grid.Row="4" Grid.Column="1" Margin="3" Content="Is done?" IsChecked="{Binding Occurrence.Appointment.IsDone, Mode=TwoWay}"/>
Figure 2: Result from Example 3
The important thing to note here is that we can bind to our new properties using Occurrence.Appointment.
Changing the Style of the AppointmentItem
Next, we are going to change the ControlTemplate of the AppointmentItem to reflect which tasks are done and which are not. We will do that by using the AppointmentStyleSelector property of the RadScheduleView. For more information on extracting and modifying the default style selector, check out the Appointment Style article. For the purposes of this example we will modify the ControlTemplate of the VerticalStyle property.
The DataContext of the AppointmentItem's ControlTemplate represents an AppointmentProxy, which holds the most important properties of the Appointment and the Appointment itself. We want to display a green dot for all tasks that are done. We will achieve this by adding an Ellipse in the ControlTemplate and then binding its Visibility to the IsDone property of the Task through a converter. The Ellipse demonstrated in Example 4 is added to the ControlTemplate of the VerticalStyle. The ControlTemplate's key is "AppointmentItemVerticalControlTemplate". You can refer to the Editing Control Templates article for more information about extracting ControlTemplates.
Example 4: Indicate the status of an appointment with an Ellipse
<Ellipse Fill="Green" Width="12" Height="12" VerticalAlignment="Top" Margin="10 5 5 5" HorizontalAlignment="Left" Visibility="{Binding Appointment.IsDone, Converter={StaticResource BooleanToVisibilityConverter}}" />
Customizing the Appointment ToolTip
This step is, of course, optional. The customization of the Appointment ToolTip is achieved by setting the ToolTipTemplate property of RadScheduleView. The DataContext in this template is once again of type AppointmentProxy so we can use the same approach we used in the AppointmentItem ControlTemplate.
Example 5 demonstrates how you can modify the Appointment ToolTipTemplate in order to add the text (Done) only for the tasks which are already done:
Example 5: Define a ToolTipTemplate
<DataTemplate x:Key="ToolTipTemplate">
<StackPanel Orientation="Horizontal" MinWidth="140" Margin="0 5">
<TextBlock MaxWidth="200" TextWrapping="Wrap" Text="{Binding Subject}"/>
<TextBlock Text="(Done)" Grid.Row="1" Margin="5 0 5 0" Foreground="#FF191D1A" Visibility="{Binding Appointment.IsDone, Converter={StaticResource BooleanToVisibilityConverter}}" FontStyle="Italic" />
</StackPanel>
</DataTemplate>
Figure 3: Result from Examples 4 and 5
By completing this last modification, we have reached the end of the process needed to create a custom appointment in RadScheduleView control.