The users will be doing a lot of drag/drop and there could be 10-15 appointments per day, so being able to see everything go makes the most sense. The row height should increace when containing more appointments and reduces its height if there are no appointments for that specific day.
This width is 6px by default and it doesn't offer convenient API for customizing it:
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.
To reproduce: 1. Add a RadScheduler and associate a RadSchedulerNavigator to it. 2. Change the culture to "fa-IR". When running the application you will notice that the time range in the navigator is not displayed correctly considering the culture of the associate RadScheduler. Note: you can set the SchedulerNavigatorElement.DateFormat property to "dd dddd". Thus, the time range should be displayed in a similar way as the header cells in RadScheduler. Workaround: you can controls what to be displayed in the navigator by the SchedulerNavigatorElement.DateLabel.Text property. this.radScheduler1.Culture = new System.Globalization.CultureInfo("fa-IR"); string start = this.radScheduler1.ActiveView.StartDate.ToString(this.radSchedulerNavigator1.SchedulerNavigatorElement.DateFormat, this.radScheduler1.Culture); string end = this.radScheduler1.ActiveView.EndDate.ToString(this.radSchedulerNavigator1.SchedulerNavigatorElement.DateFormat, this.radScheduler1.Culture); this.radSchedulerNavigator1.SchedulerNavigatorElement.DateLabel.Text = start + " - " + end;
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 screenshot from Outlook.
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.
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); } } } }