I have had to implement recurrence on the control because WinForms and WebForms are still unable to work with the same database entries.
In doing so, when recurrence is selected, I only allow daily and create physical entries in the booking table for each appointment. The child appointments point back to the parent but the recurrence rule on the parent stays blank.
All of this works fine (mostly). This allows for editing series or occurrence entries. It does however break the moment that an occurrence that does not appear on the same web page as the master entry is edited. The problem occurs on server side when you select Series on an edit of an existing entry:
#Region "Protected properties" Protected ReadOnly Property Owner() As RadScheduler Get Return Appointment.Owner End Get End Property Protected ReadOnly Property Appointment() As Appointment Get Dim container As SchedulerFormContainer = DirectCast(BindingContainer, SchedulerFormContainer) Return container.Appointment End Get End Property#End Regioncontainer.Appointment returns Nothing. If you choose instance when editing, it works fine. If you have the master appointment on the screen at the time of selecting series, it also works fine.
Any idea why that would be the case and how I can work around it? If there is no suitable solution I will be removing the control from our project as it has created huge controversy around the rendering times, browser compatibility and WinForms integration issues.
Thank you for your time, paying for it was great fun.
6 Answers, 1 is accepted
Could you please clarify that you are facing this problem with the RadScheduler for ASP.NET AJAX or for WinForms?
I would like to clarify that by default the RadScheduler for ASP.NET AJAX loads all appointments (except for the web service binding) in the RadScheduler1.Appointments collection. As far as I understand from the provided information you are implementing your own logic for occurrence appointment edit and you are facing difficulties in finding the master appointment for specific occurrence appointments in order to modify the recurrence rule.
An easy and convenient way of finding the master appointment for an occurring appointment is shown in the code snippet below:
//code behind
If e.Appointment.RecurrenceParentID IsNot Nothing Then Dim masterAppointmentID As Object = e.Appointment.RecurrenceParentID Dim masterAppointment As Appointment = RadScheduler1.Appointments.FindByID(masterAppointmentID) masterAppointment.RecurrenceRule = "here goes your new recurrence rule"End IfRegards,
Boyan Dimitrov
Telerik
Hi Boyan,
Asp.net. If I use the code you have provided, it would crash on line 1 because the e.appointment is null. This is the problem I am facing.
Which event are you using the code in? I am using it on the advanced edit form to populate the form
As far as I understand you want to modify the master appointment recurrence rule when you try to edit an occurrence appointment. So I have used the AppointmentUpdate server-side event. If you want to use the FormCreated which is fired when user opens the insert or edit forms you have to check whether e.Appointment.RecurrenceParentID is not null and then implement the logic for finding the master appointment.
Regards,
Boyan Dimitrov
Telerik
Imports SystemImports System.ComponentModelImports System.Web.UIImports System.DrawingImports Telerik.Web.UIPublic Enum BookingRequestAdvancedFormAdvancedFormMode Insert EditEnd EnumPartial Class BookingRequestAdvancedFormAdvancedForm Inherits System.Web.UI.UserControl Const DEFAULT_START_TIME As Integer = 8 Const DEFAULT_DURATION As Integer = 12#Region "Private members" Private Property FormInitialized() As Boolean Get Dim storedValue As Object = ViewState("FormInitialized") If storedValue IsNot Nothing Then Return CBool(storedValue) End If Return False End Get Set(ByVal value As Boolean) ViewState("FormInitialized") = value End Set End Property Private _mode As BookingRequestAdvancedFormAdvancedFormMode = BookingRequestAdvancedFormAdvancedFormMode.Insert#End Region#Region "Protected properties" Protected ReadOnly Property Owner() As RadScheduler Get Return Appointment.Owner End Get End Property Protected ReadOnly Property Appointment() As Appointment Get Dim container As SchedulerFormContainer = DirectCast(BindingContainer, SchedulerFormContainer) Return container.Appointment End Get End Property#End Region#Region "Attributes and resources" <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property Description() As String Get Return DescriptionText.Text End Get Set(ByVal value As String) DescriptionText.Text = value End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property ActivityID() As String Get Return ResActivity.Value.ToString End Get Set(ByVal value As String) If value <> "" Then ResActivity.Value = CInt(value) Else ResActivity.Value = "" End If End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property StatusID() As String Get Return ResStatus.Value.ToString End Get Set(ByVal value As String) If value <> "" Then ResStatus.Value = CChar(value) End If End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property EnvironmentID() As String Get Return ResEnvironment.Value.ToString End Get Set(ByVal value As String) If value <> "" Then ResEnvironment.Value = CInt(value) Else ' ResEnvironment.Value = "" End If End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property ContactNetworkID() As String Get Return txtContactPersonUserID.Text End Get Set(ByVal value As String) txtContactPersonUserID.Text = value.ToString End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property ContactEMail() As String Get Return txtContactPersonEMail.Text End Get Set(ByVal value As String) txtContactPersonEMail.Text = value.ToString End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property ContactName() As String Get Return txtContactPersonName.Text End Get Set(ByVal value As String) txtContactPersonName.Text = value.ToString End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property ContactPhoneNumber() As String Get Return txtContactPersonPhoneNumber.Text End Get Set(ByVal value As String) txtContactPersonPhoneNumber.Text = value.ToString End Set End Property#End Region#Region "Public properties" Public Property Mode() As BookingRequestAdvancedFormAdvancedFormMode Get Return _mode End Get Set(ByVal value As BookingRequestAdvancedFormAdvancedFormMode) _mode = value End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property Subject() As String Get Return SubjectText.Text End Get Set(ByVal value As String) SubjectText.Text = value End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property Start() As DateTime Get Dim result As DateTime = StartDate.SelectedDate.Value.Date If AllDayEvent.Checked Then result = result.Date Else Dim time As TimeSpan = StartTime.SelectedDate.Value.TimeOfDay result = result.Add(time) End If Return Owner.DisplayToUtc(result) End Get Set(ByVal value As DateTime) StartDate.SelectedDate = Owner.UtcToDisplay(value) StartTime.SelectedDate = Owner.UtcToDisplay(value) End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property [End]() As DateTime Get Dim result As DateTime = EndDate.SelectedDate.Value.Date If AllDayEvent.Checked Then result = result.Date.AddDays(1) Else Dim time As TimeSpan = EndTime.SelectedDate.Value.TimeOfDay result = result.Add(time) End If Return Owner.DisplayToUtc(result) End Get Set(ByVal value As DateTime) EndDate.SelectedDate = Owner.UtcToDisplay(value) EndTime.SelectedDate = Owner.UtcToDisplay(value) End Set End Property <Bindable(BindableSupport.Yes, BindingDirection.TwoWay)> _ Public Property RecurrenceRuleText() As String Get If RadCalendarRecurring.SelectedDates.Count > 0 Then Dim RepeatDates() As Date RepeatDates = RadCalendarRecurring.SelectedDates.ToArray Return String.Join("~", RepeatDates.ToList().ConvertAll(Of String)(Function(I) I.ToString).ToArray) End If Return String.Empty End Get Set(ByVal value As String) Dim rrule As RecurrenceRule RecurrenceRule.TryParse(value, rrule) If value IsNot Nothing AndAlso value <> "" Then Dim SelectedDates = value.Split("~"c).ToList().ConvertAll(Of RadDate)(Function(I) New RadDate(CDate(I))) RadCalendarRecurring.SelectedDates.AddRange(SelectedDates.ToArray) OriginalRecurrenceRule.Value = value End If End Set End Property#End Region Private Sub SetupComboBoxes() SubjectText.WebServiceSettings.Path = SystemInterface.GetProjectServicePath() End Sub Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) If Mode = BookingRequestAdvancedFormAdvancedFormMode.Edit Then UpdateButton.CommandName = "Update" Else UpdateButton.CommandName = "Insert" End If SubmitButton.CommandName = UpdateButton.CommandName InitializeStrings() If Not FormInitialized Then UpdateResetExceptionsVisibility() SetupComboBoxes() End If End Sub Private Sub SetAvailableControls() SetReadOnly(True) ' Make the form editable for the contact person If ((Security.GetLoggedOnUser.ToUpper = ContactNetworkID.ToUpper Or ContactNetworkID = "") And _ StatusID = Status.STATUS_PENDING_SUBMIT) _ Then SetReadOnly(False) End If End Sub Protected Overloads Overrides Sub OnPreRender(ByVal e As EventArgs) MyBase.OnPreRender(e) If Not FormInitialized Then If IsAllDayAppointment(Appointment) Then EndDate.SelectedDate = EndDate.SelectedDate.Value.AddDays(-1) End If FormInitialized = True RemoveBlankActivity(DirectCast(ResActivity.FindControl("ResourceValue"), RadComboBox)) RemoveBlankActivity(DirectCast(ResEnvironment.FindControl("ResourceValue"), RadComboBox)) SetDefaultStatusOption(DirectCast(ResStatus.FindControl("ResourceValue"), RadComboBox)) End If SetAvailableControls() ' InitializeRecurrenceEditor() End Sub Protected Sub BasicControlsPanel_DataBinding(ByVal sender As Object, ByVal e As EventArgs) If Mode = BookingRequestAdvancedFormAdvancedFormMode.Insert Then ' Set the default values for new entries AllDayEvent.Checked = False Appointment.Start = Now.Date.AddHours(DEFAULT_START_TIME) Appointment.End = Appointment.Start.AddHours(DEFAULT_DURATION) Start = Appointment.Start [End] = Appointment.End End If AllDayEvent.Checked = IsAllDayAppointment(Appointment) chkRecurring.Checked = RecurrenceRuleText <> String.Empty End Sub Protected Sub DurationValidator_OnServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs) Handles DurationValidator.ServerValidate args.IsValid = ([End] - Start) > TimeSpan.Zero End Sub Protected Sub ResetExceptions_OnClick(ByVal sender As Object, ByVal e As EventArgs) Owner.RemoveRecurrenceExceptions(Appointment) OriginalRecurrenceRule.Value = Appointment.RecurrenceRule ResetExceptions.Text = Owner.Localization.AdvancedDone End Sub#Region "Private methods" Private Sub RemoveBlankActivity(ByVal cboActivity As RadComboBox) Dim BlankOption As RadComboBoxItem BlankOption = cboActivity.Items.FindItemByValue("NULL") If Not BlankOption Is Nothing Then cboActivity.Items.Remove(BlankOption) End If End Sub Private Sub SetDefaultStatusOption(ByVal cboStatus As RadComboBox) If Mode = BookingRequestAdvancedFormAdvancedFormMode.Edit Then cboStatus.SelectedIndex = cboStatus.Items.FindItemByText(Status.GetDescription(StatusID)).Index Else cboStatus.SelectedIndex = cboStatus.Items.FindItemByText(Status.GetDescription(Status.STATUS_PENDING_SUBMIT)).Index End If cboStatus.Enabled = False End Sub Private Sub InitializeStrings() AllDayEvent.Text = Owner.Localization.AdvancedAllDayEvent StartDateValidator.ErrorMessage = Owner.Localization.AdvancedStartDateRequired StartDateValidator.ValidationGroup = Owner.ValidationGroup StartTimeValidator.ErrorMessage = Owner.Localization.AdvancedStartTimeRequired StartTimeValidator.ValidationGroup = Owner.ValidationGroup EndDateValidator.ErrorMessage = Owner.Localization.AdvancedEndDateRequired EndDateValidator.ValidationGroup = Owner.ValidationGroup EndTimeValidator.ErrorMessage = Owner.Localization.AdvancedEndTimeRequired EndTimeValidator.ValidationGroup = Owner.ValidationGroup DurationValidator.ErrorMessage = "(end less than start time)" DurationValidator.ValidationGroup = Owner.ValidationGroup ResetExceptions.Text = Owner.Localization.AdvancedReset SharedCalendar.FastNavigationSettings.OkButtonCaption = Owner.Localization.AdvancedCalendarOK SharedCalendar.FastNavigationSettings.CancelButtonCaption = Owner.Localization.AdvancedCalendarCancel SharedCalendar.FastNavigationSettings.TodayButtonCaption = Owner.Localization.AdvancedCalendarToday End Sub 'Private Sub InitializeRecurrenceEditor() ' AppointmentRecurrenceEditor.SharedCalendar = SharedCalendar ' AppointmentRecurrenceEditor.Culture = Owner.Culture ' AppointmentRecurrenceEditor.StartDate = Appointment.Start ' AppointmentRecurrenceEditor.EndDate = Appointment.End 'End Sub Private Sub UpdateResetExceptionsVisibility() If String.IsNullOrEmpty(Owner.WebServiceSettings.Path) Then ResetExceptions.Visible = False Dim rrule As RecurrenceRule = RecurrenceRule.Empty If RecurrenceRule.TryParse(Appointment.RecurrenceRule, rrule) Then ResetExceptions.Visible = rrule.Exceptions.Count > 0 End If End If End Sub Private Function IsAllDayAppointment(ByVal appointment As Appointment) As Boolean Dim displayStart As DateTime = Owner.UtcToDisplay(appointment.Start) Dim displayEnd As DateTime = Owner.UtcToDisplay(appointment.[End]) Return displayStart.CompareTo(displayStart.[Date]) = 0 AndAlso displayEnd.CompareTo(displayEnd.[Date]) = 0 End Function Private Sub LookupContactValues() Dim UserDetails As ADUser ' Lookup all relevant information for the given userid UserDetails = ADUser.PerformADSearch(txtContactPersonUserID.Text.Trim) ' If details were found, populate the form as best we can If UserDetails.Name.Length <> 0 Then txtContactPersonEMail.Text = UserDetails.Email txtContactPersonName.Text = UserDetails.Name & " " & UserDetails.Surname txtContactPersonPhoneNumber.Text = UserDetails.Telephone End If End Sub Private Sub SetReadOnly(ByVal DisableControls As Boolean) txtContactPersonEMail.ReadOnly = DisableControls txtContactPersonName.ReadOnly = DisableControls txtContactPersonPhoneNumber.ReadOnly = DisableControls txtContactPersonUserID.ReadOnly = DisableControls SubjectText.Enabled = Not DisableControls CheckIDButton.Enabled = Not DisableControls StartDate.Enabled = Not DisableControls EndDate.Enabled = Not DisableControls AllDayEvent.Enabled = Not DisableControls SubmitButton.Enabled = Not DisableControls UpdateButton.Enabled = Not DisableControls DescriptionText.ReadOnly = DisableControls ' AppointmentRecurrenceEditor.Enabled = Not DisableControls DirectCast(ResEnvironment.FindControl("ResourceValue"), RadComboBox).Enabled = Not DisableControls DirectCast(ResActivity.FindControl("ResourceValue"), RadComboBox).Enabled = Not DisableControls End Sub#End Region Protected Sub SubmitButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles SubmitButton.Click Page.Validate() If Page.IsValid Then StatusID = Status.STATUS_PENDING_APPROVAL End If End Sub Protected Sub CheckIDButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles CheckIDButton.Click If txtContactPersonUserID.Text.Trim <> "" Then LookupContactValues() End If End Sub Private Sub UpdateButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles UpdateButton.Click Page.Validate() End SubEnd Classat Chronos.BookingRequestAdvancedFormAdvancedForm.get_Owner() in C:\Source\Chronos\Chronos\Controls\AdvancedForm.ascx.vb:line 43
at Chronos.BookingRequestAdvancedFormAdvancedForm.InitializeStrings() in C:\Source\Chronos\Chronos\Controls\AdvancedForm.ascx.vb:line 345
at Chronos.BookingRequestAdvancedFormAdvancedForm.Page_Load(Object sender, EventArgs e) in C:\Source\Chronos\Chronos\Controls\AdvancedForm.ascx.vb:line 261
at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Control.AddedControl(Control control, Int32 index)
at System.Web.UI.ControlCollection.Add(Control child)
at System.Web.UI.Control.AddParsedSubObject(Object obj)
at System.Web.UI.Control.System.Web.UI.IParserAccessor.AddParsedSubObject(Object obj)
at ASP.forms_bookingoverview_aspx.__BuildControl__control12(Control __ctrl) in C:\Source\Chronos\Chronos\Forms\BookingOverview.aspx:line 80
at System.Web.UI.CompiledBindableTemplateBuilder.InstantiateIn(Control container)
at Telerik.Web.UI.RadScheduler.CreateChildControls(Boolean bindFromDataSource)
at Telerik.Web.UI.RadScheduler.CreateChildControls()
at System.Web.UI.Control.EnsureChildControls()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Control.PreRenderRecursiveInternal()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
Please refer to our code library help resource that shows how you use the advanced form as a complete separate user control. It does provide a template projects for server-side and web service binding in both C# and VB.
Regards,
Boyan Dimitrov
Telerik