- Enable reminders
- Adding Reminders
- Creating business object for reminders
- ReminderAlertOpening event
- Handle dismiss property of reminders
- Handle Snooze for reminders
Contact Support
Appointment reminders in .NET MAUI Scheduler (SfScheduler)
The .NET MAUI Scheduler notify an appointment reminder by using the EnableReminder property and ReminderAlertOpening event. An appointment can have one or more reminders.
NOTE
- As a restriction in enabling toast notification in .NET MAUI Framework, as of now, add an event to notify appointment reminders.
Handling reminders for future appointments
-
Future normal and recurring appointment reminder alerts will be displayed as Reminder alert time.
-
Reminder alert time will be calculated from the appointment start time substrate with reminder time interval. For example, if the appointment time is 3.30 pm and the reminder time interval is
new Timespan (0,15,0)
, then the reminder alter time is 3.15 pm.
Handling reminders for overdue appointments
-
If a Non-recurring appointment reminder is in the past, the reminder alert is overdue until dismissed.
-
Recurring appointment overdue from the last occurrence date and time until the last occurrence is dismissed.
-
Recurrence appointment due calculated from current date occurrence and is in due until the next reminder time.
-
If the last occurrence in recurrence in series is dismissed, then the recurring pattern appointment is dismissed.
Handling reminders for Changed occurrence appointments
-
Once the changed occurrence is moved to the future, then the pattern reminder value will be used for the changed occurrence.
-
When the changed occurrence is restored to a time in the past, then its reminder will be dismissed.
-
Past Changed occurrence reminders will be dismissed.
Enable reminders
Reminders can be enabled by setting the EnableReminder property to true
which will trigger the ReminderAlertOpening
event to notify appointment reminders. The reminders can be set by using the Reminders property of SchedulerAppointment.
<syncfusion:SfScheduler x:Name="Scheduler"
View ="Week"
EnableReminder="True" >
</syncfusion:SfScheduler>
Adding Reminders
Configure the appointment reminders with SchedulerReminder. The SchedulerReminder
has the following properties for reminder alert.
Properties | Description |
---|---|
Gets or sets the time interval that decides to notify the reminder before the appointment’s start time. | |
Gets the reminder time that decides when to enable event to the reminder of the appointment. | |
Gets the appointment details for which the reminder is created. | |
Gets the reminder data object associated with the | |
Gets or sets whether the reminder is dismissed. |
<syncfusion:SfScheduler x:Name="Schedule"
AppointmentsSource="{Binding Events}"
EnableReminder="True">
<syncfusion:SfScheduler.BindingContext>
<local:SchedulerViewModel/>
</syncfusion:SfScheduler.BindingContext>
</syncfusion:SfScheduler>
public class ReminderViewModel
{
...
public ObservableCollection<SchedulerAppointment> Events { get; set; } = new ObservableCollection<SchedulerAppointment>();
// Normal Appointment
SchedulerAppointment normalAppointment = new SchedulerAppointment()
{
StartTime = DateTime.Now.AddMinutes(5),
EndTime = DateTime.Now.AddHours(1),
Subject = "Normal Appointment",
Background = Brush.SkyBlue,
Reminders = new ObservableCollection<SchedulerReminder>
{
new SchedulerReminder {TimeBeforeStart = new TimeSpan (0,4,0)},
}
};
Events.Add(normalAppointment);
// All Day Appointment
SchedulerAppointment allDayAppointment = new SchedulerAppointment()
{
StartTime = DateTime.Now.AddDays(1).AddMinutes(1),
EndTime = DateTime.Now.AddDays(1).AddHours(1),
Subject = "All Day Appointment",
Background = Brush.SkyBlue,
IsAllDay = true,
Reminders = new ObservableCollection<SchedulerReminder>
{
new SchedulerReminder {TimeBeforeStart = new TimeSpan (0,0,50)},
}
};
Events.Add(allDayAppointment);
// Recurrence Appointment
SchedulerAppointment recurrenceAppointment = new SchedulerAppointment()
{
Id = 1,
StartTime = DateTime.Now.AddDays(2).AddMinutes(1),
EndTime = DateTime.Now.AddDays(2).AddHours(1),
Subject = "Recurrence Appointment",
Background = Brush.LightGray,
RecurrenceRule = "FREQ=DAILY;COUNT=3",
Reminders = new ObservableCollection<SchedulerReminder>
{
new SchedulerReminder {TimeBeforeStart = new TimeSpan (0,0,40)},
}
};
Events.Add(recurrenceAppointment);
}
Creating business object for reminders
Create a business object class Event
with mandatory fields From,
To,
and EventName.
and also business object class Reminder
with mandatory fields TimeBeforeStart
and IsDismissed
.
/// <summary>
/// Represents custom data properties.
/// </summary>
public class Event
{
public DateTime From { get; set; }
public DateTime To { get; set; }
public bool IsAllDay { get; set; }
public string EventName { get; set; }
public string Notes { get; set; }
public string StartTimeZone { get; set; }
public string EndTimeZone { get; set; }
public Brush Color { get; set; }
public object RecurrenceId { get; set; }
public object Id { get; set; }
public string RecurrenceRule { get; set; }
public ObservableCollection<DateTime> RecurrenceExceptions { get; set; }
public ObservableCollection<Reminder> Reminders { get; set; }
}
The ReminderMapping provides the mapping information about the SchedulerReminder properties to the DataItem object. ReminderMapping
has the following properties.
-
TimeBeforeStart: Maps the property name of a custom class, which is equivalent to the SchedulerReminder.TimeBeforeStart.
-
IsDismissed: Maps the property name of a custom class, which is equivalent to the SchedulerReminder.IsDismissed.
/// <summary>
/// Represents custom reminder properties.
/// </summary>
public class Reminder
{
/// <summary>
/// Gets or sets a value that decides to notify the reminder before the appointment’s start time.
/// </summary>
public TimeSpan TimeBeforeStart { get; set; }
/// <summary>
/// Gets or sets the value indicating whether the reminder is dismissed or not.
/// </summary>
public bool IsDismissed { get; set; }
}
Map those properties of the Event
class with the SfScheduler control by using the SchedulerAppointmentMapping and map CustomReminder
properties with the SchedulerReminder by using SchedulerReminderMapping.
<syncfusion:SfScheduler x:Name="Schedule"
AppointmentsSource="{Binding Events}"
EnableReminder="True">
<syncfusion:SfScheduler.BindingContext>
<local:SchedulerViewModel/>
</syncfusion:SfScheduler.BindingContext>
<syncfusion:SfScheduler.AppointmentMapping>
<syncfusion:SchedulerAppointmentMapping
Subject="EventName"
StartTime="From"
EndTime="To"
Background="Color"
IsAllDay="IsAllDay"
StartTimeZone="StartTimeZone"
EndTimeZone="EndTimeZone"
RecurrenceExceptionDates="RecurrenceExceptionDates"
RecurrenceRule="RecurrenceRule"
RecurrenceId="RecurrenceId"
Reminders="Reminders">
<syncfusion:SchedulerAppointmentMapping.ReminderMapping>
<syncfusion:SchedulerReminderMapping IsDismissed="IsDismissed"
TimeBeforeStart="TimeBeforeStart"/>
</syncfusion:SchedulerAppointmentMapping.ReminderMapping>
</syncfusion:SchedulerAppointmentMapping>
</syncfusion:SfScheduler.AppointmentMapping>
</syncfusion:SfScheduler>
public class ReminderViewModel
{
...
public ObservableCollection<Event> Events { get; set; } = new ObservableCollection<Event>();
// Normal Appointment
Event normalAppointment = new Event()
{
From = DateTime.Now.AddMinutes(5),
To = DateTime.Now.AddHours(1),
EventName = "Normal Appointment",
Color = Brush.SkyBlue,
Reminders = new ObservableCollection<Reminder>
{
new Reminder {TimeBeforeStart = new TimeSpan (0,4,0)},
}
};
Events.Add(normalAppointment);
// All Day Appointment
Event allDayAppointment = new Event()
{
From = DateTime.Now.AddDays(1).AddMinutes(1),
To = DateTime.Now.AddDays(1).AddHours(1),
EventName = "All Day Appointment",
Color = Brush.SkyBlue,
IsAllDay = true,
Reminders = new ObservableCollection<Reminder>
{
new Reminder {TimeBeforeStart = new TimeSpan (0,0,50)},
}
};
Events.Add(allDayAppointment);
// Recurrence Appointment
Event recurrenceAppointment = new Event()
{
Id = 1,
From = DateTime.Now.AddDays(2).AddMinutes(1),
To = DateTime.Now.AddDays(2).AddHours(1),
EventName = "Recurrence Appointment",
Color = Brush.LightGray,
RecurrenceRule = "FREQ=DAILY;COUNT=3",
Reminders = new ObservableCollection<Reminder>
{
new Reminder {TimeBeforeStart = new TimeSpan (0,0,40)},
}
};
Events.Add(recurrenceAppointment);
}
ReminderAlertOpening event
Scheduler notifies the appointment’s reminders by ReminderAlertOpening event before the appointment’s start time.The ReminderAlertOpeningEventArgs has the following property,
- Reminders: Gets a list of reminders that are used to notify the appointment reminders.
scheduler.ReminderAlertOpening += Scheduler_ReminderAlertOpening;
private void Scheduler_ReminderAlertOpening(object sender, ReminderAlertOpeningEventArgs e)
{
bool snooze = await DisplayAlert("Reminder", Reminders[0].Appointment.Subject + " - " + Reminders[0].Appointment.StartTime.ToString(" dddd, MMMM dd, yyyy, hh:mm tt"), "Snooze", "Dismiss");
}
Handle dismiss property of reminders
-
Normal appointment directly dismissed using IsDismissed property
-
For recurrence appointment, if current occurrence need to dismiss then need to add changed occurrence for reminder occurrence dismissed, then current occurrence dismissed details get updated in underlying appointment or data source.
-
If only occurrence dismissed, then the changed icon will not be updated for dismissed changed occurrence
scheduler.ReminderAlertOpening += Scheduler_ReminderAlertOpening;
private void Scheduler_ReminderAlertOpening(object sender, ReminderAlertOpeningEventArgs e)
{
bool snooze = await DisplayAlert("Reminder", Reminders[0].Appointment.Subject + " - " + Reminders[0].Appointment.StartTime.ToString(" dddd, MMMM dd, yyyy, hh:mm tt"), "Snooze", "Dismiss");
if (Dismiss)
{
// For Recurrence appointment, if current occurrence need to dismiss then need to add changed occurrence for reminder occurrence dismissed
if (!string.IsNullOrEmpty(e.Reminders[0].Appointment.RecurrenceRule))
{
ObservableCollection<SchedulerAppointment> appointments = scheduler.AppointmentsSource as ObservableCollection<SchedulerAppointment>;
SchedulerAppointment patternAppointment = appointments.FirstOrDefault(x => x.Id == e.Reminders[0].Appointment.Id);
DateTime changedExceptionDate = e.Reminders[0].Appointment.StartTime;
DateTime endDate = e.Reminders[0].Appointment.EndTime;
patternAppointment.RecurrenceExceptionDates = new ObservableCollection<DateTime>()
{
changedExceptionDate,
};
// Clone parent details
SchedulerAppointment exceptionAppointment = new SchedulerAppointment()
{
Id = 2,
Subject = patternAppointment.Subject,
StartTime = new DateTime(changedExceptionDate.Year, changedExceptionDate.Month, changedExceptionDate.Day, changedExceptionDate.Hour, 0, 0),
EndTime = new DateTime(endDate.Year, endDate.Month, endDate.Day, endDate.Hour, 0, 0),
Background = patternAppointment.Background,
RecurrenceId = 1,
Reminders = patternAppointment.Reminders,
};
if (!appointments.Contains(exceptionAppointment))
{
exceptionAppointment.Reminders[0].IsDismissed = true;
appointments.Add(exceptionAppointment);
}
}
// To dismiss normal reminder
else
{
for (int i = e.Reminders.Count - 1; i >= 0; i--)
{
e.Reminders[i].IsDismissed = true;
}
}
}
}
Handle Snooze for reminders
If Snooze time is set to 5 minutes than the value of reminder TimeBeforeStart property is calculated by
-
For Future appointments,
TimeBeforeStart = Appointment.StartTime - AlertTime - snoozeTime
. -
For Overdue appointments,
TimeBeforeStart = Appointment.StartTime.AddSeconds(DateTime.Now.Second) - DateTime.Now - snoozeTime
. -
For All day appointment,
TimeBeforeStart = Appointment.StartTime.Date.AddSeconds(DateTime.Now.Second) - DateTime.Now - snoozeTime
.
scheduler.ReminderAlertOpening += Scheduler_ReminderAlertOpening;
private void Scheduler_ReminderAlertOpening(object sender, ReminderAlertOpeningEventArgs e)
{
bool snooze = await DisplayAlert("Reminder", Reminders[0].Appointment.Subject + " - " + Reminders[0].Appointment.StartTime.ToString(" dddd, MMMM dd, yyyy, hh:mm tt"), "Snooze", "Dismiss");
if (snooze)
{
TimeSpan snoozeTime = new TimeSpan(0, 5, 0);
// Future appointment reminder
if (e.Reminders[0].Appointment.ActualStartTime > DateTime.Now && !e.Reminders[0].Appointment.IsAllDay)
{
e.Reminders[0].TimeBeforeStart = e.Reminders[0].Appointment.StartTime - e.Reminders[0].AlertTime - snoozeTime;
}
// Allday appointment reminder
else if (e.Reminders[0].Appointment.IsAllDay)
{
e.Reminders[0].TimeBeforeStart = e.Reminders[0].Appointment.StartTime.Date.AddSeconds(DateTime.Now.Second) - DateTime.Now - snoozeTime;
}
// Overdue appointment reminder
else
{
e.Reminders[0].TimeBeforeStart = e.Reminders[0].Appointment.StartTime.AddSeconds(DateTime.Now.Second) - DateTime.Now - snoozeTime;
}
if (!string.IsNullOrEmpty(e.Reminders[0].Appointment.RecurrenceRule))
{
ObservableCollection<SchedulerAppointment> appointments = scheduler.AppointmentsSource as ObservableCollection<SchedulerAppointment>;
SchedulerAppointment patternAppointment = appointments.FirstOrDefault(x => x.Id == e.Reminders[0].Appointment.Id);
DateTime changedExceptionDate = e.Reminders[0].Appointment.StartTime;
DateTime endDate = e.Reminders[0].Appointment.EndTime;
patternAppointment.RecurrenceExceptionDates = new ObservableCollection<DateTime>()
{
changedExceptionDate,
};
// Clone parent details
SchedulerAppointment exceptionAppointment = new SchedulerAppointment()
{
Id = 2,
Subject = patternAppointment.Subject,
StartTime = new DateTime(changedExceptionDate.Year, changedExceptionDate.Month, changedExceptionDate.Day, changedExceptionDate.Hour, 0, 0),
EndTime = new DateTime(endDate.Year, endDate.Month, endDate.Day, endDate.Hour, 0, 0),
Background = patternAppointment.Background,
RecurrenceId = 1,
Reminders = new ObservableCollection<SchedulerReminder> { new SchedulerReminder { TimeBeforeStart = e.Reminders[0].TimeBeforeStart } },
};
if (!appointments.Contains(exceptionAppointment))
{
appointments.Add(exceptionAppointment);
}
}
}
}
NOTE