How to Save Custom Backgrounds in RadScheduler when Exporting to ICal
Environment
| Product Version | Product | Author |
|---|---|---|
| 2022.1.222 | RadScheduler for WinForms | Desislava Yordanova |
Description
RadScheduler offers a predefined list of available statuses and backgrounds for its appointments. There are cases when none of the available options is suitable for your specific scenario. You can add your custom status or background. However, the custom added backgrounds or statuses are not automatically saved when exporting to ICal.
SchedulerICalendarExporter is purposed to export these statuses and backgrounds that are available by default. For any custom added statuses/backgrounds, it is necessary to adopt the SchedulerICalendarExporter and cover these cases. This article demonstrates a sample approach how to do it.
Solution
Let's start with providing some information about the default export logic that the SchedulerICalendarExporter class offers:
WriteAdditionalDataForAppointment method
protected virtual void WriteAdditionalDataForAppointment(IEvent appointment, CalObject calObject)
{
if (appointment == null)
{
return;
}
if (appointment.Description != null)
{
calObject.AddProperty(CalendarConstants.Description, CalHelper.CalTextEncode(appointment.Description));
}
object idObject = appointment.UniqueId.KeyValue;
string idString = (idObject != null) ? idObject.ToString() : string.Empty;
calObject.ReplaceProperty(CalendarConstants.UniqueId, idString);
if (appointment.Location != null)
{
calObject.AddProperty(CalendarConstants.Location, appointment.Location);
}
calObject.AddProperty(CalendarConstants.MSStatus, CalHelper.GetAppointmentStatusName((AppointmentStatus)appointment.StatusId));
calObject.AddProperty(CalendarConstants.Background, CalHelper.GetAppointmentBackgroundName((AppointmentBackground)appointment.BackgroundId));
IRemindObject remindObject = appointment as IRemindObject;
if (remindObject != null && remindObject.Reminder.HasValue && !remindObject.Dismissed)
{
CalObject alarmObject = new CalObject(CalendarConstants.Alarm);
alarmObject.AddProperty(CalendarConstants.Action, CalendarConstants.DisplayValue);
alarmObject.AddProperty(CalendarConstants.Description, "Reminder");
alarmObject.AddProperty(CalendarConstants.Trigger, CalHelper.GetReminderString(remindObject.Reminder.Value));
calObject.Children.Add(alarmObject);
}
}
GetAppointmentStatusName method
public static string GetAppointmentStatusName(AppointmentStatus status)
{
string name = "BUSY";
switch (status)
{
case AppointmentStatus.Free:
name = "FREE";
break;
case AppointmentStatus.Tentative:
name = "TENTATIVE";
break;
case AppointmentStatus.Unavailable:
name = "UNAVAILABLE";
break;
}
return name;
}
GetAppointmentBackgroundName method
public static string GetAppointmentBackgroundName(AppointmentBackground background)
{
string name = "BUSY";
switch (background)
{
case AppointmentBackground.Anniversary:
name = "ANNIVERSARY";
break;
case AppointmentBackground.Birthday:
name = "BIRTHDAY";
break;
case AppointmentBackground.Business:
name = "BUSINESS";
break;
case AppointmentBackground.Important:
name = "IMPORTANT";
break;
case AppointmentBackground.MustAttend:
name = "MUSTATTEND";
break;
case AppointmentBackground.NeedsPreparation:
name = "NEEDSPREPARATION";
break;
case AppointmentBackground.Personal:
name = "PERSONAL";
break;
case AppointmentBackground.PhoneCall:
name = "PHONECALL";
break;
case AppointmentBackground.TravelRequired:
name = "TRAVELREQUIRED";
break;
case AppointmentBackground.Vacation:
name = "VACATION";
break;
}
return name;
}
AppointmentStatus
public enum AppointmentStatus
{
/// <summary>
/// Specifies that the status of an appointment is Free
/// </summary>
Free = 1,
/// <summary>
/// Specifies that the status of an appointment is Busy
/// </summary>
Busy,
/// <summary>
/// Specifies that the status of an appointment is Unavailable
/// </summary>
Unavailable,
/// <summary>
/// Specifies that the status of an appointment is Tentative
/// </summary>
Tentative
}
AppointmentBackground
public enum AppointmentBackground
{
/// <summary>
/// Specifies that no background fill is drawn
/// </summary>
None = 1,
/// <summary>
/// Specifies that the important type of background fill should be drawn
/// </summary>
Important,
/// <summary>
/// Specifies that the business type background fill should be drawn
/// </summary>
Business,
/// <summary>
/// Specifies that the personal type of background fill should be drawn
/// </summary>
Personal,
/// <summary>
/// Specifies that vacation type of background fill should be drawn
/// </summary>
Vacation,
/// <summary>
/// Specifies that the MustAttend type of background fill should be drawn
/// </summary>
MustAttend,
/// <summary>
/// Specifies that the TravelRequired type of background fill should be drawn
/// </summary>
TravelRequired,
/// <summary>
/// Specifies that the NeedsPreparation type of background fill should be drawn
/// </summary>
NeedsPreparation,
/// <summary>
/// Specifies that the Birthday type of background fill should be drawn
/// </summary>
Birthday,
/// <summary>
/// Specifies that the Anniversary type of background fill should be drawn
/// </summary>
Anniversary,
/// <summary>
/// Specifies that the PhoneCall type of background fill should be drawn
/// </summary>
PhoneCall
}
In the WriteAdditionalDataForAppointment method both, StatusId and BackgroundId are converted to AppointmentStatus and AppointmentBackground respectively. If you use a custom status/background, the ID wouldn't be available in the respective enum. Hence, the default value will be exported. This behavior is expected.
If you want to export the custom status/background, it would be necessary to create a derivative of the SchedulerICalendarExporter class and override its WriteAdditionalDataForAppointment method. You can refer to the above default logic and adjust this part of the method that adds the status/background to the cal object according to the custom requirement you have.
Custom SchedulerICalendarExporter
public class CustomSchedulerICalendarExporter : SchedulerICalendarExporter
{
private RadScheduler Scheduler;
public CustomSchedulerICalendarExporter(RadScheduler radScheduler)
{
this.Scheduler = radScheduler;
}
protected override void WriteAdditionalDataForAppointment(IEvent appointment, CalObject calObject)
{
if (appointment == null)
{
return;
}
if (appointment.Description != null)
{
calObject.AddProperty("DESCRIPTION", CalTextEncode(appointment.Description));
}
object idObject = appointment.UniqueId.KeyValue;
string idString = (idObject != null) ? idObject.ToString() : string.Empty;
calObject.AddProperty("UID", idString);
if (appointment.Location != null)
{
calObject.AddProperty("LOCATION", appointment.Location);
}
bool exists = Enum.IsDefined(typeof(AppointmentStatus), appointment.StatusId);
if (exists)
{
calObject.AddProperty(@"X-MICROSOFT-CDO-BUSYSTATUS", CalHelper.GetAppointmentStatusName((AppointmentStatus)appointment.StatusId));
}
else
{
calObject.AddProperty(@"X-MICROSOFT-CDO-BUSYSTATUS",GetAppointmentStatusName(appointment.StatusId));
}
exists= Enum.IsDefined(typeof(AppointmentBackground), appointment.BackgroundId);
if (exists)
{
calObject.AddProperty("BACKGROUND", CalHelper.GetAppointmentBackgroundName((AppointmentBackground)appointment.BackgroundId));
}
else
{
calObject.AddProperty("BACKGROUND", GetAppointmentBackgroundName(appointment.BackgroundId));
}
IRemindObject remindObject = appointment as IRemindObject;
if (remindObject != null && remindObject.Reminder.HasValue && !remindObject.Dismissed)
{
CalObject alarmObject = new CalObject("VALARM");
alarmObject.AddProperty("ACTION", "DISPLAY");
alarmObject.AddProperty("DESCRIPTION", "Reminder");
alarmObject.AddProperty("TRIGGER", CalHelper.GetReminderString(remindObject.Reminder.Value));
calObject.Children.Add(alarmObject);
}
}
private string GetAppointmentBackgroundName(int background)
{
foreach (AppointmentBackgroundInfo b in this.Scheduler.Backgrounds)
{
if (b.Id ==background)
{
return b.DisplayName;
}
}
return "default background";
}
private string GetAppointmentStatusName(int status)
{
foreach (AppointmentStatusInfo s in this.Scheduler.Statuses)
{
if (s.Id ==status)
{
return s.DisplayName;
}
}
return "default status";
}
internal string CalTextEncode(string value)
{
string v = NormalizeString(value);
v = v.Replace(@"\", @"\\");
v = v.Replace("\n", @"\n");
v = v.Replace(";", @"\;");
v = v.Replace(",", @"\,");
return v;
}
private static string NormalizeString(string value)
{
string v = value;
v = v.Replace("\r\n", @"\n");
v = v.Replace("\r", @"\n");
v = v.Replace("\n", @"\n");
v = v.Replace(@"\n", "\n");
v = v.Replace(@"\N", "\n");
v = v.Replace(@"\;", ";");
v = v.Replace(@"\,", ",");
v = v.Replace("\\\"", "\"");
v = v.Replace(@"\\", @"\");
return v;
}
}
Using the Custom SchedulerICalendarExporter
string fileName = @"schedule.ics";
using (FileStream stream = File.Create(fileName))
{
this.radScheduler1.Export(stream, new CustomSchedulerICalendarExporter(this.radScheduler1));
}
Importing ICal data
Once the scheduler data is exported, it would be good to have it back by importing at a later moment. This requires creating a derivative of the SchedulerICalendarImporter class and overriding its ApplyAdditionalData method. It is necessary to find the correct ID of the custom status/background and apply it to the appointment:
Custom SchedulerICalendarImporter
public class CustomSchedulerICalendarImporter : SchedulerICalendarImporter
{
private RadScheduler Scheduler;
public CustomSchedulerICalendarImporter(RadScheduler radScheduler)
{
this.Scheduler = radScheduler;
}
protected override void ApplyAdditionalData(IEvent appointment, CalObject calObject)
{
base.ApplyAdditionalData(appointment, calObject);
CalProperty prop = calObject[@"X-MICROSOFT-CDO-BUSYSTATUS"];
if (prop != null)
{
appointment.StatusId = GetAppointmentStatus(CalProperty.ToText(prop));
}
prop = calObject["BACKGROUND"];
if (prop != null)
{
appointment.BackgroundId = GetAppointmentBackground(CalProperty.ToText(prop));
}
}
private int GetAppointmentBackground(string background)
{
foreach (AppointmentBackgroundInfo b in this.Scheduler.Backgrounds)
{
if (b.DisplayName == background)
{
return b.Id;
}
}
return 0;
}
private int GetAppointmentStatus(string statusName)
{
foreach (AppointmentStatusInfo s in this.Scheduler.Statuses)
{
if (s.DisplayName == statusName)
{
return s.Id;
}
}
return 0;
}
}
Using the Custom SchedulerICalendarImporter
string fileName = @"schedule.ics";
using (FileStream stream = File.OpenRead(fileName))
{
this.radScheduler1.Import(stream, new CustomSchedulerICalendarImporter(this.radScheduler1));
}