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 Region
container.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
If
Regards,
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 System
Imports System.ComponentModel
Imports System.Web.UI
Imports System.Drawing
Imports Telerik.Web.UI
Public Enum BookingRequestAdvancedFormAdvancedFormMode
Insert
Edit
End Enum
Partial 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 Sub
End Class
at 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