Currently the range starts from the start date of the first appointment an ends with the end date of the last appointment within the range. One should be able to show that there are no appointments within the specified range (start from the DateStartRange).
To reproduce: Use the following code snippet:
DateTime dt = DateTime.Now;
this.radScheduler1.Appointments.Add(new Appointment(dt, TimeSpan.FromHours(4),"A1"));
this.radScheduler1.Appointments.Add(new Appointment(dt, TimeSpan.FromHours(4),"A2"));
By default, when two appointments start at the same time, on the left side it is displayed the shorter appointment. Hence, if you start resizing an appointment it may jump to another location leaving the mouse in a wrong and inconsistent position. The attached gif illustartes the behavior.
Workaround:
public RadForm1()
{
InitializeComponent();
this.radScheduler1.ElementProvider = new MyElementProvider(this.radScheduler1);
DateTime dt = DateTime.Now;
this.radScheduler1.Appointments.Add(new Appointment(dt, TimeSpan.FromHours(4),"A1"));
this.radScheduler1.Appointments.Add(new Appointment(dt, TimeSpan.FromHours(4),"A2"));
}
private class AppointmentDateInfo : IComparable
{
public AppointmentElement appointment;
public DateTime date;
public bool isEnd;
private DateTimeComparer comparer;
public AppointmentDateInfo(AppointmentElement appointment, DateTime date, bool isEnd)
{
this.comparer = new DateTimeComparer(appointment.Scheduler);
this.appointment = appointment;
this.date = date;
this.isEnd = isEnd;
}
public int CompareTo(object obj)
{
AppointmentDateInfo other = obj as AppointmentDateInfo;
if (other == null)
{
return 1;
}
int res = date.CompareTo(other.date);
if (res == 0)
{
if (other.appointment != this.appointment)
{
res = other.isEnd.CompareTo(isEnd);
}
else
{
res = this.isEnd.CompareTo(other.isEnd);
}
}
if (appointment.Start.Equals(other.appointment.Start))
{
return res != 0 ? res : appointment.AppointmentSubject.CompareTo(other.appointment.AppointmentSubject);
}
return res != 0 ? res : comparer.Compare(appointment, other.appointment);
}
}
public class MyElementProvider : SchedulerElementProvider
{
public MyElementProvider(RadScheduler scheduler) : base(scheduler)
{
}
protected override T CreateElement<T>(SchedulerView view, object context)
{
if (typeof(T) == typeof(DayViewAppointmentsTable))
{
return new CustomDayViewAppointmentsTable(this.Scheduler, view, (DayViewAppointmentsArea)context)as T;
}
return base.CreateElement<T>(view, context);
}
}
public class CustomDayViewAppointmentsTable : DayViewAppointmentsTable
{
public CustomDayViewAppointmentsTable(RadScheduler scheduler, SchedulerView view, DayViewAppointmentsArea area) : base(scheduler, view, area)
{
}
private int FindLaneSpan(AppointmentElement appointment, Dictionary<AppointmentElement, int> appointmentLanes, int maxLanes)
{
int lane = appointmentLanes[appointment];
int span = maxLanes - lane;
int cellsPerHour = this.GetDayViewBase().GetNumberOfCellsPerHour();
int minutesPerSlot = 60 / cellsPerHour;
DateTime roundedAppStart = this.GetRulerRoundedDateTime(appointment.Start, false);
DateTime roundedAppEnd = this.GetRulerRoundedDateTime(appointment.End, true);
if (appointment.Start == appointment.End)
{
roundedAppEnd = roundedAppEnd.AddMinutes(minutesPerSlot);
}
foreach (KeyValuePair<AppointmentElement, int> item in appointmentLanes)
{
if (appointment == item.Key)
{
continue;
}
DateTime roundedItemStart = this.GetRulerRoundedDateTime(item.Key.Start, false);
DateTime roundedItemEnd = this.GetRulerRoundedDateTime(item.Key.End, true);
if (item.Key.Start == item.Key.End)
{
roundedItemEnd = roundedItemEnd.AddMinutes(minutesPerSlot);
}
if (roundedItemStart < roundedAppEnd && roundedItemEnd > roundedAppStart && item.Value > lane)
{
span = Math.Min(span, item.Value - lane);
}
}
return span;
}
private int FindFreeLane(List<AppointmentElement> overlappingAppointments, Dictionary<AppointmentElement, int> appointmentLanes)
{
List<int> indexes = new List<int>();
for (int i = 0; i < overlappingAppointments.Count; i++)
{
indexes.Add(appointmentLanes[overlappingAppointments[i]]);
}
indexes.Sort();
for (int i = 0; i < indexes.Count; i++)
{
if (i < indexes[i])
{
return i;
}
}
return overlappingAppointments.Count;
}
protected override void ArrangeAppointments(SizeF finalSize)
{
RectangleF containerRect = new RectangleF(PointF.Empty, finalSize);
List<AppointmentElement> appointmentsInLayout = new List<AppointmentElement>();
foreach (AppointmentElement element in this.AppointmentElements)
{
if (element.Visibility != ElementVisibility.Collapsed)
{
appointmentsInLayout.Add(element);
}
}
appointmentsInLayout.Sort(new DateTimeComparer(this.Scheduler));
Dictionary<AppointmentElement, object> arrangedAppointments = new Dictionary<AppointmentElement, object>();
foreach (AppointmentElement appointmentElement in appointmentsInLayout)
{
if (arrangedAppointments.ContainsKey(appointmentElement))
{
continue;
}
appointmentElement.RelatedAppointments.Sort(new DateTimeComparer(this.Scheduler));
List<AppointmentDateInfo> appointmentDates = new List<AppointmentDateInfo>();
List<AppointmentElement> overlappingAppointments = new List<AppointmentElement>();
Dictionary<AppointmentElement, int> appointmentLanes = new Dictionary<AppointmentElement, int>();
foreach (AppointmentElement relatedAppointment in appointmentElement.RelatedAppointments)
{
if (relatedAppointment.Visibility == ElementVisibility.Collapsed)
{
continue;
}
DateTime appStart = relatedAppointment.Start <= relatedAppointment.End ? relatedAppointment.Start : relatedAppointment.End;
DateTime appEnd = relatedAppointment.Start <= relatedAppointment.End ? relatedAppointment.End : relatedAppointment.Start;
if (!this.Scheduler.EnableExactTimeRendering)
{
if (appStart != appEnd)
{
appointmentDates.Add(new AppointmentDateInfo(relatedAppointment, this.GetRulerRoundedDateTime(appStart, false), false));
appointmentDates.Add(new AppointmentDateInfo(relatedAppointment, this.GetRulerRoundedDateTime(appEnd, true), true));
}
else
{
int cellsPerHour = this.GetDayViewBase().GetNumberOfCellsPerHour();
int minutesPerSlot = 60 / cellsPerHour;
appointmentDates.Add(new AppointmentDateInfo(relatedAppointment, this.GetRulerRoundedDateTime(appStart, false), false));
appointmentDates.Add(new AppointmentDateInfo(relatedAppointment, this.GetRulerRoundedDateTime(appEnd, true).AddMinutes(minutesPerSlot),
true));
}
}
else
{
appointmentDates.Add(new AppointmentDateInfo(relatedAppointment, appStart, false));
appointmentDates.Add(new AppointmentDateInfo(relatedAppointment, appEnd, true));
}
}
appointmentDates.Sort();
int maxLanesCount = 0;
for (int i = 0; i < appointmentDates.Count; i++)
{
if (!appointmentDates[i].isEnd)
{
int freeLane = FindFreeLane(overlappingAppointments, appointmentLanes);
appointmentLanes.Add(appointmentDates[i].appointment, freeLane);
overlappingAppointments.Add(appointmentDates[i].appointment);
maxLanesCount = Math.Max(maxLanesCount, freeLane + 1);
}
else
{
overlappingAppointments.Remove(appointmentDates[i].appointment);
}
if (overlappingAppointments.Count == 0)
{
foreach (KeyValuePair<AppointmentElement, int> item in appointmentLanes)
{
AppointmentElement appointment = item.Key;
int lane = item.Value;
DateTime rulerRoundStart = this.GetRulerRoundedDateTime(appointment.Start, false);
DateTime rulerRoundEnd = this.GetRulerRoundedDateTime(appointment.End, true);
if (appointment.Start == appointment.End)
{
int cellsPerHour = this.GetDayViewBase().GetNumberOfCellsPerHour();
int minutesPerSlot = 60 / cellsPerHour;
rulerRoundEnd = rulerRoundEnd.AddMinutes(minutesPerSlot);
}
PointF location = GetItemLocationInTable(appointment.Start, finalSize);
float dayWidth = this.Area.DayViewElement.GetColumnWidth(this.Area.DayViewElement.GetColumnForDate(rulerRoundStart), finalSize.Width);
int laneSpan = FindLaneSpan(appointment, appointmentLanes, maxLanesCount);
SizeF size = new SizeF(dayWidth / maxLanesCount * laneSpan, this.GetItemHeight(appointment.Start, appointment.End));
location.X += dayWidth / maxLanesCount * lane + this.Area.DayViewElement.AppointmentMargin.Left;
location.Y += this.Area.DayViewElement.AppointmentMargin.Top;
size.Width -= this.Area.DayViewElement.AppointmentMargin.Horizontal;
appointment.DesiredBounds = this.OnAppointmentLayout(appointment, new RectangleF(location, size), finalSize);
if (!arrangedAppointments.ContainsKey(appointment))
{
arrangedAppointments.Add(appointment, null);
}
}
appointmentLanes.Clear();
maxLanesCount = 0;
}
}
for (int i = 0; i < appointmentsInLayout.Count; i++)
{
AppointmentElement appointment = appointmentsInLayout[i];
if (appointment.Visibility != ElementVisibility.Visible)
continue;
RectangleF rectangle = appointment.DesiredBounds;
if (this.RightToLeft)
{
rectangle = LayoutUtils.RTLTranslateNonRelative(rectangle, containerRect);
}
appointment.Arrange(rectangle);
}
}
}
}
To reproduce: please refer to the attached sample project and run it. Try to edit the existing appointment by using the inline editor. As a result, the AppointmentAdded event will be fired. You will notice that the Appointments collection contains more appointments than the existing ones. Note that if you use the edit dialog, this event is not fired and the behavior is as expected. Workaround: RadScheduler1.SchedulerElement.EditorManager.EditorViewMode = SchedulerEditorViewMode.EditorDialog
How to reproduce: public partial class Form2 : Form
{
BindingList<AppointmenntObject> appointments;
BindingSource bs;
public Form2()
{
InitializeComponent();
this.appointments = new BindingList<AppointmenntObject>();
this.bs = new BindingSource();
this.bs.DataSource = this.appointments;
for (int i = 0; i <= 3; i++)
{
appointments.Add(new AppointmenntObject(DateTime.Now.AddDays(i), DateTime.Now.AddDays(i).AddHours(2), "Summary " + i, "Description" + i, "Location" + i));
}
AppointmentMappingInfo appointmentMappingInfo = new AppointmentMappingInfo();
appointmentMappingInfo.Start = "Start";
appointmentMappingInfo.End = "End";
appointmentMappingInfo.Description = "Description";
appointmentMappingInfo.Summary = "Summary";
this.schedulerBindingDataSource1.EventProvider.Mapping = appointmentMappingInfo;
schedulerBindingDataSource1.EventProvider.DataSource = this.bs;
this.radScheduler1.DataSource = this.schedulerBindingDataSource1;
}
private void button1_Click(object sender, EventArgs e)
{
var ev = (AppointmenntObject)this.bs.Current;
var end = ev.End;
ev.End = end.AddHours(1);
}
}
public class AppointmenntObject : INotifyPropertyChanged
{
private DateTime start;
private DateTime end;
private string summary;
private string description;
private string location;
public AppointmenntObject(DateTime start, DateTime end, string summary, string description, string location)
{
this.Start = start;
this.End = end;
this.Summary = summary;
this.Description = description;
this.Location = location;
}
public DateTime Start
{
get
{
return this.start;
}
set
{
if (this.start != value)
{
this.start = value;
OnPropertyChanged("Start");
}
}
}
public DateTime End
{
get
{
return this.end;
}
set
{
if (this.end != value)
{
this.end= value;
OnPropertyChanged("end");
}
}
}
public string Description
{
get
{
return this.description;
}
set
{
if (this.description != value)
{
this.description = value;
OnPropertyChanged("Description");
}
}
}
public string Summary
{
get
{
return this.summary;
}
set
{
if (this.summary != value)
{
this.summary= value;
OnPropertyChanged("Summary");
}
}
}
public string Location
{
get
{
return this.location;
}
set
{
if (this.location != value)
{
this.location = value;
OnPropertyChanged("Location");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Workaround: Rebind the SchedulerBindingDataSource object
private void button1_Click(object sender, EventArgs e)
{
var ev = (AppointmenntObject)this.bs.Current;
var end = ev.End;
ev.End = end.AddHours(1);
this.schedulerBindingDataSource1.Rebind();
}
To reproduce:
RadScheduler radScheduler1 = new RadScheduler();
public Form1()
{
InitializeComponent();
radScheduler1.Top += 100;
radScheduler1.Parent = this;
this.radSchedulerNavigator1.AssociatedScheduler = radScheduler1;
this.radScheduler1.ActiveViewType = SchedulerViewType.Timeline;
Color[] colors = new Color[]{Color.LightBlue, Color.LightGreen, Color.LightYellow,
Color.Red, Color.Orange, Color.Pink, Color.Purple, Color.Peru, Color.PowderBlue};
string[] names = new string[]{"Alan Smith", "Anne Dodsworth",
"Boyan Mastoni", "Richard Duncan", "Maria Shnaider"};
for (int i = 0; i < names.Length; i++)
{
Resource resource = new Resource();
resource.Id = new EventId(i);
resource.Name = names[i];
resource.Color = colors[i];
this.radScheduler1.Resources.Add(resource);
}
this.radScheduler1.GroupType = GroupType.Resource;
this.radScheduler1.ActiveView.ResourcesPerView = 2;
}
private void radButton1_Click(object sender, EventArgs e)
{
SchedulerWeeklyCalendarPrintStyle weeklyCalendarStyle = new SchedulerWeeklyCalendarPrintStyle();
weeklyCalendarStyle.AppointmentFont = new System.Drawing.Font("Segoe UI", 12, FontStyle.Regular);
weeklyCalendarStyle.HeadingAreaHeight = 120;
weeklyCalendarStyle.HoursColumnWidth = 30;
this.radScheduler1.PrintStyle = weeklyCalendarStyle;
this.radScheduler1.PrintPreview();
}
Workaround:
Change the view before printing.
To reproduce:
Appointment a = new Appointment(DateTime.Now, TimeSpan.FromHours(20),"Meeting");
a.StatusId = this.radScheduler1.Statuses.Last().Id;
this.radScheduler1.Appointments.Add(a);
this.radScheduler1.ShowAppointmentStatus = false;
The status for AppointmentElements is shown although it shouldn't.
Please refer to the attached sample project. When you start the application the form's text indicates how many times the AppointmentFormatting event is fired for only 30 appointments - more than 400 times. The more appointments you have , the more times the vent is initially fired,e.g. if you have 50 appointmens it is fired If you switch between the different views you will see how the counter increases its value.
Please refer to the attached gif file and sample project. Workaround: remove the database restrictions and validate the data in the edit dialog before submitting the new appointment data.
To reproduce: Appointment a1 = new Appointment(DateTime.Today.AddHours(10).AddMinutes(30), TimeSpan.FromMinutes(30), "A1"); Appointment a2 = new Appointment(DateTime.Today.AddHours(11) , TimeSpan.FromMinutes(30), "A2"); Appointment a3 = new Appointment(DateTime.Today.AddHours(11).AddMinutes(30), TimeSpan.FromMinutes(30), "A3"); this.radScheduler1.Appointments.Add(a1); this.radScheduler1.Appointments.Add(a2); this.radScheduler1.Appointments.Add(a3); this.radScheduler1.ActiveViewType = SchedulerViewType.Timeline; SchedulerTimelineView timelineView = radScheduler1.GetTimelineView(); timelineView.ShowTimescale(Timescales.Minutes); timelineView.GetScaling().DisplayedCellsCount = 10; Please refer to the attached sample project which incorrect behavior when scrolling is illustrated in the attached gif file. Workaround: instead of specifying 30 minutes duration of an appointment, use 29 minutes and 59 seconds.
To reproduce:
this.radScheduler1.ActiveViewType = Telerik.WinControls.UI.SchedulerViewType.Timeline;
this.radScheduler1.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
Please refer to the attached gif file. Select the left border of an appointment and try to resize it to the left. You will notice that you are allowed to resize it to the right and vice versa.
Workaround: this.radScheduler1.SchedulerElement.ResizeBehavior = new MyResizingBehavior(this.radScheduler1.SchedulerElement);
public class MyResizingBehavior : AppointmentResizingBehavior
{
RadScheduler scheduler;
public MyResizingBehavior(SchedulerVisualElement activeOwner)
: base(activeOwner)
{
scheduler = activeOwner.Scheduler;
}
protected override bool UpdateMouseCursor(Point mousePosition, Rectangle nearRect, Rectangle farRect)
{
bool result = base.UpdateMouseCursor(mousePosition, nearRect, farRect);
if (scheduler.RightToLeft == RightToLeft.Yes )
{
FieldInfo leftFI = typeof(AppointmentResizingBehavior).GetField("leftResize", BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo rightFI = typeof(AppointmentResizingBehavior).GetField("rightResize", BindingFlags.NonPublic | BindingFlags.Instance);
if (nearRect.Contains(mousePosition) && scheduler.Cursor == Cursors.SizeWE)
{
leftFI.SetValue(this, false);
rightFI.SetValue(this, true);
}
else if (farRect.Contains(mousePosition) && scheduler.Cursor == Cursors.SizeWE)
{
leftFI.SetValue(this, true);
rightFI.SetValue(this, false);
}
}
return result;
}
}
Color[] colors = new Color[]
{
Color.LightBlue, Color.LightGreen, Color.LightYellow,
Color.Red, Color.Orange, Color.Pink, Color.Purple, Color.Peru, Color.PowderBlue
};
string[] names = new string[]
{
"Alan Smith", "Anne Dodsworth",
"Boyan Mastoni", "Richard Duncan", "Maria Shnaider"
};
for (int i = 0; i < names.Length; i++)
{
Resource resource = new Resource();
resource.Id = new EventId(i);
resource.Name = names[i];
resource.Color = colors[i];
this.radScheduler1.Resources.Add(resource);
}
this.radScheduler1.GroupType = GroupType.Resource;
this.radScheduler1.ActiveView.ResourcesPerView = 4;
this.radScheduler1.ActiveViewType = SchedulerViewType.Timeline;
this.radScheduler1.GetTimelineView().GroupSeparatorWidth = 0;
By default, the GroupSeparatorWidth property is set to 3. If you need to remove the resources separator, it is necessary to set the GroupSeparatorWidth property to 0 which is not possible at the moment with the public API.
Workaround:
FieldInfo fi = typeof(SchedulerView).GetField("groupSeparatorWidth", BindingFlags.Instance | BindingFlags.NonPublic);
fi.SetValue(this.radScheduler1.GetTimelineView(), 0);
To reproduce: please refer to the attached sample project. Edit some appointment by changing its name or time slot. In the AppointmentChanged event the changes are stored to database. However, if you restart the application you will notice that the changes are not saved at all.
Workaround: call the IEditableObject.EndEdit method for the affected record before saving the changes to the database.
private void RadScheduler1_AppointmentChanged(object sender, AppointmentChangedEventArgs e)
{
IEditableObject editableObject = e.Appointment.DataItem as IEditableObject;
if (editableObject!=null)
{
editableObject.EndEdit();
}
SaveChanges();
}
To reproduce: please run the attached sample project and have a look at the gif file. Workaround: in the _HideWeekDays method, don't pass 0 width as a parameter in the SetColumnWidth method. Use 0.01 instead.
Run the attached project and add one appointment. Press the "save" button and restart the application.
Workaround:
private void radButton1_Click(object sender, EventArgs e)
{
Telerik.WinControls.UI.Appointment a = new Telerik.WinControls.UI.Appointment(DateTime.Now, TimeSpan.FromHours(2));
this.radScheduler1.Appointments.Add(a);
this.radScheduler1.Appointments.Remove(a);
dbContext.SaveChanges();
}
Workaround: create a derivative of the EditAppointmentDialog. Open the designer and adjust the form's height and button's position. Then replace the default dialog with the custom one:
public RadForm1()
{
InitializeComponent();
this.radScheduler1.AppointmentEditDialogShowing += radScheduler1_AppointmentEditDialogShowing;
}
CustomEditAppointmentDialog dialog;
private void radScheduler1_AppointmentEditDialogShowing(object sender, Telerik.WinControls.UI.AppointmentEditDialogShowingEventArgs e)
{
if (dialog==null)
{
dialog = new CustomEditAppointmentDialog();
}
e.AppointmentEditDialog = dialog;
}
How to reproduce:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.radScheduler1.AppointmentAdded += radScheduler1_AppointmentAdded;
Appointment appointment = new Appointment(DateTime.Now, TimeSpan.FromMinutes(60), "Summary", "Description");
appointment.StatusId = 2;
appointment.BackgroundId = 6;
this.radScheduler1.Appointments.Add(appointment);
}
private void radScheduler1_AppointmentAdded(object sender, AppointmentAddedEventArgs e)
{
Console.WriteLine("AppointmentAdded");
}
}
Workaround:
public Form1()
{
InitializeComponent();
this.radScheduler1.Appointments.CollectionChanged += Appointments_CollectionChanged;
Appointment appointment = new Appointment(DateTime.Now, TimeSpan.FromMinutes(60), "Summary", "Description");
appointment.StatusId = 2;
appointment.BackgroundId = 6;
this.radScheduler1.Appointments.Add(appointment);
}
private void Appointments_CollectionChanged(object sender, Telerik.WinControls.Data.NotifyCollectionChangedEventArgs e)
{
if (e.Action == Telerik.WinControls.Data.NotifyCollectionChangedAction.Add)
{
Console.WriteLine("AppointmentAdded");
}
}
To reproduce:
Image img1;
Image img2;
public RadForm1()
{
InitializeComponent();
new Telerik.WinControls.RadControlSpy.RadControlSpyForm().Show();
img1 = Image.FromFile(@"..\..\delete.png");
img2 = Image.FromFile(@"..\..\111.png");
Color[] colors = new Color[]{Color.LightBlue, Color.LightGreen, Color.LightYellow,
.Red, Color.Orange, Color.Pink, Color.Purple, Color.Peru, Color.PowderBlue};
string[] names = new string[]{"Alan Smith", "Anne Dodsworth",
n Mastoni", "Richard Duncan", "Maria Shnaider"};
for (int i = 0; i < names.Length; i++)
{
Resource resource = new Resource();
resource.Id = new EventId(i);
resource.Name = names[i];
resource.Color = colors[i];
resource.Image = img2;
this.radScheduler1.Resources.Add(resource);
}
this.radScheduler1.ActiveView.ResourcesPerView = 2;
this.radScheduler1.GroupType = GroupType.Resource;
SchedulerDayViewGroupedByResourceElement dayView = this.radScheduler1.SchedulerElement.ViewElement as SchedulerDayViewGroupedByResourceElement;
dayView.ResourceHeaderHeight = 120;
radScheduler1.AppointmentFormatting += RadScheduler1_AppointmentFormatting;
radScheduler1.Appointments.Add(new Appointment(DateTime.Now.AddHours(-7), DateTime.Now.AddHours(-5), "Summary", "Description") { ResourceId = radScheduler1.Resources[0].Id });
}
private void RadScheduler1_AppointmentFormatting(object sender, SchedulerAppointmentEventArgs e)
{
e.AppointmentElement.Image = img1
}
- Drag the appointment to the top if needed.
- Scroll down.
Workaround:
private void RadScheduler1_AppointmentFormatting(object sender, SchedulerAppointmentEventArgs e)
{
if (e.AppointmentElement.Children.Count ==0)
{
LightVisualElement el = new LightVisualElement();
el.DrawImage = true;
el.Image = img1;
e.AppointmentElement.Children.Add(el);
}
}
To reproduce: - Bind the scheduler using SchedulerBindingDataSource and group by resources. - At runtime change the data source of the SchedulerBindingDataSource. - A System.ArgumentException is thrown.
To reproduce:
1. Add a RadScheduler on a form and set the culture:
this.radScheduler1.Culture = new System.Globalization.CultureInfo("fa-IR");
2. In regional settings set the Format to Persian(Iran) as it is illustrated in the attached screenshot.
3. Run the application and try to add an appointment. Open the Recurrence dialog and you will get the exception.
Please refer to the attached sample project and gif file.
Workaround: this.radScheduler1.ElementProvider = new CustomSchedulerElementProvider(this.radScheduler1);
public class CustomSchedulerElementProvider : SchedulerElementProvider
{
public CustomSchedulerElementProvider(RadScheduler scheduler) : base(scheduler)
{
}
protected override T CreateElement<T>(SchedulerView view, object context)
{
if (typeof(T) == typeof(TimelineGroupingByResourcesElement))
{
return new CustomTimelineGroupingByResourcesElement(this.Scheduler,view)as T;
}
return base.CreateElement<T>(view, context);
}
}
public class CustomTimelineGroupingByResourcesElement : TimelineGroupingByResourcesElement
{
public CustomTimelineGroupingByResourcesElement(RadScheduler scheduler, SchedulerView view) : base(scheduler, view)
{
}
protected override SizeF ArrangeOverride(SizeF finalSize)
{
RectangleF contentRect = new RectangleF(PointF.Empty, finalSize);
int vScrollOffset = this.View.AllowResourcesScrolling ? this.ResourceScrollBarThickness : 0;
if (this.VScrollBar != null && this.VScrollBar.Maximum < this.VScrollBar.LargeChange)
{
vScrollOffset = 0;
}
IList<SchedulerTimelineViewElement> timelineElements = this.GetChildViewElements();
IList<LightVisualElement> viewSeparators = this.GetViewSeparators();
List<RectangleF> resourcesBounds = new List<RectangleF>();
List<RectangleF> separatorsBounds = new List<RectangleF>();
int scrollBarOffset = 0;
if (timelineElements.Count > 0)
{
if (timelineElements[timelineElements.Count - 1].NavigationElement.Visibility == ElementVisibility.Visible)
{
scrollBarOffset = 17;
}
}
int headerHeight = 0;
if (timelineElements.Count > 0 && timelineElements[0].Header.Visibility != ElementVisibility.Collapsed)
{
headerHeight = timelineElements[0].ViewHeaderHeight + timelineElements[0].ColumnHeaderHeight;
}
FieldInfo fi = typeof(TimelineGroupingByResourcesElement).GetField("leftCell", BindingFlags.Instance | BindingFlags.NonPublic);
SchedulerCellElement leftCell = fi.GetValue(this) as SchedulerCellElement;
FieldInfo fi2 = typeof(TimelineGroupingByResourcesElement).GetField("bottomCell", BindingFlags.Instance | BindingFlags.NonPublic);
SchedulerCellElement bottomCell = fi2.GetValue(this) as SchedulerCellElement;
if (leftCell != null)
{
RectangleF arrangeRectangle = new RectangleF(0, 0, ResourceHeaderWidth, headerHeight);
if (this.RightToLeft)
{
arrangeRectangle = LayoutUtils.RTLTranslateNonRelative(arrangeRectangle, contentRect);
}
leftCell.Arrange(arrangeRectangle);
}
if (bottomCell != null)
{
RectangleF arrangeRectangle = new RectangleF(0, finalSize.Height - scrollBarOffset, ResourceHeaderWidth, scrollBarOffset);
if (this.RightToLeft)
{
arrangeRectangle = LayoutUtils.RTLTranslateNonRelative(arrangeRectangle, contentRect);
}
bottomCell.Arrange(arrangeRectangle);
}
float separatorsWidth = this.View.GroupSeparatorWidth * (this.View.ResourcesPerView - 1);
float resourcesHeight = finalSize.Height - separatorsWidth - headerHeight ;
float y = 0;
for (int i = 0; i < timelineElements.Count; i++)
{
SchedulerTimelineViewElement timelineElement = timelineElements[i];
LightVisualElement separator = i < viewSeparators.Count ? viewSeparators[i] : null;
if (timelineElement != null)
{
float calculatedHeight = this.GetResourceSize(i, resourcesHeight);
RectangleF arrangeRect = new RectangleF(this.ResourceHeaderWidth, y,
finalSize.Width - this.ResourceHeaderWidth - vScrollOffset, calculatedHeight);
if (i == 0)
{
arrangeRect.Height += headerHeight;
}
else if (i == timelineElements.Count - 1)
{
arrangeRect.Height = finalSize.Height - arrangeRect.Top - 1;
}
if (this.RightToLeft)
{
arrangeRect = LayoutUtils.RTLTranslateNonRelative(arrangeRect, contentRect);
}
timelineElement.Arrange(arrangeRect);
y += arrangeRect.Height;
if (i == 0)
{
arrangeRect.Height -= headerHeight;
}
else
{
arrangeRect.Y -= headerHeight;
}
resourcesBounds.Add(arrangeRect);
}
if (separator != null)
{
float calculatedHeight = this.View.GroupSeparatorWidth;
RectangleF arrangeRect = new RectangleF(this.ResourceHeaderWidth, y,
finalSize.Width - this.ResourceHeaderWidth - vScrollOffset, calculatedHeight);
if (this.RightToLeft)
{
arrangeRect = LayoutUtils.RTLTranslateNonRelative(arrangeRect, contentRect);
}
separator.Arrange(arrangeRect);
arrangeRect.Y -= headerHeight;
separatorsBounds.Add(arrangeRect);
y += calculatedHeight;
}
}
if (this.ResourcesHeader != null)
{
RectangleF arrangeRectangle = new RectangleF(0, headerHeight,
this.ResourceHeaderWidth, finalSize.Height - scrollBarOffset - headerHeight);
if (this.RightToLeft)
{
arrangeRectangle = LayoutUtils.RTLTranslateNonRelative(arrangeRectangle, contentRect);
}
this.ResourcesHeader.SetResourcesLayoutInfo(resourcesBounds, separatorsBounds);
this.ResourcesHeader.Arrange(arrangeRectangle);
}
if (this.View.AllowResourcesScrolling)
{
RectangleF scrollbarRect = new RectangleF(finalSize.Width - vScrollOffset,
0, vScrollOffset, finalSize.Height - scrollBarOffset);
if (this.RightToLeft)
{
scrollbarRect = LayoutUtils.RTLTranslateNonRelative(scrollbarRect, contentRect);
}
this.VScrollBar.Arrange(scrollbarRect);
}
else
{
this.VScrollBar.Arrange(RectangleF.Empty);
}
return finalSize;
}
}