# Custom Appointment

A very common scenario when using RadScheduler 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.

## 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 implements 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 Scheduler 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:

> important When inheriting the AppointmentBase class it is required to create a parameter-less constructor for the the custom class.

#### **Example 1: Create Custom Appointment**

```C#
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);
	}
}
```

For the next step, it is important to set the **AppointmentsSource** of RadScheduler to be of type **`IList<Task>`**, because this way the Scheduler knows that our custom appointments should be of type **Task**. **Example 2** demonstrates how to create an **BindableCollection\<Task>**.

#### **Example 2: Create the TasksCollection**

```C#
public MainWindow()
{
	InitializeComponent();
	
	// "this.scheduler" refers to the RadScheduler instance that we are targetting
	this.scheduler.AppointmentsSource = new TasksCollection();
}

public class TasksCollection : BindableCollection<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);
		 }
	}
}
```

#### **Figure 1: Result from Example 2**

![WinUI radscheduler-features-appointments-custom-appointment with custom appointments](https://www.telerik.com/winui/documentation/assets/bcb30200fe0e261cebbb6535e21755de/custom_appointment1.png)

## Creating a Custom Appointment Dialog

In order to create a custom appointment dialog we are going to modify the **EditAppointmentDialogStyle** property of **RadScheduler** 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".

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**

```XAML
<Page.Resources>
	<ResourceDictionary>
		<ResourceDictionary.MergedDictionaries>
			<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
			
			<ResourceDictionary Source="ms-appx:///Telerik.WinUI.Controls/Themes/Generic.xaml"/>
		</ResourceDictionary.MergedDictionaries>
		

		 <ControlTemplate x:Key="EditAppointmentTemplate" TargetType="telerik:SchedulerDialog">
			<Grid>
				<Grid.RowDefinitions>
					<RowDefinition Height="Auto" />
					<RowDefinition Height="Auto" />
					<RowDefinition Height="*" />
					<RowDefinition Height="Auto" />
					<RowDefinition Height="Auto" />
					<RowDefinition Height="Auto" />
				</Grid.RowDefinitions>

				<StackPanel x:Name="AppointmentToolbar"
					AutomationProperties.Name="Appointment"
					Background="{ThemeResource SystemChromeLowColor}"
					Orientation="Horizontal" Padding="0 2">
					<telerik:RadButton x:Name="EditRecurrenceButton"
							 Background="{x:Null}" Padding="2 4"
							 AutomationProperties.AutomationId="EditRecurrenceButton" 
							 Command="{core:Static Member=telerik:RadSchedulerCommands.EditRecurrenceRule}"
							 VerticalAlignment="Center"
							 Visibility="{Binding CanEditParentAppointment, Converter={StaticResource BooleanToVisibilityConverter},ConverterParameter=True}"
							 Margin="10 0 0 0">
						<ToolTipService.ToolTip>
							<ToolTip core:LocalizationManager.Key="Scheduler_EditRecurrence" core:LocalizationManager.PropertyName="Content" />
						</ToolTipService.ToolTip>
						<StackPanel Orientation="Horizontal" Margin="2 0">
							<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="&#xE895;"/>
							<TextBlock Margin="5 0"
							   core:LocalizationManager.Key="Scheduler_EditRecurrence"
							   core:LocalizationManager.PropertyName="Text" />
						</StackPanel>
					</telerik:RadButton>
					<telerik:RadButton x:Name="EditParentAppointmentButton"
							 Background="{x:Null}" Padding="2 4"
							 AutomationProperties.AutomationId="EditParentAppointmentButton"
							 IsEnabled="{Binding IsReadOnly, Converter={StaticResource InvertedBooleanConverter}}"
							 Command="{core:Static Member=telerik:RadSchedulerCommands.EditParentAppointment}"
							 VerticalAlignment="Center"
							 Visibility="{Binding CanEditParentAppointment, Converter={StaticResource BooleanToVisibilityConverter}}"
							 Margin="10 0 0 0">
						<ToolTipService.ToolTip>
							<TextBlock core:LocalizationManager.Key="Scheduler_EditParentAppointment" core:LocalizationManager.PropertyName="Text" />
						</ToolTipService.ToolTip>
						<TextBlock Margin="5 0" core:LocalizationManager.Key="Scheduler_EditParentAppointment" core:LocalizationManager.PropertyName="Text" />
					</telerik:RadButton>

					<Rectangle Width="1" Fill="{ThemeResource TelerikScheduler_BorderBrush}" VerticalAlignment="Stretch" Margin="2 0 0 0"/>

					<TextBlock x:Name="ShowAs" Margin="10 0"
					   core:LocalizationManager.Key="Scheduler_ShowAs"
					   core:LocalizationManager.PropertyName="Text"
					   VerticalAlignment="Center"/>

					<ComboBox x:Name="PART_TimeMarkers" AutomationProperties.Name="TimeMarkers"
					  AutomationProperties.AutomationId="PART_TimeMarkers" Width="130"
					  IsEnabled="{Binding IsReadOnly, Converter={StaticResource InvertedBooleanConverter}}"
					  ItemsSource="{Binding TimeMarkers}" PlaceholderText="TimeMarkers"
					  SelectedItem="{Binding Occurrence.Appointment.TimeMarker, Mode=TwoWay}"
					  ItemTemplate="{StaticResource TimeMarkerComboBoxItemContentTemplate}" />
					<ComboBox x:Name="PART_Categories" AutomationProperties.Name="Categories"
					  AutomationProperties.AutomationId="PART_Categories" Margin="10 0 0 0" Width="130"
					  IsEnabled="{Binding IsReadOnly, Converter={StaticResource InvertedBooleanConverter}}"
					  ItemsSource="{Binding Categories}" PlaceholderText="Categorize"
					  SelectedItem="{Binding Occurrence.Appointment.Category, Mode=TwoWay}"
					  ItemTemplate="{StaticResource CategoryComboBoxItemContentTemplate}" />
					<ToggleButton x:Name="HighImportaceButton"
						  Background="Transparent" Width="22" Height="22"
						  Padding="0" Margin="5 0 0 0"
						  AutomationProperties.AutomationId="HighImportaceButton"
						  IsEnabled="{Binding IsReadOnly, Converter={StaticResource InvertedBooleanConverter}}"
						  IsChecked="{Binding IsHighImportance, Mode=TwoWay}">
						<ToolTipService.ToolTip>
							<ToolTip core:LocalizationManager.Key="Scheduler_HighImportance" core:LocalizationManager.PropertyName="Content" />
						</ToolTipService.ToolTip>
						<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}" FontSize="16" Glyph="&#xE8C9;" Foreground="#D90000"/>
					</ToggleButton>
					<ToggleButton x:Name="LowImportaceButton"
						  Background="Transparent" Width="22" Height="22"
						  Padding="0" Margin="5 0 0 0"
						  AutomationProperties.AutomationId="LowImportaceButton"
						  IsEnabled="{Binding IsReadOnly, Converter={StaticResource InvertedBooleanConverter}}"
						  IsChecked="{Binding IsLowImportance, Mode=TwoWay}">
						<ToolTipService.ToolTip>
							<ToolTip core:LocalizationManager.Key="Scheduler_LowImportance" core:LocalizationManager.PropertyName="Content"  />
						</ToolTipService.ToolTip>
						<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}" FontSize="16" Glyph="&#xE74B;"/>
					</ToggleButton>
				</StackPanel>

				<Border x:Name="AppointmentCategory" Grid.Row="1" Height="20" Margin="6 6"
				CornerRadius="3" Background="{Binding SelectedItem.CategoryBrush, ElementName=PART_Categories}"
				Visibility="{Binding SelectedItem, ElementName=PART_Categories, Converter={StaticResource NullToVisibilityConverter}}">
					<TextBlock Margin="6 0" Text="{Binding SelectedItem.DisplayName, ElementName=PART_Categories}" VerticalAlignment="Center" />
				</Border>

				<Grid x:Name="Details" Grid.Row="2" Margin="0 10 0 0" Padding="10">
					<Grid.RowDefinitions>
						<RowDefinition Height="Auto" />
						<RowDefinition Height="*" />
						<RowDefinition Height="Auto" />
						<RowDefinition Height="Auto" />
						<RowDefinition Height="Auto" />
					</Grid.RowDefinitions>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="90" />
						<ColumnDefinition Width="*" />
						<ColumnDefinition Width="*" />
					</Grid.ColumnDefinitions>

					<TextBlock Grid.Row="0" Grid.Column="0" Margin="0 5"
					   core:LocalizationManager.Key="Scheduler_Subject"
					   core:LocalizationManager.PropertyName="Text"/>
					<TextBox AutomationProperties.AutomationId="SubjectTextBox"
					 AutomationProperties.Name="Subject"
					 Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Margin="0 5"
					 IsReadOnly="{Binding IsReadOnly}"
					 Text="{Binding Occurrence.Appointment.Subject, Mode=TwoWay}" />

					<TextBlock Grid.Row="1" Grid.Column="0" Margin="0 5"
					   core:LocalizationManager.Key="Scheduler_Body"
					   core:LocalizationManager.PropertyName="Text" />
					<TextBox AutomationProperties.AutomationId="DescriptionTextBox"
					 AutomationProperties.Name="Description"
					 Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0 5"
					 Height="70" IsReadOnly="{Binding IsReadOnly}"
					 VerticalAlignment="Stretch" VerticalContentAlignment="Stretch"
					 Text="{Binding Occurrence.Appointment.Body, Mode=TwoWay}" TextWrapping="Wrap" />

					<TextBlock Grid.Row="2" Grid.Column="0" Margin="0 5"
					   core:LocalizationManager.Key="Scheduler_StartTime"
					   core:LocalizationManager.PropertyName="Text" />
					<StackPanel x:Name="StartTime" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"
						Orientation="Horizontal" Margin="0 5">
						<DatePicker x:Name="startDatePicker"
							SelectedDate="{Binding ActualStartDatePart, Mode=TwoWay, Converter={StaticResource DateTimeToDateTimeOffsetConverter}}"
							AutomationProperties.Name="StartDate"
							AutomationProperties.AutomationId="StartDatePicker"
							DayFormat="{}{day.integer} ({dayofweek.abbreviated})"
							IsEnabled="{Binding IsNotRecurrent}"/>
						<TimePicker x:Name="startTimePicker"
							SelectedTime="{Binding ActualStartTimeOfDayPart, Mode=TwoWay}"
							AutomationProperties.Name="StartTime"
							AutomationProperties.AutomationId="StartTimePicker"
							MinuteIncrement="15" Margin="2 0 0 0"
							IsEnabled="{Binding IsNotRecurrent}"/>
					</StackPanel>

					<TextBlock Grid.Row="3" Grid.Column="0" Margin="0 5"
					   core:LocalizationManager.Key="Scheduler_EndTime"
					   core:LocalizationManager.PropertyName="Text" />
					<StackPanel x:Name="EndTime" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2"
						Orientation="Horizontal" Margin="0 5">
						<DatePicker x:Name="endDatePicker"
							SelectedDate="{Binding ActualEndDatePart, Mode=TwoWay, Converter={StaticResource DateTimeToDateTimeOffsetConverter}}"
							AutomationProperties.Name="EndDate"
							AutomationProperties.AutomationId="EndDatePicker"
							DayFormat="{}{day.integer} ({dayofweek.abbreviated})"
							IsEnabled="{Binding IsNotRecurrent}"/>
						<TimePicker x:Name="endTimePicker"
							SelectedTime="{Binding ActualEndTimeOfDayPart, Mode=TwoWay}"
							AutomationProperties.Name="EndTime"
							AutomationProperties.AutomationId="EndTimePicker"
							MinuteIncrement="15" Margin="2 0 0 0"
							IsEnabled="{Binding IsNotRecurrent}"/>
					</StackPanel>

					
					<CheckBox Grid.Row="4" Grid.Column="1" Margin="3" Content="Is done?" IsChecked="{Binding Occurrence.Appointment.IsDone, Mode=TwoWay}"/>
				</Grid>

				
				<Grid Grid.Row="3" Visibility="{Binding ResourceTypesVisibility}" Padding="10">
					<ItemsControl x:Name="PART_Resources" IsTabStop="false"
						  BorderBrush="{x:Null}" BorderThickness="0"
						  HorizontalContentAlignment="Stretch"
						  VerticalContentAlignment="Stretch"
						  IsEnabled="{Binding IsReadOnly, Converter={StaticResource InvertedBooleanConverter}}"
						  ItemsSource="{Binding ResourceTypes}"
						  ItemTemplateSelector="{StaticResource ResourcesEditorItemTemplateSelector}"
						  Visibility="{Binding ResourceTypesVisibility}" />

					<TextBlock HorizontalAlignment="Left" Text="You can only change the resources by opening the series"
					   Visibility="{Binding ResourceTypesVisibility, Converter={StaticResource InvertedVisibilityConverter}}" />
				</Grid>

				<StackPanel Grid.Row="4" Margin="0 0 10 10" HorizontalAlignment="Right" Orientation="Horizontal">
					<telerik:RadButton AutomationProperties.AutomationId="OKButton"
							 CornerRadius="2" MinWidth="80"
							 Command="{core:Static Member=telerik:WindowCommands.Confirm}"
							 telerik:RadWindow.ResponseButton="Accept"
							 core:LocalizationManager.Key="Scheduler_Ok"
							 core:LocalizationManager.PropertyName="Content">
						<ToolTipService.ToolTip>
							<TextBlock core:LocalizationManager.Key="Scheduler_SaveAndClose" core:LocalizationManager.PropertyName="Text" />
						</ToolTipService.ToolTip>
					</telerik:RadButton>
					<telerik:RadButton AutomationProperties.AutomationId="CancelButton"
							 Margin="10 0 0 0" CornerRadius="2" MinWidth="80"
							 Command="{core:Static Member=telerik:WindowCommands.Cancel}"
							 telerik:RadWindow.ResponseButton="Cancel"
							 core:LocalizationManager.Key="Scheduler_Cancel"
							 core:LocalizationManager.PropertyName="Content" />
				</StackPanel>
			</Grid>
		</ControlTemplate>

		<Style x:Key="EditAppointmentDialogStyle" TargetType="telerik:SchedulerDialog" BasedOn="{StaticResource EditAppointmentDialogStyle}">
			<Setter Property="Template" Value="{StaticResource EditAppointmentTemplate}" />
		</Style>
	</ResourceDictionary>
</Page.Resources>

<Grid>
	<telerik:RadScheduler x:Name="scheduler" EditAppointmentDialogStyle="{StaticResource EditAppointmentDialogStyle}">
		<telerik:RadScheduler.ViewDefinitions>
			<telerik:DayViewDefinition  />
			<telerik:WeekViewDefinition />
			<telerik:TimelineViewDefinition />
		</telerik:RadScheduler.ViewDefinitions>
	</telerik:RadScheduler>
</Grid>
```

#### **Figure 2: Result from Example 3**

![WinUI RadScheduler with custom EditAppointmentDialogStyle](https://www.telerik.com/winui/documentation/assets/5da281abb670a7e37802fba4362a0f86/custom_appointment2.png)

> tip 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 ContentTemplate of the AppointmentItem to reflect which tasks are done and which are not. We will do that by using the **AppointmentItemContentTemplate** property of the **RadScheduler**. **Example 4** demonstrates how to add an ellipse inside the ContentTemplate indicating the status of the task.

#### **Example 4: Indicate the status of an appointment with an Ellipse**

```XAML
<Page.Resources >
	<core:BoolToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
	<DataTemplate x:Key="AppointmentItemContentTemplate">
		<StackPanel Orientation="Horizontal">
			<Ellipse Fill="Green" Width="12" Height="12" VerticalAlignment="Top" Margin="0 5 5 0" Visibility="{Binding Appointment.IsDone, Converter={StaticResource BooleanToVisibilityConverter}}" />
			<TextBlock Text="{Binding DisplayText}" TextWrapping="Wrap" TextTrimming="WordEllipsis" />
		</StackPanel>
	</DataTemplate>
</Page.Resources>
```

## Customizing the Appointment ToolTip

The customization of the Appointment ToolTip is achieved by setting the **ToolTipTemplate** property of **RadScheduler**. The DataContext in this template is of type **AppointmentProxy** so we can use the same approach we used in **Example 4**.

**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**

```XAML
<Page.Resources >
	<core:BoolToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
	<DataTemplate x:Key="AppointmentItemContentTemplate">
		<StackPanel Orientation="Horizontal">
			<Ellipse Fill="Green" Width="12" Height="12" VerticalAlignment="Top" Margin="0 5 5 0" Visibility="{Binding Appointment.IsDone, Converter={StaticResource BooleanToVisibilityConverter}}" />
			<TextBlock Text="{Binding DisplayText}" TextWrapping="Wrap" TextTrimming="WordEllipsis" />
		</StackPanel>
	</DataTemplate>

	<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>
</Page.Resources>
<telerik:RadScheduler x:Name="scheduler" AppointmentItemContentTemplate="{StaticResource AppointmentItemContentTemplate}" ToolTipTemplate="{StaticResource ToolTipTemplate}">
	<telerik:RadScheduler.ViewDefinitions>
		<telerik:DayViewDefinition />
		<telerik:WeekViewDefinition />
		<telerik:MonthViewDefinition />
		<telerik:TimelineViewDefinition />
	</telerik:RadScheduler.ViewDefinitions>
</telerik:RadScheduler>
```

#### **Figure 3: Result from Examples 4 and 5**

![WinUI RadScheduler with Custom AppointmentItemContentTemplate and ToolTipTemplate](https://www.telerik.com/winui/documentation/assets/28794606381bb64713f8778cc9686e34/custom_appointment7.png)

By completing this last modification, we have reached the end of the process needed to create a custom appointment in RadScheduler control.
