Category / Section
How to add custom appointment editor in WPF Scheduler (Calendar)
4 mins read
Scheduler allows to add and edit the appointments using custom editor with the help of AppointmentEditorOpening event.
XAML
Custom appointment editor with default AppointmentEditor options.
<Window x:Class="WpfScheduler.Helper.AppointmentEditor" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:syncfusion="http://schemas.syncfusion.com/wpf" mc:Ignorable="d" HorizontalAlignment="Center" VerticalAlignment="Center" ResizeMode="NoResize" Title="New Event" Height="500" Width="500"> <Window.Resources> <syncfusion:ReminderTimeIntervalConverter x:Key="ReminderTimeIntervalConverter"/> </Window.Resources> <Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition/> </Grid.ColumnDefinitions> <StackPanel Grid.Row="0" Margin="5" Grid.ColumnSpan="2" Orientation="Vertical"> <Label x:Name="TitleLabel" Content="Subject" /> <TextBox x:Name="Subject" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="25" /> </StackPanel> <StackPanel Grid.Row="0" Margin="5" Grid.Column="2" Grid.ColumnSpan="2" Orientation="Vertical"> <Label x:Name="LocationLabel" Content="Location" /> <TextBox x:Name="location" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="25" /> </StackPanel> <StackPanel Grid.Row="1" Margin="5" Grid.ColumnSpan="2" Orientation="Vertical"> <Label x:Name="StartLabel" Content="Start Time" /> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="5"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <syncfusion:SfDatePicker x:Name="StartDatePicker" VerticalAlignment="Top" HorizontalAlignment="Left"/> <GridSplitter Grid.Column="1" IsEnabled="False"/> <syncfusion:SfTimePicker x:Name="StartTimePicker" VerticalAlignment="Top" Grid.Column="2" HorizontalAlignment="Right"/> </Grid> </StackPanel> <StackPanel Grid.Row="1" Margin="5" Grid.Column="2" Grid.ColumnSpan="2" Orientation="Vertical"> <Label x:Name="EndLabel" Content="End Time" /> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="5"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <syncfusion:SfDatePicker x:Name="EndDatePicker" VerticalAlignment="Top" HorizontalAlignment="Left"/> <GridSplitter Grid.Column="1" IsEnabled="False"/> <syncfusion:SfTimePicker x:Name="EndTimePicker" VerticalAlignment="Top" Grid.Column="2" HorizontalAlignment="Right"/> </Grid> </StackPanel> <CheckBox x:Name="allDay" Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5" Content="All Day" /> <CheckBox x:Name="timeZone" Grid.Row="2" Margin="5" Grid.Column="1" Checked="OnTimeZoneChecked" HorizontalAlignment="Stretch" VerticalAlignment="Center" Content="Time Zone"/> <StackPanel x:Name="TimeZoneMenuPanel" Grid.Row="3" Grid.ColumnSpan="4" Visibility="Collapsed"> <syncfusion:ComboBoxAdv x:Name="TimeZoneMenu" Margin="8, 0, 0, 0" Width="200" Height="24" HorizontalAlignment="Left" VerticalAlignment="Center"/> </StackPanel> <Grid Grid.Row="4" Grid.ColumnSpan="4"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition/> </Grid.ColumnDefinitions> <Button x:Name="addRememainder" Grid.Row="0" Grid.ColumnSpan="1" Margin="5" Height="30" Click="OnAddRememainderClicked" Content="Add Remainder"/> <ListView x:Name="ReminderList" Grid.Row="1" Grid.ColumnSpan="4" Margin="7, 8, 0, 8" Background="Transparent" BorderThickness="0" BorderBrush="Transparent"> <ListView.ItemTemplate> <DataTemplate> <StackPanel x:Name="ReminderStack" Orientation="Horizontal" Margin="3,0,0,0" Background="Transparent"> <syncfusion:UpDown x:Name="ReminderTimeInterval" Margin="5,0,0,0" Height="24" Width="100" IsScrollingOnCircle="True" MinValue="0" Step="1" NumberDecimalDigits="0" Value="{Binding Converter={StaticResource ReminderTimeIntervalConverter}, Path=ReminderTimeInterval, Mode=OneWay, ConverterParameter=TimeInterval}"/> <syncfusion:ComboBoxAdv x:Name="ReminderTimeIntervalMenu" Margin="8, 0, 0, 0" Width="80" Height="24" HorizontalAlignment="Left" VerticalAlignment="Center" SelectedIndex="{Binding Converter={StaticResource ReminderTimeIntervalConverter}, Path=ReminderTimeInterval, Mode=OneWay, ConverterParameter=TimeIntervalMenu}" /> <Button Height="24" x:Name="RemoveReminder" Width="25" Background="Transparent" Margin="6,0,0,0" Click="OnRemoveReminderClicked" > <Image Height="19" Source="D:\2021Incident\May10KB1\new\SchedulerWPF\SchedulerWPF\Resource\delete.png" Width="19"/> </Button> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> <StackPanel Grid.Row="5" Grid.ColumnSpan="4" Margin="5" Orientation="Vertical"> <Label x:Name="descriptionLabel" Content="Description" /> <TextBox x:Name="description" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="25" /> </StackPanel> <StackPanel Orientation="Horizontal" Margin="5" Grid.Row="7" Grid.ColumnSpan="4" HorizontalAlignment="Right" VerticalAlignment="Bottom"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="5"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="5"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <Button x:Name="Save" Height="30" Grid.Column="0" Content="Save" Click="OnSaveClicked"/> <GridSplitter Grid.Column="1" IsEnabled="False"/> <Button x:Name="Delete" Height="30" Grid.Column="2" Content="Delete" Click="OnDeleteClicked"/> <GridSplitter Grid.Column="3" IsEnabled="False"/> <Button x:Name="Cancel" Height="30" Grid.Column="4" Content="Cancel" Click="OnCancelClicked"/> </Grid> </StackPanel> </Grid> </Window>
C#
Handles custom appointment editor options.
public partial class AppointmentEditor : Window { private SfScheduler scheduler; private ScheduleAppointment appointment; public AppointmentEditor(SfScheduler scheduler, ScheduleAppointment appointment, DateTime dateTime) { InitializeComponent(); GetTimeZone(); this.scheduler = scheduler; this.appointment = appointment; if (appointment != null) { this.Subject.Text = appointment.Subject; this.StartDatePicker.Value = appointment.StartTime.Date; this.EndDatePicker.Value = appointment.EndTime.Date; this.StartTimePicker.Value = appointment.StartTime; this.EndTimePicker.Value = appointment.EndTime; this.location.Text = appointment.Location; this.description.Text = appointment.Notes; this.allDay.IsChecked = appointment.IsAllDay; this.ReminderList.ItemsSource = (IList)appointment.Reminders; this.ReminderList.ItemContainerGenerator.StatusChanged += this.OnListViewItemGeneratorStatusChanged; this.timeZone.IsChecked = (appointment.StartTimeZone != null); if((bool)this.timeZone.IsChecked) { this.TimeZoneMenu.Text = appointment.StartTimeZone.ToString(); } } else { this.StartDatePicker.Value = dateTime.Date; this.EndDatePicker.Value = dateTime.Date; this.StartTimePicker.Value = dateTime; this.EndTimePicker.Value = dateTime.AddHours(1); } } private void GetTimeZone() { this.TimeZoneMenu.ItemsSource = new List<string>() { "Samoa Standard Time", "Dateline Standard Time", "UTC-11", "Hawaiian Standard Time", "Alaskan Standard Time", "Pacific Standard Time", "Pacific Standard Time (Mexico)", "Mountain Standard Time", "Mountain Standard Time (Mexico)", "US Mountain Standard Time", "Canada Central Standard Time", "Central America Standard Time", "Central Standard Time", "Eastern Standard Time", "SA Pacific Standard Time", "US Eastern Standard Time", "Venezuela Standard Time", "Atlantic Standard Time", "Central Brazilian Standard Time", "Pacific SA Standard Time", "Paraguay Standard Time", "SA Western Standard Time", "Newfoundland Standard Time", "Argentina Standard Time", "Bahia Standard Time", "Greenland Standard Time", "E. South America Standard Time", "Montevideo Standard Time", "SA Eastern Standard Time", "UTC-02", "(UTC - 01:00) Azores Standard Time", "(UTC - 01:00) Cape Verde Standard Time", "(UTC) GMT Standard Time", "(UTC) Greenwich Standard Time", "(UTC) Morocco Standard Time", "(UTC) UTC", "Magadan Standard Time", "New Zealand Standard Time", "Russia Time Zone 11", "UTC+12", "Line Islands Standard Time", "Tonga Standard Time", }; } private void OnCancelClicked(object sender, RoutedEventArgs e) { this.Close(); } private void OnSaveClicked(object sender, RoutedEventArgs e) { if (appointment == null) { var scheduleAppointment = new ScheduleAppointment(); scheduleAppointment.Subject = this.Subject.Text; scheduleAppointment.StartTime = this.StartDatePicker.Value.Value.Date.Add(this.StartTimePicker.Value.Value.TimeOfDay); scheduleAppointment.EndTime = this.EndDatePicker.Value.Value.Date.Add(this.EndTimePicker.Value.Value.TimeOfDay); scheduleAppointment.Location = this.location.Text; scheduleAppointment.IsAllDay = (bool)this.allDay.IsChecked; scheduleAppointment.Notes = this.description.Text; scheduleAppointment.Reminders=(ObservableCollection<SchedulerReminder>)this.ReminderList.ItemsSource; if ((bool)this.timeZone.IsChecked) { scheduleAppointment.StartTimeZone = this.TimeZoneMenu.Text; scheduleAppointment.EndTimeZone = this.TimeZoneMenu.Text; } if (this.scheduler.ItemsSource == null) { this.scheduler.ItemsSource = new ScheduleAppointmentCollection(); } (this.scheduler.ItemsSource as ScheduleAppointmentCollection).Add(scheduleAppointment); } else { appointment.Subject = this.Subject.Text; appointment.StartTime = this.StartDatePicker.Value.Value.Date.Add(this.StartTimePicker.Value.Value.TimeOfDay); appointment.EndTime = this.EndDatePicker.Value.Value.Date.Add(this.EndTimePicker.Value.Value.TimeOfDay); appointment.Location = this.location.Text; appointment.IsAllDay = (bool)this.allDay.IsChecked; appointment.Notes = this.description.Text; appointment.Reminders = (ObservableCollection<SchedulerReminder>)this.ReminderList.ItemsSource; appointment.StartTimeZone = this.TimeZoneMenu.Text; appointment.EndTimeZone = this.TimeZoneMenu.Text; } this.Close(); } protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); this.Save.Click -= this.OnSaveClicked; this.Cancel.Click -= this.OnCancelClicked; this.scheduler = null; this.appointment = null; } private void OnAddRememainderClicked(object sender, RoutedEventArgs e) { if (this.ReminderList != null) { var reminders = this.ReminderList.ItemsSource as IList; this.ReminderList.ItemContainerGenerator.StatusChanged += this.OnListViewItemGeneratorStatusChanged; if (reminders == null) { reminders = new ObservableCollection<SchedulerReminder>(); } else if (reminders.Count == 5) { // Only maximum of 5 reminders allowed in editor window. return; } var newRemainder = new SchedulerReminder(); reminders.Add(newRemainder); this.ReminderList.ItemsSource = reminders; } } private void OnListViewItemGeneratorStatusChanged(object sender, EventArgs e) { foreach (var reminder in this.ReminderList.Items) { var listViewItem = this.ReminderList.ItemContainerGenerator.ContainerFromItem(reminder) as ListViewItem; if (listViewItem == null) { continue; } //// Sets the reminder interval types. var reminderTimeIntervalMenu = VisualUtils.FindDescendant(listViewItem, typeof(ComboBoxAdv)) as ComboBoxAdv; if (reminderTimeIntervalMenu != null) { reminderTimeIntervalMenu.ItemsSource = new List<string>() { SchedulerLocalizationResourceAccessor.Instance.GetString("MinutesIntervalTypeReminder", CultureInfo.CurrentUICulture), SchedulerLocalizationResourceAccessor.Instance.GetString("HoursIntervalTypeReminder", CultureInfo.CurrentUICulture), SchedulerLocalizationResourceAccessor.Instance.GetString("DaysIntervalTypeReminder", CultureInfo.CurrentUICulture), SchedulerLocalizationResourceAccessor.Instance.GetString("WeeksIntervalTypeReminder", CultureInfo.CurrentUICulture), }; } } } private void OnRemoveReminderClicked(object sender, RoutedEventArgs e) { var button = sender as Button; var reminderCollection = this.ReminderList.ItemsSource as IList; reminderCollection.Remove(button.DataContext as SchedulerReminder); } private void OnTimeZoneChecked(object sender, RoutedEventArgs e) { if(this.timeZone.IsChecked==true) this.TimeZoneMenuPanel.Visibility = Visibility.Visible; else this.TimeZoneMenuPanel.Visibility = Visibility.Visible; } private void OnDeleteClicked(object sender, RoutedEventArgs e) { if(appointment != null) (this.scheduler.ItemsSource as ScheduleAppointmentCollection).Remove(appointment); this.Close(); } }
XAML
In AppointmentEditorOpening event, show the custom editor by cancelling the default AppointmentEditor.
private void Scheduler_AppointmentEditorOpening(object sender, AppointmentEditorOpeningEventArgs e) { e.Cancel = true; var editor = new AppointmentEditor(this.scheduler, e.Appointment, e.DateTime); editor.ShowDialog(); }