When RadScheduler is grouped by resources, the programmatically added Holidays are not displayed into the view:
Sub New()
InitializeComponent()
Dim colors() As Color = {Color.LightBlue, Color.LightGreen, Color.LightYellow, Color.Red, Color.Orange, Color.Pink, Color.Purple, Color.Peru, Color.PowderBlue}
Dim names() As String = {"Alan Smith", "Anne Dodsworth", "Boyan Mastoni", "Richard Duncan", "Maria Shnaider"}
For i As Integer = 0 To names.Length - 1
Dim resource As New Telerik.WinControls.UI.Resource()
resource.Id = New EventId(i)
resource.Name = names(i)
resource.Color = colors(i)
Me.RadScheduler1.Resources.Add(resource)
Next i
Me.RadScheduler1.GroupType = GroupType.Resource
Me.RadScheduler1.ActiveView.ResourcesPerView = 2
Dim holiday As Holiday = New Holiday()
holiday.Date = New DateTime(2023, 3, 8)
holiday.HolidayName = "International Women's Day"
holiday.Location = "Bulgaria"
Dim generateAppointment As Boolean = True
Me.RadScheduler1.Holidays.AddHoliday(holiday, generateAppointment)
End Sub
How it looks with resources:
How it looks without resources:
Currently,
I’m using the Radscheduler MultiDayView to create a “two week” view in which
the end user has the possibility to choose if he wants to see weekend days or
not.
When this
view is used in combination with appointments spanning multiple days and the display
of the weekend days, every thing works fine (with_weekend.png). The appointment
starts at 06/21/19 05:00 and ends 06/24/19 21:05.
However, if
weekend days are not shown in this view (without_weekend.png), part of the same
appointment is shown on 06/25/19 and the end time on 06/24/19 is also incorrect.
How can I solve this? It is not an option to show it as an “All day appointment” since the customer wants to know the exact start and end time.
To reproduce: Me.RadScheduler1.ActiveViewType = Telerik.WinControls.UI.SchedulerViewType.Timeline Dim timelineView As SchedulerTimelineView = Me.RadScheduler1.GetTimelineView() Dim currentScaling As SchedulerTimescale = timelineView.GetScaling() currentScaling.DisplayedCellsCount = 100 Try to scroll horizontally. Then, change the currentScaling.DisplayedCellsCount property to 50 and try to scroll again. You will notice a considerable difference. Workaround: reduce the number of the displayed visual cell elements by the DisplayedCellsCount.
To reproduce: this.radScheduler1.ActiveViewType = Telerik.WinControls.UI.SchedulerViewType.Month; SchedulerMonthView monthView = this.radScheduler1.GetMonthView(); DateTime start= new DateTime(2018, 8, 1); DateTime end= new DateTime(2018,8,31); monthView.ShowFullMonth = true; monthView.StartDate=start; monthView.RangeStartDate = start; monthView.RangeEndDate = end; You will notice that you can navigate outside the specified range. Workaround: manipulate the start date of the view: this.radScheduler1.ActiveView.PropertyChanged += ActiveView_PropertyChanged; DateTime start = new DateTime(2018, 8, 1); DateTime end = new DateTime(2018,8,31); private void ActiveView_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "StartDate" && (this.radScheduler1.ActiveView.StartDate < start || start.AddDays(30) < this.radScheduler1.ActiveView.StartDate) || e.PropertyName == "WeekCount" && this.radScheduler1.ActiveView.EndDate > end) { this.radScheduler1.ActiveView.StartDate = start; } }
Run the application on a Windows 10 machine and scroll to the bottom. Please refer to the attached gif file.
This is reproducible not only with Material theme but with the other themes as well. Workaround: private void radScheduler1_CellFormatting(object sender, Telerik.WinControls.UI.SchedulerCellEventArgs e) { e.CellElement.ZIndex = 0; }
If you want to show the borders for the weekdays, the left most border is not shown because another element is over the cell. It is not possible to hide this top left cell and show the left border for Sunday.
Please refer to the attached sample project and screenshot. Note that if RadScheduler is not grouped by resources, the scrollbar works as expected.
Please refer to the attached sample project. When you toggle the button the Visible property for the first resource is inverted. However, it doesn't take effect in RadScheduler and the resource is always visible. Workaround: Currently, if you want to hide some of the resources, you should remove them from the Resources collection of RadScheduler. When you want to use them again, you should just add them to the same collection.
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
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.
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: 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.
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; }
Please refer to the attached gif files. The separator ";" should be displayed when the appointment's information is displayed in a single line. Otherwise, the information should be displayed multi-line like in the OutlookSeparator.gif. Note: you can test with different AppointmentTitleFormat. Important: setting the Text in the AppointmentFormatting event must have higher priority than AppointmentTitleFormat!!! Workaround: Sub New() InitializeComponent() Me.RadScheduler1.ElementProvider = New MyElementProvider(Me.RadScheduler1) Me.RadScheduler1.Appointments.Add(New Appointment(DateTime.Now, TimeSpan.FromMinutes(40), "Summary", "Description", "Location")) End Sub Public Class CustomAppointmentElement Inherits AppointmentElement Public Sub New(scheduler As RadScheduler, view As SchedulerView, appointment As IEvent) MyBase.New(scheduler, view, appointment) End Sub Protected Overrides Function CreateAppointmentText() As String Return String.Format("{0:HH:mm} - {1:HH:mm} {3} <b>{2}</b>", Me.Appointment.Start, Me.Appointment.End, _ Me.Appointment.Location, Me.Appointment.Summary) End Function End Class Public Class MyElementProvider Inherits SchedulerElementProvider Public Sub New(scheduler As RadScheduler) MyBase.New(scheduler) End Sub Protected Overrides Function CreateElement(Of T As SchedulerVisualElement)(view As SchedulerView, context As Object) As T If GetType(T) = GetType(AppointmentElement) Then Return TryCast(New CustomAppointmentElement(Me.Scheduler, view, DirectCast(context, IEvent)), T) End If Return MyBase.CreateElement(Of T)(view, context) End Function End Class
To reproduce: use the following code Sub New() InitializeComponent() AddHandler Me.RadScheduler1.ActiveViewChanged, AddressOf ActiveViewChanged Me.RadScheduler1.ActiveViewType = Telerik.WinControls.UI.SchedulerViewType.Week End Sub Private Sub ActiveViewChanged(sender As Object, e As Telerik.WinControls.UI.SchedulerViewChangedEventArgs) Dim dayView As SchedulerDayViewBase = TryCast(Me.RadScheduler1.ActiveView, SchedulerDayViewBase) Dim dayViewElement As SchedulerDayViewElement = TryCast(Me.RadScheduler1.SchedulerElement.ViewElement, SchedulerDayViewElement) If dayViewElement IsNot Nothing Then Dim ruler As RulerPrimitive = dayViewElement.DataAreaElement.Ruler ruler.StartScale = 6 ruler.EndScale = 22 dayView.WorkTime = New TimeInterval(TimeSpan.FromHours(13), TimeSpan.FromHours(16)) End If End Sub When you run the project you will notice that the work time starts from 19:00 to 22:00. When you switch between DayView and WeekView, the ruler is not aligned with the scheduler cells as well. The attached gif file illustrates the incorrect behavior. Workaround: use the RulerStartScale and RulerEndScale of the SchedulerDayViewBase Sub New() InitializeComponent() AddHandler Me.RadScheduler1.ActiveViewChanged, AddressOf ActiveViewChanged Me.RadScheduler1.ActiveViewType = Telerik.WinControls.UI.SchedulerViewType.Week End Sub Private Sub ActiveViewChanged(sender As Object, e As Telerik.WinControls.UI.SchedulerViewChangedEventArgs) Dim dayView As SchedulerDayViewBase = TryCast(Me.RadScheduler1.ActiveView, SchedulerDayViewBase) If dayView IsNot Nothing Then dayView.RulerStartScale = 6 dayView.RulerEndScale = 22 dayView.WorkTime = New TimeInterval(TimeSpan.FromHours(13), TimeSpan.FromHours(16)) End If End Sub
To reproduce: public Form1() { InitializeComponent(); this.radScheduler1.ActiveViewType = SchedulerViewType.Week; SchedulerWeekView weekView = this.radScheduler1.GetWeekView(); weekView.RangeFactor = ScaleRange.QuarterHour; Appointment appointment = new Appointment(DateTime.Today.AddHours(23).AddMinutes(45), new TimeSpan(0,15,0), "Meeting"); DailyRecurrenceRule rrule = new DailyRecurrenceRule(appointment.Start, 1, 10); appointment.RecurrenceRule = rrule; this.radScheduler1.Appointments.Add(appointment); } Workaround: In order to deal with the border case with appointment ending at 00:00h, use a new TimeSpan(0,14,59)
Please refer to the attached screenshot. Workaround: public Form1() { InitializeComponent(); this.radScheduler1.ElementProvider = new MyElementProvider(this.radScheduler1); } public class MyElementProvider : SchedulerElementProvider { public MyElementProvider(RadScheduler scheduler) : base(scheduler) { } protected override T CreateElement<T>(SchedulerView view, object context) { if (typeof(T) == typeof(AppointmentElement)) { return new CustomAppointmentElement(this.Scheduler, view, (IEvent)context)as T; } return base.CreateElement<T>(view, context); } } public class CustomAppointmentElement : AppointmentElement { protected override Type ThemeEffectiveType { get { return typeof(AppointmentElement); } } public CustomAppointmentElement(RadScheduler scheduler, SchedulerView view, IEvent appointment) : base(scheduler, view, appointment) { } protected override SizeF ArrangeOverride(SizeF finalSize) { SizeF s = base.ArrangeOverride(finalSize); return new SizeF(s.Width - 5,s.Height); } }
To reproduce: this.radScheduler1.ActiveViewType = SchedulerViewType.Month; for (int i = 0; i < 10; i++) { this.radScheduler1.Appointments.Add(new Appointment(DateTime.Now.AddHours(i),TimeSpan.FromMinutes(30),"A"+i)); } Scroll to the bottom and try to select an appointment. You will notice that selection is not possible. The attached gif file illustrates the incorrect behavior. Workaround: use the overflow button by setting the SchedulerMonthView.EnableCellOverflowButton property to true: SchedulerMonthView monthView = this.radScheduler1.GetMonthView(); monthView.EnableCellOverflowButton = true;