Workaround: create a custom Appointment and override the PaintRecurrenceIcon method Public Class MyAppointmentElement Inherits AppointmentElement Public Sub New(scheduler As RadScheduler, view As SchedulerView, appointment As IEvent) MyBase.New(scheduler, view, appointment) End Sub Public Overrides Sub PaintRecurrenceIcon(graphics As IGraphics) If Not Me.Recurring Then Return End If Dim icon As Image = DirectCast(GetType(AppointmentElement).GetMethod("GetRecurrenceIcon", BindingFlags.Instance Or BindingFlags.NonPublic).Invoke(Me, Nothing), Image) If icon Is Nothing Then Return End If SyncLock icon Dim clientRect As RectangleF = Me.GetClientRectangle(Me.Bounds.Size) Dim x As Integer = CInt(clientRect.X) + CInt(clientRect.Width) - icon.Width If Me.RightToLeft Then x = CInt(clientRect.X) End If Dim imageRect As Rectangle = New Rectangle(x, CInt(clientRect.Y) + CInt(clientRect.Height) - icon.Height, icon.Width, icon.Height) graphics.DrawImage(imageRect, icon, ContentAlignment.TopLeft, True) End SyncLock End Sub 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 MyAppointmentElement(Me.Scheduler, view, DirectCast(context, IEvent)), T) End If Return MyBase.CreateElement(Of T)(view, context) End Function End Class
How to reproduce: public partial class RadForm1 : Telerik.WinControls.UI.RadForm { public RadForm1() { InitializeComponent(); this.radScheduler1.MouseMove += RadScheduler1_MouseMove; this.radScheduler1.GroupType = GroupType.Resource; Appointment appointment = new Appointment(DateTime.Today.AddHours(13), TimeSpan.FromHours(1), "Test Appointment"); this.radScheduler1.Appointments.Add(appointment); } private void RadScheduler1_MouseMove(object sender, MouseEventArgs e) { Point pt = this.radScheduler1.PointToClient(Cursor.Position); SchedulerCellElement cell = this.radScheduler1.SchedulerElement.ElementTree.GetElementAtPoint(pt) as SchedulerCellElement; if (cell != null) { if (cell.Date != null) { Console.WriteLine(cell.Date.ToShortTimeString()); } } } private void button1_Click(object sender, EventArgs e) { switch (this.radScheduler1.ActiveViewType) { // showing the Day View case SchedulerViewType.Day: var theDayView = this.radScheduler1.GetDayView(); if (theDayView != null) { RulerPrimitive ruler = (this.radScheduler1.SchedulerElement.ViewElement as SchedulerDayViewElement).DataAreaElement.Ruler; ruler.RangeFactor = ScaleRange.QuarterHour; ruler.StartScale = 8; ruler.EndScale = 18; } break; } } } Workaround: instead of accessing directly the ruler, apply the scaling on the view element private void button2_Click(object sender, EventArgs e) { switch (this.radScheduler1.ActiveViewType) { // showing the Day View case SchedulerViewType.Day: var theDayView = this.radScheduler1.GetDayView(); if (theDayView != null) { theDayView.RangeFactor = ScaleRange.QuarterHour; theDayView.RulerStartScale = 8; theDayView.RulerEndScale = 18; } break; } }
How to rerproduce: this.radScheduler1.AccessibleInterval.Start = new DateTime(2018, 7, 18, 00, 00, 00).AddMonths(-2); this.radScheduler1.AccessibleInterval.End = new DateTime(2018, 7, 18, 00, 00, 00).AddMonths(2); this.radScheduler1.ActiveViewType = Telerik.WinControls.UI.SchedulerViewType.Month; Workaround: public class CustomRadScheduler : RadScheduler { private FieldInfo schedulerFi; private FieldInfo activeViewsFi; private MethodInfo onPropertyChangedMi; private MethodInfo setActiveViewMi; protected override void CreateChildItems(RadElement parent) { base.CreateChildItems(parent); this.activeViewsFi = typeof(RadScheduler).GetField("activeViews", BindingFlags.Instance | BindingFlags.NonPublic); this.schedulerFi = typeof(SchedulerView).GetField("scheduler", BindingFlags.Instance | BindingFlags.NonPublic); this.onPropertyChangedMi = typeof(SchedulerView).GetMethod("OnPropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic); this.setActiveViewMi = typeof(RadScheduler).GetMethod("SetActiveView", BindingFlags.Instance | BindingFlags.NonPublic); } public override string ThemeClassName { get { return typeof(RadScheduler).FullName; } } /// <summary> /// Gets or sets the type of the active view. /// </summary> /// <value>The type of the active view.</value> [DefaultValue(SchedulerViewType.Day)] [NotifyParentProperty(true)] public override SchedulerViewType ActiveViewType { get { return base.ActiveViewType; } set { if (this.ActiveViewType != value && value == SchedulerViewType.Month) { SchedulerView newView; Dictionary<SchedulerViewType, SchedulerView> activeViews = this.activeViewsFi.GetValue(this) as Dictionary<SchedulerViewType, SchedulerView>; if (activeViews.ContainsKey(value)) { newView = activeViews[value]; } else { SchedulerView view = new CustomSchedulerMonthView(); this.schedulerFi.SetValue(view, this); this.onPropertyChangedMi.Invoke(view, new object[] { new string[] { "Scheduler" } }); newView = view; } if (this.ActiveView != newView && newView != null) { this.setActiveViewMi.Invoke(this, new object[] { newView, true }); } } else { base.ActiveViewType = value; } } } } public class CustomSchedulerMonthView : SchedulerMonthView { public override SchedulerView OffsetView(int offset) { if (this.ShowFullMonth) { DateTime dtStart = DateHelper.GetStartOfMonth(this.StartDate); if (this.StartDate.Day > 1) { dtStart = dtStart.AddMonths(1); } dtStart = dtStart.AddMonths(offset); return this.CreateViewWithStartDate(dtStart); } else { DateTime startDate = this.StartDate.Add(new TimeSpan(offset * this.OffsetTimeSpan.Ticks)); DateTimeInterval interval = new DateTimeInterval(startDate, this.GetEndDate(startDate)); if (this.Scheduler.AccessibleInterval.Contains(interval)) { return this.CreateViewWithStartDate(startDate); } if (true) { } return this.CreateViewWithStartDate(startDate); } } protected override SchedulerView CreateViewWithStartDate(DateTime startDate) { SchedulerMonthView monthView = new SchedulerMonthView(); this.CopyPropertiesToView(monthView); DateTimeInterval interval = new DateTimeInterval(startDate, this.GetEndDate(startDate)); if (interval.End > this.Scheduler.AccessibleInterval.End) { startDate = startDate.Add(new TimeSpan(-1 * this.OffsetTimeSpan.Ticks)); } if (interval.Start < this.Scheduler.AccessibleInterval.Start) { startDate = startDate.Add(new TimeSpan(1 * this.OffsetTimeSpan.Ticks)); } monthView.StartDate = startDate; if (this.ShowFullMonth) { monthView.WeekCount = DateHelper.GetMonthDisplayWeeks(startDate, this.CurrentCulture.DateTimeFormat); } return monthView; } }
Use attached to reproduce. Workaround: protected override void OnShown(EventArgs e) { base.OnShown(e); radScheduler1.SchedulerElement.ViewElement.UpdateCells(); }
If you set the ResourcesPerView property first to a value greater than the available resources in RadScheduler and then add even more resources, incorrect layout is displayed. Please refer to the attached gif file. However, note that if you first add the resources and then manipulate the ResourcesPerView property, everything is OK. Workaround: set the ResourcesPerView property considering the available resources in RadScheduler Private Sub RadSpinEditor2_ValueChanged(sender As Object, e As EventArgs) Handles RadSpinEditor2.ValueChanged Me.RadScheduler1.Resources.Clear() For i As Integer = 1 To Me.RadSpinEditor2.Value Dim resource As New Telerik.WinControls.UI.Resource() resource.Id = New EventId(i) resource.Name = "Resource" & i resource.Visible = True resource.Color = Color.LightBlue Me.RadScheduler1.Resources.Add(resource) Next i Me.RadScheduler1.GroupType = GroupType.Resource Me.RadScheduler1.ActiveView.ResourcesPerView = Math.Min(Me.RadScheduler1.Resources.Count, Me.RadSpinEditor1.Value) End Sub
To reproduce: - Use the search in the scheduler navigator. - Sort the grid by start/end date. - The values are sorted as strings. Workraround: - Use custom sorting.
How to reproduce: public partial class RadForm1 : Telerik.WinControls.UI.RadForm { public RadForm1() { InitializeComponent(); Resource resource1 = new Resource(); resource1.Id = new EventId(1); resource1.Name = "Resource 1"; resource1.Color = Color.Blue; Resource resource2 = new Resource(); resource2.Id = new EventId(2); resource2.Name = "Resource 2"; resource2.Color = Color.Green; Resource resource3 = new Resource(); resource3.Id = new EventId(3); resource3.Name = "Resource 3"; resource3.Color = Color.Red; this.radScheduler1.Resources.Add(resource1); this.radScheduler1.Resources.Add(resource2); this.radScheduler1.Resources.Add(resource3); } private void RadForm1_Load(object sender, EventArgs e) { this.radScheduler1.GroupType = GroupType.Resource; ((SchedulerViewGroupedByResourceElementBase)this.radScheduler1.ViewElement).ResourceStartIndex = 2; } private void radButton1_Click(object sender, EventArgs e) { this.radScheduler1.Resources.RemoveAt(0); } } Workaround: public partial class RadForm1 : Telerik.WinControls.UI.RadForm { public RadForm1() { InitializeComponent(); Resource resource1 = new Resource(); resource1.Id = new EventId(1); resource1.Name = "Resource 1"; resource1.Color = Color.Blue; Resource resource2 = new Resource(); resource2.Id = new EventId(2); resource2.Name = "Resource 2"; resource2.Color = Color.Green; Resource resource3 = new Resource(); resource3.Id = new EventId(3); resource3.Name = "Resource 3"; resource3.Color = Color.Red; this.radScheduler1.Resources.Add(resource1); this.radScheduler1.Resources.Add(resource2); this.radScheduler1.Resources.Add(resource3); } private void RadForm1_Load(object sender, EventArgs e) { this.radScheduler1.GroupType = GroupType.Resource; ((SchedulerViewGroupedByResourceElementBase)this.radScheduler1.ViewElement).ResourceStartIndex = 2; } private void radButton1_Click(object sender, EventArgs e) { this.radScheduler1.GroupType = GroupType.None; this.radScheduler1.Resources.RemoveAt(0); this.radScheduler1.GroupType = GroupType.Resource; } }
To reproduce: run the sample project and follow the illustrates steps from the gif file. Workaround: close the tooltip programmatically: private void radScheduler1_AppointmentMouseDown(object sender, SchedulerAppointmentMouseEventArgs e) { if (tooltip != null) { tooltip.Hide(this.radScheduler1); } } ToolTip tooltip = null; private void radScheduler1_ToolTipTextNeeded(object sender, Telerik.WinControls.ToolTipTextNeededEventArgs e) { AppointmentElement app = sender as AppointmentElement; if (app != null) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10; i++) { sb.AppendLine(app.Appointment.Summary + i + " " + app.Appointment.Summary + i + i + " " + app.Appointment.Summary); } e.ToolTipText = sb.ToString(); tooltip = e.ToolTip; } }
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 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: 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(); }
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(); }
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.