I'm trying to include more than just the summary in the tooltip. I put the code below in the AppointmentFormatting event but I get a stack overflow unless I only include the first line that sets the tooltip to the summary, basically leaving it unchanged.
The issue is when you drag up or drag down the top or bottom of the appointment i.e extend or decrease the slot. The error I get with that same codebase is Object reference not set to an instance of an object. inner exception null Source: Telerik.WinControls.Scheduler StackTrace: at Telerik.WinControls.UI.DayViewAppointmentsTable.appointment_MouseUp(Object sender, MouseEventArgs e) at Telerik.WinControls.UI.DayViewAppointmentsTable.AppointmentMouseUp(Object sender, MouseEventArgs e) at Telerik.WinControls.UI.RadSchedulerElement.scheduler_AppointmentElementMouseUp(Object sender, MouseEventArgs e) at Telerik.WinControls.UI.RadScheduler.OnAppointmentElementMouseUp(Object sender, MouseEventArgs args)...
EnableExactTimeRendering property should get or set a value indicating whether the appointment start and end time should be rendered exactly.
Description: When the appointment is moved (dragged and dropped) from one resource to another resource in Timeline view only, the AppointmentDropped event is not fired. This results in NullReferenceException in Windows 8. To reproduce: - add RadScheduler in Timeline view and use the following code: List<Appointment> appointmentlist = new List<Appointment>(); public Form1() { InitializeComponent(); this.radScheduler1.Resources.Add(new Resource { Id = new EventId("1"), Name = "Jack", Visible = true }); this.radScheduler1.Resources.Add(new Resource { Id = new EventId("2"), Name = "John", Visible = true }); this.radScheduler1.Resources.Add(new Resource { Id = new EventId("3"), Name = "John", Visible = true }); this.radScheduler1.GroupType = GroupType.Resource; this.radScheduler1.GetDayView().DayCount = 1; Random rand = new Random(); for (int i = 0; i < 20; i++) { Appointment appointment = new Appointment(DateTime.Now, TimeSpan.FromMinutes(30), "Summary", "Description"); appointment.StatusId = rand.Next(1, radScheduler1.Statuses.Count); appointment.BackgroundId = rand.Next(1, radScheduler1.Backgrounds.Count); appointment.ResourceId = radScheduler1.Resources[rand.Next(0, radScheduler1.Resources.Count)].Id; appointmentlist.Add(appointment); } this.radScheduler1.Appointments.BeginUpdate(); this.radScheduler1.Appointments.AddRange(this.appointmentlist.ToArray()); this.radScheduler1.Appointments.EndUpdate(); this.radScheduler1.AppointmentDropped += radScheduler1_AppointmentDropped; } private void radScheduler1_AppointmentDropped(object sender, AppointmentMovedEventArgs e) { MessageBox.Show("Dropped"); } Workaround: use custom DragDropBehavior as follows: this.radScheduler1.SchedulerElement.DragDropBehavior = new CustomDragDrop(this.radScheduler1.SchedulerElement); public class CustomDragDrop : AppointmentDraggingBehavior { public CustomDragDrop(SchedulerVisualElement activeOwner) : base(activeOwner) { } public override void Drop() { DateTime start = this.ActiveFeedback.Scheduler.SystemTimeZone.OffsetTime(this.ActiveFeedback.Appointment.Start, this.ActiveFeedback.View.DefaultTimeZone); DateTime end = this.ActiveFeedback.Scheduler.SystemTimeZone.OffsetTime(this.ActiveFeedback.Appointment.End, this.ActiveFeedback.View.DefaultTimeZone); IEvent ev = this.ActiveFeedback.AssociatedAppointment; bool changeResourceId = (this.Scheduler.GroupType == GroupType.Resource) && (this.ActiveFeedback.Appointment.ResourceId != this.ActiveFeedback.AssociatedAppointment.ResourceId || this.ActiveFeedback.Appointment.ResourceIds.Count != this.ActiveFeedback.AssociatedAppointment.ResourceIds.Count); AppointmentMovingEventArgs cancelArgs = (this.Scheduler.GroupType != GroupType.Resource) ? new AppointmentMovingEventArgs(start, ev) : new AppointmentMovingEventArgs(start, ev, this.ActiveFeedback.Appointment.ResourceId); this.OnAppointmentDropping(cancelArgs); this.ActiveOwner.Scheduler.GetType().GetMethod("OnAppointmentDropping", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(this.ActiveOwner.Scheduler, new object[] { cancelArgs }); if (cancelArgs.Cancel) { this.Stop(false); return; } this.ActiveFeedback.Scheduler.Appointments.BeginUpdate(); if (this.ActiveOwner as DayViewAllDayHeader != null && this.ActiveFeedback.Appointment.Duration < new TimeSpan(1, 0, 0, 0)) { start = this.ActiveFeedback.Appointment.Start.Date; end = DateHelper.GetEndOfDay(this.ActiveFeedback.Appointment.End); start = this.ActiveFeedback.Scheduler.SystemTimeZone.OffsetTime(start, this.ActiveFeedback.View.DefaultTimeZone); end = this.ActiveFeedback.Scheduler.SystemTimeZone.OffsetTime(end, this.ActiveFeedback.View.DefaultTimeZone); } this.ActiveFeedback.AssociatedAppointment.Start = start; this.ActiveFeedback.AssociatedAppointment.End = end; if (changeResourceId) { this.ActiveFeedback.AssociatedAppointment.ResourceId = this.ActiveFeedback.Appointment.ResourceId; } RadScheduler scheduler = this.ActiveOwner.Scheduler; if (scheduler.DataSource != null) { scheduler.DataSource.GetEventProvider().Update(this.ActiveFeedback.AssociatedAppointment, "Start"); scheduler.DataSource.GetEventProvider().Update(this.ActiveFeedback.AssociatedAppointment, "End"); scheduler.DataSource.GetEventProvider().Update(this.ActiveFeedback.AssociatedAppointment, "Duration"); if (changeResourceId) { scheduler.DataSource.GetEventProvider().Update(this.ActiveFeedback.AssociatedAppointment, "ResourceId"); scheduler.DataSource.GetEventProvider().Update(this.ActiveFeedback.AssociatedAppointment, "ResourceIds"); } } while (!this.ActiveFeedback.Scheduler.Appointments.IsUpdated) this.ActiveFeedback.Scheduler.Appointments.EndUpdate(true); AppointmentMovedEventArgs args = (this.Scheduler.GroupType != GroupType.Resource) ? new AppointmentMovedEventArgs(start, ev) : new AppointmentMovedEventArgs(start, ev, this.ActiveFeedback.Appointment.ResourceId); this.HideFeedbacks(); SchedulerUIHelper.SelectAppointment(this.Scheduler, ev, true, false); this.OnAppointmentDropped(args); this.ActiveOwner.Scheduler.GetType().GetMethod("OnAppointmentDropped", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(this.ActiveOwner.Scheduler, new object[] { args }); } }
When the Open Item button in the reminder dialog is clicked, the recurring appointment is opened without asking whether you want to open the occurange or the series
To reproduce: 1. Have a scheduler grouped by resource 2. On a button click call its PrintPreview method 3. Open the print settings dialog 4. Select Weekly Style 5. Select Grouped by: Resource 6. Select Two pages per week at this point, exception is thrown Workaround: protected override void OnLoad(EventArgs e) { base.OnLoad(e); AddScheduler(); radScheduler1.PrintSettingsDialogFactory = new MyFactory(); } private void radButton1_Click(object sender, EventArgs e) { radScheduler1.PrintPreview(); } class MySchedulerPrintSettingsDialogFactory : SchedulerPrintSettingsDialog { SchedulerPrintStyleSettings userControl; RadDropDownList groupBy; RadDropDownList twoPagesPerView; RadDropDownList printStyle; public MySchedulerPrintSettingsDialogFactory(RadPrintDocument document) : base(document) { } protected override Control CreateFormatControl() { userControl = base.CreateFormatControl() as SchedulerPrintStyleSettings; RadGroupBox groupBox = userControl.Controls["groupBoxStyleSettings"] as RadGroupBox; groupBy = groupBox.Controls["dropDownGroupBy"] as RadDropDownList; groupBy.SelectedIndexChanged += groupBy_SelectedIndexChanged; twoPagesPerView = groupBox.Controls["dropDownLayout"] as RadDropDownList; twoPagesPerView.SelectedIndexChanging += twoPagesPerView_SelectedIndexChanging; RadGroupBox groupBoxMain = userControl.Controls["groupBoxPrintStyle"] as RadGroupBox; printStyle = groupBoxMain.Controls["dropDownPrintStyle"] as RadDropDownList; printStyle.SelectedValueChanged += printStyle_SelectedValueChanged; return userControl; } void printStyle_SelectedValueChanged(object sender, EventArgs e) { if (printStyle.SelectedItem.Text == "Weekly Style") { twoPagesPerView.SelectedItem = twoPagesPerView.Items[0]; } } void twoPagesPerView_SelectedIndexChanging(object sender, Telerik.WinControls.UI.Data.PositionChangingCancelEventArgs e) { if (groupBy.SelectedItem.Text == "Resource" && e.Position == 1) { e.Cancel = true; RadMessageBox.Show("Two pages per view is not supported in GroupBy:Resource mode"); } } void groupBy_SelectedIndexChanged(object sender, Telerik.WinControls.UI.Data.PositionChangedEventArgs e) { if (groupBy.Items[e.Position].Text == "Resource") { twoPagesPerView.SelectedItem = twoPagesPerView.Items[0]; } } } class MyFactory : IPrintSettingsDialogFactory { public Form CreateDialog(RadPrintDocument document) { return new MySchedulerPrintSettingsDialogFactory(document); } }
To reproduce: - Open an appointment with the open item button from the alarm form. - Change the appointment start time and close it. - The "Due in' column in the grid is not properly updated.
Add support for parsing hourly recurrence rules from a database.
To reproduce scroll to the working hours in the ActiveViewChanged event: void radScheduler1_ActiveViewChanged(object sender, Telerik.WinControls.UI.SchedulerViewChangedEventArgs e) { if (e.OldView.ViewType == SchedulerViewType.Month && (e.NewView.ViewType == SchedulerViewType.Day || e.NewView.ViewType == SchedulerViewType.Week)) { SchedulerDayViewBase dayView = radScheduler1.ActiveView as SchedulerDayViewBase; dayView.AutoScrollToWorkTime = true; } } Workaround: public class MyScheduler : RadScheduler { public override string ThemeClassName { get { return typeof(RadScheduler).FullName; } } protected override void SetValuesToViewElement(SchedulerVisualElement viewElement, ViewData viewData) { if (viewElement is SchedulerDayViewElement) { return; } base.SetValuesToViewElement(viewElement, viewData); } }
To reproduce: 1. Open Demo application >> Scheduler >> Printing example. 2. Change to Weekly style and return back to Daily style. 3. Modify a random appointment's end time to be half an hour, e.g. 10:30. 4. Change the rule scale to 5 minute and press PrintPreview button. You will notice that the rendered appointment in the PrintPreview dialog fills the entire hour interval, instead of half of it. public Form1() { InitializeComponent(); this.radScheduler1.PrintStyle = new CustomSchedulerDailyPrintStyle(); } public class CustomSchedulerDailyPrintStyle :SchedulerDailyPrintStyle { protected override void DrawAppointments(DateTime currentDate, IResource resource, Rectangle appArea, Graphics graphics) { int rowCount = Math.Max(1, (int)Math.Ceiling((TimeEndRange - TimeSpan.FromHours(TimeStartRange.Hours)).TotalHours)); float rowHeight = (float)appArea.Height / rowCount; List<IEvent> appointments = this.GetAppointments(currentDate, false, resource); appointments.Sort(CompareAppointments); bool setColumn = true; Dictionary<IEvent, int> columns = new Dictionary<IEvent, int>(); Dictionary<IEvent, int> maxColumns = new Dictionary<IEvent, int>(); int currentColumn = 0; while (setColumn) { setColumn = false; DateTime currentTime = DateTime.MinValue; foreach (IEvent app in appointments) { if (!columns.ContainsKey(app) && DateFloor(app.Start) >= currentTime) { setColumn = true; columns.Add(app, currentColumn); currentTime = DateCeiling(app.End); } } currentColumn++; } DateTime maxEndDate = DateTime.MinValue; int lastIndex = 0; int maxColumn = 0; for (int i = 0; i <= appointments.Count; i++) { if (i == appointments.Count || DateFloor(appointments[i].Start) >= maxEndDate) { for (int j = lastIndex; j < i; j++) { maxColumns.Add(appointments[j], maxColumn); } maxColumn = 0; lastIndex = i; } if (i == appointments.Count) { break; } maxColumn = Math.Max(maxColumn, columns[appointments[i]]); if (maxEndDate < DateCeiling(appointments[i].End)) { maxEndDate = DateCeiling(appointments[i].End); } } foreach (IEvent app in appointments) { AppointmentPrintElement printedAppointment = new AppointmentPrintElement(app, this.Scheduler); printedAppointment.ShowHours = false; printedAppointment.DrawBorder = printedAppointment.DrawFill = true; float appY = appArea.Y + Math.Max(0, (float)(((DateFloor(printedAppointment.Start) - currentDate.Add(this.TimeStartRange)).TotalHours) * rowHeight)); float appBottom = appArea.Y + Math.Min(appArea.Height, (float)(((DateCeiling(printedAppointment.End) - currentDate.Add(this.TimeStartRange)).TotalHours) * rowHeight)); float appHeight = appBottom - appY - app.End.Minute; float appWidth = (appArea.Width - HoursColumnWidth) / (maxColumns[app] + 1); float appX = appArea.X + HoursColumnWidth + columns[app] * appWidth; RectangleF appRect = new RectangleF(appX, appY, appWidth, appHeight); appRect.Inflate(-2f, -2f); this.DrawAppointment(printedAppointment, graphics, appRect); } } } private void radButton1_Click(object sender, EventArgs e) { this.radScheduler1.PrintPreview(); }
If your current time is 9:30 AM and you have an appointment in 11:00 AM with reminder 6 hours, the alarm popup should be displayed at 5:00 AM. When you run the application and show the scheduler, the reminder pop up appears immediately. This behavior is expected. However, if you snooze the event with 1 hour, it is expected that the reminder for this appointment will not popup the next hour. However, the RadScheduler makes the following calculation: reminder start time - reminder value + snooze time = 11:00 - 6 + 1 = 6:00. Hence, the reminder popup shows immediately as the current time is 9:30h.
ADD. RadScheduler - add the ability to change the culture of the generated print document.
The recurrence rule has a missing letter "Z" at the end of the start/end values.
To reproduce: Open Demo application >> Scheduler >> Printing example. Add two appointments one of which is with duration 1 hour, the other one lasts 2 hours. When you click the PrintPreview button, the shorter appointment is missing. Workaround: public Form1() { InitializeComponent(); this.radScheduler1.PrintStyle = new CustomSchedulerDailyPrintStyle(); } public class CustomSchedulerDailyPrintStyle :SchedulerDailyPrintStyle { protected override void DrawAppointments(DateTime currentDate, IResource resource, Rectangle appArea, Graphics graphics) { int rowCount = Math.Max(1, (int)Math.Ceiling((TimeEndRange - TimeSpan.FromHours(TimeStartRange.Hours)).TotalHours)); float rowHeight = (float)appArea.Height / rowCount; List<IEvent> appointments = this.GetAppointments(currentDate, false, resource); appointments.Sort(CompareAppointments); bool setColumn = true; Dictionary<IEvent, int> columns = new Dictionary<IEvent, int>(); Dictionary<IEvent, int> maxColumns = new Dictionary<IEvent, int>(); int currentColumn = 0; while (setColumn) { setColumn = false; DateTime currentTime = DateTime.MinValue; foreach (IEvent app in appointments) { if (!columns.ContainsKey(app) && DateFloor(app.Start) >= currentTime) { setColumn = true; columns.Add(app, currentColumn); currentTime = DateCeiling(app.End); } } currentColumn++; } DateTime maxEndDate = DateTime.MinValue; int lastIndex = 0; int maxColumn = 0; for (int i = 0; i <= appointments.Count; i++) { if (i == appointments.Count || DateFloor(appointments[i].Start) >= maxEndDate) { for (int j = lastIndex; j < i; j++) { maxColumns.Add(appointments[j], maxColumn); } maxColumn = 0; lastIndex = i; } if (i == appointments.Count) { break; } maxColumn = Math.Max(maxColumn, columns[appointments[i]]); if (maxEndDate < DateCeiling(appointments[i].End)) { maxEndDate = DateCeiling(appointments[i].End); } } foreach (IEvent app in appointments) { AppointmentPrintElement printedAppointment = new AppointmentPrintElement(app, this.Scheduler); printedAppointment.ShowHours = false; printedAppointment.DrawBorder = printedAppointment.DrawFill = true; float appY = appArea.Y + Math.Max(0, (float)(((DateFloor(printedAppointment.Start) - currentDate.Add(this.TimeStartRange)).TotalHours) * rowHeight)); float appBottom = appArea.Y + Math.Min(appArea.Height, (float)(((DateCeiling(printedAppointment.End) - currentDate.Add(this.TimeStartRange)).TotalHours) * rowHeight)); float appHeight = appBottom - appY - app.End.Minute; float appWidth = (appArea.Width - HoursColumnWidth) / (maxColumns[app] + 1); float appX = appArea.X + HoursColumnWidth + columns[app] * appWidth; RectangleF appRect = new RectangleF(appX, appY, appWidth, appHeight); appRect.Inflate(-2f, -2f); this.DrawAppointment(printedAppointment, graphics, appRect); } } }
Add AgendaView to the winforms RadScheduler. (Seeing as the view exists in the ASP version of the RadScheduler).
Workaround: subscribe to the PanGesture event and set the StartDate property of the current view to the desired new date.
To reproduce: 1. Add a RadDock with one DocumentWindow. 2. Place a RadSchedulerNavigator in the DocumentWindow. 3. Add a RadButton and use the following code snippet: private void radButton1_Click(object sender, EventArgs e) { this.radSchedulerNavigator1.SchedulerNavigatorElement.TimeZonesDropDown.SelectedIndexChanging += TimeZonesDropDown_SelectedIndexChanging; string path = @"..\..\layout.xml"; this.radDock1.SaveToXml(path); this.radDock1.LoadFromXml(path); RadScheduler sched = new RadScheduler(); this.documentWindow1.Controls.Add(sched); sched.Dock = DockStyle.Left; this.radSchedulerNavigator1.AssociatedScheduler = this.documentWindow1.Controls[1] as RadScheduler; } After running the application and clicking the button, you will notice that RadScheduler has time zone "Casablanca" but the RadSchedulerNavigator has a different time zone. Workaround: associate the RadSchedulerNavigator before loading the layout.
To reproduce: public Form1() { InitializeComponent(); this.radScheduler1.ActiveViewType = SchedulerViewType.Timeline; Timescales scale = Timescales.Hours; this.radScheduler1.GetTimelineView().ShowTimescale(scale); Appointment app1 = new Appointment(DateTime.Today.AddHours(6), TimeSpan.FromMinutes(25), "A1"); Appointment app2 = new Appointment(DateTime.Today.AddHours(6).AddMinutes(25), TimeSpan.FromMinutes(16), "A2"); Appointment app3 = new Appointment(DateTime.Today.AddHours(6).AddMinutes(41), TimeSpan.FromMinutes(12), "A3"); this.radScheduler1.Appointments.Add(app1); this.radScheduler1.Appointments.Add(app2); this.radScheduler1.Appointments.Add(app3); this.radScheduler1.EnableExactTimeRendering = true; } Workaround: 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(TimelineAppointmentsPresenter)) { return new CustomTimelineAppointmentsPresenter(this.Scheduler, view, (SchedulerTimelineViewElement)context)as T; } return base.CreateElement<T>(view, context); } } public class CustomTimelineAppointmentsPresenter: TimelineAppointmentsPresenter { public CustomTimelineAppointmentsPresenter(RadScheduler scheduler, SchedulerView view, SchedulerTimelineViewElement timelineViewElement) : base(scheduler, view, timelineViewElement) { } protected override void ResolveOverlappingAppointments(SizeF availableSize) { List<AppointmentElement> appointments = new List<AppointmentElement>(); foreach (AppointmentElement element in this.AppointmentElements) { if (element.Visibility != ElementVisibility.Collapsed) { appointments.Add(element); } } appointments.Sort(new DateTimeComparer(this.Scheduler)); List<AppointmentElement> arrangedAppointments = new List<AppointmentElement>(); foreach (AppointmentElement appointment in appointments) { for (int i = 0; i < arrangedAppointments.Count; i++) { AppointmentElement otherAppointment = arrangedAppointments[i]; if (otherAppointment == appointment) { continue; } RectangleF rect = appointment.DesiredBounds; rect.Inflate(new SizeF(-2,-2)); if (otherAppointment.DesiredBounds.IntersectsWith(rect)) { appointment.DesiredBounds.Y = otherAppointment.DesiredBounds.Bottom; i = -1; continue; } } arrangedAppointments.Add(appointment); } } }
To reproduce: use a German TimeZone 1. Create a recurring appointment with a yearly recurrence rule on the last Sunday of October. 2. Export to iCal. As a result, the TimeZone information is not correct. The exported RRULE BYSETPOS=5 defines the 5th Sunday. This is wrong. There may be a year with 5 Sundays in March/October but that is not true for every year. The correct encoding for ics files is BYSETPOS=-1. This indicates the last Sunday of a month. The other error is the DTSTART:16010101T030000 and DTSTART:00010101T020000. The German daylight rule is valid since 1996, not since 1601 or year 1. Note: German timezone defines the switch to daylight saving time as - start on last Sunday in March at 03:00 - end on last Sunday in October at 02:00 This rule is valid since 1996. Alert: Events created in RadScheduler for the last Sunday of a month are correct! The rule here is exported as expected and contains BYSETPOS=-1: