Example E2924
Visible to All Users

Scheduler for ASP.NET Web Forms - How to customize the appointment edit form for working with custom fields

This example provides a step-by-step tutorial for simple appointment form customization. You'll learn how to replace a text box used for editing an appointment's subject with a combo box and add an additional text box for editing an appointment's custom field.

To follow this tutorial, create a project in which the ASPxScheduler control is bound to a collection of custom objects: How to: Bind ASPxScheduler Appointment to ObjectDataSource.

Follow the steps below to customize the appointment edit form.

Step 1 - Update Data Source

Add a new field named "CustomInfo" to your data source. The CustomInfo field will contain text, thus, depending on your database and data provider, it could be of the string:NVARCHAR(MAX) type or one similar to it. Modify your select/insert/update queries to include those fields.

Step 2 – Specify Custom Field Mappings

Add custom field mapping for the previously created CustomInfo field. You can do this in the Visual Studio Designer, in the markup or using the property grid (ASPxScheduler control ->Storage->Appointments->CustomFieldMappings).

ASPx
<CustomFieldMappings> <dx:ASPxAppointmentCustomFieldMapping Member="CustomInfo" Name="ApptCustomInfo" ValueType="String" /> </CustomFieldMappings>

Depending on your requirements, you may need to add mappings at runtime instead. In this case, use the following code in the code-behind file for the page containing the ASPxScheduler control (Default.aspx.cs or Default.aspx.vb if you use Visual Basic).

C#
ASPxScheduler1.Storage.Appointments.CustomFieldMappings.Add(new AppointmentCustomFieldMapping("ApptCustomInfo", "CustomInfo"));

Step 3 – Prepare a Custom Appointment Editing Form

Create a new folder named "CustomForms". Copy default ASPxScheduler forms and templates to your project site using the ASPxScheduler control's Smart Tag, as described in the Dialog Forms article. Locate the AppointmentForm.ascx form and the corresponding code-behind file AppointmentForm.ascx.cs (or AppointmentForm.ascx.vb if you use Visual Basic) and copy them to the newly created CustomForms folder.

Step 4 – Register the Custom Form

After copying default ASPxScheduler forms and templates into your project, the corresponding paths will be automatically registered using the OptionsForms and OptionsToolTips properties. Change the ASPxSchedulerOptionsForms.AppointmentFormTemplateUrl property value to the value corresponding to the new location of the form's template - ~/CustomForms/AppointmentForm.ascx. You can clear the other paths or keep them for further customization of default templates.

ASPx
<OptionsForms AppointmentFormTemplateUrl="CustomForms/AppointmentForm.ascx" />

Step 5 - Add Custom Editors

In the form's markup, replace the predefined text box used for editing an appointment's subject with a combo box and add an additional text box for editing the CustomInfo field.

ASPx
<td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblSubject" runat="server"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxComboBox ID="cbSubject" runat="server" ValueType="System.String"></dx:ASPxComboBox> </td> </tr> </table> </td> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblCustomInfo" runat="server" Text="Custom info:"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxTextBox ID="tbCustomInfo" runat="server" Width="100%" /> </td> </tr> </table> </td>

[!IMPORTANT]
Note that the AssociatedControlID property of the lblSubject label points to the tbSubject text box, which has been removed from the form. This may cause unwanted re-creation of the form's controls hierarchy. As a result, the form editors will return empty values when you save an appointment. To prevent incorrect behavior, remove this property or change its value to the cbSubject combo box ID.

After you add custom editors to the form, you have to register them. Add IDs of the newly added editors to the array returned by the form's GetChildEditors method. This step ensures that the editors will be accessible by client-side scripts. After that, the method will look as follows.

C#
protected override ASPxEditBase[] GetChildEditors() { ASPxEditBase[] edits = new ASPxEditBase[] { lblSubject, cbSubject, tbCustomInfo, lblLocation, tbLocation, lblLabel, edtLabel, lblStartDate, edtStartDate, lblEndDate, edtEndDate, lblStatus, edtStatus, lblAllDay, chkAllDay, lblResource, edtResource, tbDescription, cbReminder, lblReminder, ddResource, chkReminder, GetMultiResourceEditor(), edtStartTime, edtEndTime, cbTimeZone }; return edits; }

Step 6 – Bind the Custom Editors to Data

In the DataBind method of the Edit Appointment form, you can provide values for the new editors based on the properties of the currently edited appointment. Since you use a combo box for editing an appointment's subject, you need to assign a corresponding data source to this combo box as shown below.

C#
private void BindSubjectCombobox() { List<string> subjectList = new List<string>(); subjectList.Add("Meeting"); subjectList.Add("Business travel"); subjectList.Add("Phone call"); cbSubject.DataSource = subjectList; cbSubject.DataBind(); } public override void DataBind() { base.DataBind(); // ... BindSubjectCombobox(); cbSubject.Value = container.Subject; tbCustomInfo.Text = container.Appointment.CustomFields["ApptCustomInfo"] != null ? container.Appointment.CustomFields["ApptCustomInfo"].ToString() : ""; // ... }

Step 7 – Save Editor Values

To save values of the new editors as corresponding appointment properties, override the default AppointmentFormSaveCallbackCommand. Create this command's descendant in the CustomForms folder or in the App_Code folder (for websites).

C#
public class CustomAppointmentSaveCallbackCommand : AppointmentFormSaveCallbackCommand { public CustomAppointmentSaveCallbackCommand(ASPxScheduler control) : base(control) { } }

In the overridden AssignControllerValues method, assign the values of the new editors to the corresponding properties of the AppointmentFormController. Note that your custom code should be placed after you execute the base implementation of the AssignControllerValues method. Otherwise, changes made to standard appointment fields (for example, the Controller.Subject field) will be reset with the base implementation.

C#
protected override void AssignControllerValues() { base.AssignControllerValues(); ASPxComboBox cbSubject = FindControlByID("cbSubject") as ASPxComboBox; ASPxTextBox tbCustomInfo = FindControlByID("tbCustomInfo") as ASPxTextBox; Controller.Subject = cbSubject.Text; Controller.CustomInfoField = tbCustomInfo.Text; }

Make a note that the default FindControlByID method searches the editor located in the AppointmentFormTemplateContainer's controls collection without taking into account the complex hierarchy of the controls with multiple nesting levels. It is recommended that you also override this method as shown below.

C#
protected override System.Web.UI.Control FindControlByID(string id) { return FindTemplateControl(TemplateContainer, id); } System.Web.UI.Control FindTemplateControl(System.Web.UI.Control RootControl, string id) { System.Web.UI.Control foundedControl = RootControl.FindControl(id); if(foundedControl == null) { foreach(System.Web.UI.Control item in RootControl.Controls) { foundedControl = FindTemplateControl(item, id); if(foundedControl != null) break; } } return foundedControl; }

Step 8 – Create a Custom Appointment Form Controller

In the previous step, the tbCustomInfo editor value is saved as an appointment's custom field using the Controller.CustomInfoField property. The default AppointmentFormController class does not contain such a property so you need to manually implement this property in a custom AppointmentFormController class descendant.

The code sample below demonstrates the implementation of a custom appointment form controller.

C#
public class CustomAppointmentFormController : AppointmentFormController { public CustomAppointmentFormController(ASPxScheduler control, Appointment apt) : base(control, apt) { } // Provides access to a user-specified value of the custom field. public string CustomInfoField { get { return (string)EditedAppointmentCopy.CustomFields["ApptCustomInfo"]; } set { EditedAppointmentCopy.CustomFields["ApptCustomInfo"] = value; } } // Provides access to an initial value of the custom field. string SourceCustomInfoField { get { return (string)SourceAppointment.CustomFields["ApptCustomInfo"]; } set { SourceAppointment.CustomFields["ApptCustomInfo"] = value; } } // Checks whether or not an appointment has been modified taking a custom field value into account. public override bool IsAppointmentChanged() { bool isChanged = base.IsAppointmentChanged(); return isChanged || SourceCustomInfoField != CustomInfoField; } } public class CustomAppointmentSaveCallbackCommand : AppointmentFormSaveCallbackCommand { public CustomAppointmentSaveCallbackCommand(ASPxScheduler control) : base(control) { } protected internal new CustomAppointmentFormController Controller { get { return (CustomAppointmentFormController)base.Controller; } } protected override AppointmentFormController CreateAppointmentFormController(DevExpress.XtraScheduler.Appointment apt) { return new CustomAppointmentFormController(Control, apt); } }

Step 9 – Execute the Custom Save Command

Your newly created CustomAppointmentSaveCallbackCommand should be executed instead of the default save command whenever a user issues the Save command. To accomplish this, handle the ASPxScheduler.BeforeExecuteCallbackCommand event as illustrated below.

C#
protected void ASPxScheduler1_BeforeExecuteCallbackCommand(object sender, DevExpress.Web.ASPxScheduler.SchedulerCallbackCommandEventArgs e) { if(e.CommandId == SchedulerCallbackCommandId.AppointmentSave) { e.Command = new CustomAppointmentSaveCallbackCommand((ASPxScheduler)sender); } }

Step 10 – Get the Result

Run the project. Check to see whether or not your custom form is shown when you open an appointment for editing. Fill in the custom fields and see whether or not new values can be saved when clicking OK, and then restored with the next opening of the form.

Files to Review

Example Code

WebSite/App_Code/CustomAppointmentFormController.cs(vb)
C#
using DevExpress.Web.ASPxScheduler; using DevExpress.Web.ASPxScheduler.Internal; using DevExpress.XtraScheduler; public class CustomAppointmentFormController : AppointmentFormController { public CustomAppointmentFormController(ASPxScheduler control, Appointment apt) : base(control, apt) { } public string CustomInfoField { get { return (string)EditedAppointmentCopy.CustomFields["ApptCustomInfo"]; } set { EditedAppointmentCopy.CustomFields["ApptCustomInfo"] = value; } } string SourceCustomInfoField { get { return (string)SourceAppointment.CustomFields["ApptCustomInfo"]; } set { SourceAppointment.CustomFields["ApptCustomInfo"] = value; } } public override bool IsAppointmentChanged() { bool isChanged = base.IsAppointmentChanged(); return isChanged || SourceCustomInfoField != CustomInfoField; } }
WebSite/App_Code/CustomAppointmentSaveCallbackCommand.cs(vb)
C#
using DevExpress.Web; using DevExpress.Web.ASPxScheduler; using DevExpress.Web.ASPxScheduler.Internal; using System.Web.UI; public class CustomAppointmentSaveCallbackCommand : AppointmentFormSaveCallbackCommand { public CustomAppointmentSaveCallbackCommand(ASPxScheduler control) : base(control) { } protected internal new CustomAppointmentFormController Controller { get { return (CustomAppointmentFormController)base.Controller; } } protected override AppointmentFormController CreateAppointmentFormController(DevExpress.XtraScheduler.Appointment apt) { return new CustomAppointmentFormController(Control, apt); } protected override Control FindControlByID(string id) { return FindTemplateControl(TemplateContainer, id); } System.Web.UI.Control FindTemplateControl(System.Web.UI.Control RootControl, string id) { System.Web.UI.Control foundedControl = RootControl.FindControl(id); if (foundedControl == null) { foreach (System.Web.UI.Control item in RootControl.Controls) { foundedControl = FindTemplateControl(item, id); if (foundedControl != null) break; } } return foundedControl; } protected override void AssignControllerValues() { base.AssignControllerValues(); ASPxComboBox cbSubject = FindControlByID("cbSubject") as ASPxComboBox; ASPxTextBox tbCustomInfo = FindControlByID("tbCustomInfo") as ASPxTextBox; Controller.Subject = cbSubject.Text; Controller.CustomInfoField = tbCustomInfo.Text; } }
WebSite/CustomForms/AppointmentForm.ascx
Code
<%@ Control Language="C#" AutoEventWireup="true" Inherits="AppointmentForm" CodeFile="AppointmentForm.ascx.cs" %> <%@ Register Assembly="DevExpress.Web.v16.1, Version=16.1.17.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web" TagPrefix="dx" %> <%@ Register Assembly="DevExpress.Web.ASPxScheduler.v16.1, Version=16.1.17.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web.ASPxScheduler.Controls" TagPrefix="dxsc" %> <%@ Register Assembly="DevExpress.Web.ASPxScheduler.v16.1, Version=16.1.17.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web.ASPxScheduler" TagPrefix="dxwschs" %> <div runat="server" id="ValidationContainer"> <table class="dxscAppointmentForm" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %> style="width: 100%; height: 230px;"> <tr> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblSubject" runat="server"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxComboBox ID="cbSubject" runat="server" ValueType="System.String"></dx:ASPxComboBox> </td> </tr> </table> </td> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblCustomInfo" runat="server" Text="Custom info:"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxTextBox ID="tbCustomInfo" runat="server" Width="100%" /> </td> </tr> </table> </td> </tr> <tr> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblLocation" runat="server" AssociatedControlID="tbLocation"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxTextBox ClientInstanceName="_dx" ID="tbLocation" runat="server" Width="100%" Text='<%# ((AppointmentFormTemplateContainer)Container).Appointment.Location %>' /> </td> </tr> </table> </td> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell" style="padding-left: 25px;"> <dx:ASPxLabel ID="lblLabel" runat="server" AssociatedControlID="edtLabel"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxComboBox ClientInstanceName="_dx" ID="edtLabel" runat="server" Width="100%" DataSource='<%# ((AppointmentFormTemplateContainer)Container).LabelDataSource %>' /> </td> </tr> </table> </td> </tr> <tr> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblStartDate" runat="server" AssociatedControlID="edtStartDate" Wrap="false"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxDateEdit ID="edtStartDate" runat="server" Width="100%" Date='<%# ((AppointmentFormTemplateContainer)Container).Start %>' EditFormat="Date" DateOnError="Undo" AllowNull="false" EnableClientSideAPI="true" > <ValidationSettings ErrorDisplayMode="ImageWithTooltip" ValidateOnLeave="false" EnableCustomValidation="True" Display="Dynamic" ValidationGroup="DateValidatoinGroup"> </ValidationSettings> </dx:ASPxDateEdit> </td> <td class="dxscControlCell" id="edtStartTimeLayoutRoot" style="padding-left: 5px;"> <dx:ASPxTimeEdit ID="edtStartTime" runat="server" Width="100%" DateTime='<%# ((AppointmentFormTemplateContainer)Container).Start %>' DateOnError="Undo" AllowNull="false" EnableClientSideAPI="true" > <ValidationSettings ErrorDisplayMode="ImageWithTooltip" ValidateOnLeave="false" EnableCustomValidation="True" Display="Dynamic" ValidationGroup="DateValidatoinGroup"> </ValidationSettings> </dx:ASPxTimeEdit> </td> </tr> </table> </td> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell" style="padding-left: 25px;"> <dx:ASPxLabel runat="server" ID="lblEndDate" Wrap="false" AssociatedControlID="edtEndDate"/> </td> <td class="dxscControlCell"> <dx:ASPxDateEdit id="edtEndDate" runat="server" Date='<%# ((AppointmentFormTemplateContainer)Container).End %>' EditFormat="Date" Width="100%" DateOnError="Undo" AllowNull="false" EnableClientSideAPI="true"> <ValidationSettings ErrorDisplayMode="ImageWithTooltip" ValidateOnLeave="false" EnableCustomValidation="True" Display="Dynamic" ValidationGroup="DateValidatoinGroup"> </ValidationSettings> </dx:ASPxDateEdit> </td> <td class="dxscControlCell" id="edtEndTimeLayoutRoot" style="padding-left: 5px;"> <dx:ASPxTimeEdit ID="edtEndTime" runat="server" Width="100%" DateTime='<%# ((AppointmentFormTemplateContainer)Container).End %>' DateOnError="Undo" AllowNull="false" EnableClientSideAPI="true" HelpTextSettings-PopupMargins-MarginLeft="50"> <ValidationSettings ErrorDisplayMode="ImageWithTooltip" ValidateOnLeave="false" EnableCustomValidation="True" Display="Dynamic" ValidationGroup="DateValidatoinGroup"> </ValidationSettings> </dx:ASPxTimeEdit> </td> </tr> </table> </td> </tr> <% if (TimeZonesEnabled) { %> <tr> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblTimeZone" runat="server" AssociatedControlID="edtStatus" Wrap="false"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxComboBox ClientInstanceName="_dx" ID="cbTimeZone" runat="server" Width="100%"/> </td> </tr> </table> </td> </tr> <% } %> <tr> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblStatus" runat="server" AssociatedControlID="edtStatus" Wrap="false"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <dx:ASPxComboBox ClientInstanceName="_dx" ID="edtStatus" runat="server" Width="100%" DataSource='<%# ((AppointmentFormTemplateContainer)Container).StatusDataSource %>' /> </td> </tr> </table> </td> <td class="dxscSingleCell" style="padding-left: 22px;"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td style="width: 20px; height: 20px;"> <dx:ASPxCheckBox ClientInstanceName="_dx" ID="chkAllDay" runat="server" Checked='<%# ((AppointmentFormTemplateContainer)Container).Appointment.AllDay %>'> </dx:ASPxCheckBox> </td> <td style="padding-left: 2px;"> <dx:ASPxLabel ID="lblAllDay" runat="server" AssociatedControlID="chkAllDay" /> </td> </tr> </table> </td> </tr> <tr> <% if(CanShowReminders) { %> <td class="dxscSingleCell"> <% } else { %> <td class="dxscDoubleCell" colspan="2"> <% } %> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell"> <dx:ASPxLabel ID="lblResource" runat="server" AssociatedControlID="edtResource"> </dx:ASPxLabel> </td> <td class="dxscControlCell"> <% if(ResourceSharing) { %> <dx:ASPxDropDownEdit id="ddResource" runat="server" Width="100%" ClientInstanceName="ddResource" Enabled='<%# ((AppointmentFormTemplateContainer)Container).CanEditResource %>' AllowUserInput="false"> <DropDownWindowTemplate> <dx:ASPxListBox id="edtMultiResource" runat="server" width="100%" SelectionMode="CheckColumn" DataSource='<%# ResourceDataSource %>' Border-BorderWidth="0"/> </DropDownWindowTemplate> </dx:ASPxDropDownEdit> <% } else { %> <dx:ASPxComboBox ClientInstanceName="_dx" ID="edtResource" runat="server" Width="100%" DataSource='<%# ResourceDataSource %> ' Enabled='<%# ((AppointmentFormTemplateContainer)Container).CanEditResource %>' /> <% } %> </td> </tr> </table> </td> <% if(CanShowReminders) { %> <td class="dxscSingleCell"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td class="dxscLabelCell" style="padding-left: 22px;"> <table class="dxscLabelControlPair" <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %>> <tr> <td style="width: 20px; height: 20px;"> <dx:ASPxCheckBox ID="chkReminder" runat="server"> </dx:ASPxCheckBox> </td> <td style="padding-left: 2px;"> <dx:ASPxLabel ID="lblReminder" runat="server" AssociatedControlID="chkReminder" /> </td> </tr> </table> </td> <td class="dxscControlCell" style="padding-left: 3px"> <dx:ASPxComboBox ID="cbReminder" runat="server" Width="100%" DataSource='<%# ((AppointmentFormTemplateContainer)Container).ReminderDataSource %>' /> </td> </tr> </table> </td> <% } %> </tr> <tr> <td class="dxscDoubleCell" colspan="2" style="height: 90px;"> <dx:ASPxMemo ClientInstanceName="_dx" ID="tbDescription" runat="server" Width="100%" Rows="6" Text='<%# ((AppointmentFormTemplateContainer)Container).Appointment.Description %>' /> </td> </tr> </table> </div> <dxsc:AppointmentRecurrenceForm ID="AppointmentRecurrenceForm1" runat="server" IsRecurring='<%# ((AppointmentFormTemplateContainer)Container).Appointment.IsRecurring %>' DayNumber='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceDayNumber %>' End='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceEnd %>' Month='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceMonth %>' OccurrenceCount='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceOccurrenceCount %>' Periodicity='<%# ((AppointmentFormTemplateContainer)Container).RecurrencePeriodicity %>' RecurrenceRange='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceRange %>' Start='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceStart %>' WeekDays='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceWeekDays %>' WeekOfMonth='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceWeekOfMonth %>' RecurrenceType='<%# ((AppointmentFormTemplateContainer)Container).RecurrenceType %>' IsFormRecreated='<%# ((AppointmentFormTemplateContainer)Container).IsFormRecreated %>' > </dxsc:AppointmentRecurrenceForm> <table <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %> style="width: 100%; height: 35px;"> <tr> <td class="dx-ac" style="width: 100%; height: 100%;" <%= DevExpress.Web.Internal.RenderUtils.GetAlignAttributes(this, "center", null) %>> <table class="dxscButtonTable" style="height: 100%"> <tr> <td class="dxscCellWithPadding"> <dx:ASPxButton runat="server" ID="btnOk" UseSubmitBehavior="false" AutoPostBack="false" EnableViewState="false" Width="91px" EnableClientSideAPI="true"/> </td> <td class="dxscCellWithPadding"> <dx:ASPxButton runat="server" ID="btnCancel" UseSubmitBehavior="false" AutoPostBack="false" EnableViewState="false" Width="91px" CausesValidation="False" EnableClientSideAPI="true" /> </td> <td class="dxscCellWithPadding"> <dx:ASPxButton runat="server" ID="btnDelete" UseSubmitBehavior="false" AutoPostBack="false" EnableViewState="false" Width="91px" Enabled='<%# ((AppointmentFormTemplateContainer)Container).CanDeleteAppointment %>' CausesValidation="False" /> </td> </tr> </table> </td> </tr> </table> <table <%= DevExpress.Web.Internal.RenderUtils.GetTableSpacings(this, 0, 0) %> style="width: 100%;"> <tr> <td class="dx-al" style="width: 100%;" <%= DevExpress.Web.Internal.RenderUtils.GetAlignAttributes(this, "left", null) %>> <dxsc:ASPxSchedulerStatusInfo runat="server" ID="schedulerStatusInfo" Priority="1" MasterControlId='<%# ((DevExpress.Web.ASPxScheduler.AppointmentFormTemplateContainer)Container).ControlId %>' /> </td> </tr> </table> <script id="dxss_ASPxSchedulerAppoinmentForm" type="text/javascript"> ASPxAppointmentForm = ASPx.CreateClass(ASPxClientFormBase, { Initialize: function () { this.isValid = true; this.isRecurrenceValid = true; this.controls.edtStartDate.Validation.AddHandler(ASPx.CreateDelegate(this.OnEdtStartDateValidate, this)); this.controls.edtEndDate.Validation.AddHandler(ASPx.CreateDelegate(this.OnEdtEndDateValidate, this)); this.controls.edtStartDate.ValueChanged.AddHandler(ASPx.CreateDelegate(this.OnUpdateStartDateTimeValue, this)); this.controls.edtEndDate.ValueChanged.AddHandler(ASPx.CreateDelegate(this.OnUpdateEndDateTimeValue, this)); this.controls.edtStartTime.ValueChanged.AddHandler(ASPx.CreateDelegate(this.OnUpdateStartDateTimeValue, this)); this.controls.edtEndTime.ValueChanged.AddHandler(ASPx.CreateDelegate(this.OnUpdateEndDateTimeValue, this)); this.controls.chkAllDay.CheckedChanged.AddHandler(ASPx.CreateDelegate(this.OnChkAllDayCheckedChanged, this)); this.controls.btnOk.Click.AddHandler(ASPx.CreateDelegate(this.OnBtnOk, this)); if (this.controls.AppointmentRecurrenceForm1) this.controls.AppointmentRecurrenceForm1.ValidationCompleted.AddHandler(ASPx.CreateDelegate(this.OnRecurrenceRangeControlValidationCompleted, this)); this.UpdateTimeEditorsVisibility(); if (this.controls.chkReminder) this.controls.chkReminder.CheckedChanged.AddHandler(ASPx.CreateDelegate(this.OnChkReminderCheckedChanged, this)); if (this.controls.edtMultiResource) this.controls.edtMultiResource.SelectedIndexChanged.AddHandler(ASPx.CreateDelegate(this.OnEdtMultiResourceSelectedIndexChanged, this)); var start = this.controls.edtStartDate.GetValue(); var end = this.controls.edtEndDate.GetValue(); var duration = ASPxClientTimeInterval.CalculateDuration(start, end); this.appointmentInterval = new ASPxClientTimeInterval(start, duration); this.appointmentInterval.SetAllDay(this.controls.chkAllDay.GetValue()); this.primaryIntervalJson = ASPx.Json.ToJson(this.appointmentInterval); this.UpdateDateTimeEditors(); }, OnBtnOk: function (s, e) { e.processOnServer = false; var formOwner = this.GetFormOwner(); if (!formOwner) return; if (this.controls.AppointmentRecurrenceForm1 && this.IsRecurrenceChainRecreationNeeded() && this.cpHasExceptions) { formOwner.ShowMessageBox(this.localization.SchedulerLocalizer.Msg_Warning, this.localization.SchedulerLocalizer.Msg_RecurrenceExceptionsWillBeLost, this.OnWarningExceptionWillBeLostOk.aspxBind(this)); } else { formOwner.AppointmentFormSave(); } }, IsRecurrenceChainRecreationNeeded: function () { var isIntervalChanged = this.primaryIntervalJson != ASPx.Json.ToJson(this.appointmentInterval); return isIntervalChanged || this.controls.AppointmentRecurrenceForm1.IsChanged(); }, OnWarningExceptionWillBeLostOk: function () { this.GetFormOwner().AppointmentFormSave(); }, OnEdtMultiResourceSelectedIndexChanged: function (s, e) { var resourceNames = new Array(); var items = s.GetSelectedItems(); var count = items.length; if (count > 0) { for (var i = 0; i < count; i++) resourceNames.push(items[i].text); } else resourceNames.push(ddResource.cp_Caption_ResourceNone); ddResource.SetValue(resourceNames.join(', ')); }, OnEdtStartDateValidate: function (s, e) { if (!e.isValid) return; var startDate = this.controls.edtStartDate.GetDate(); var endDate = this.controls.edtEndDate.GetDate(); e.isValid = startDate == null || endDate == null || startDate <= endDate; e.errorText = "The Start Date must precede the End Date."; }, OnEdtEndDateValidate: function (s, e) { if (!e.isValid) return; var startDate = this.controls.edtStartDate.GetDate(); var endDate = this.controls.edtEndDate.GetDate(); e.isValid = startDate == null || endDate == null || startDate <= endDate; e.errorText = "The Start Date must precede the End Date."; }, OnUpdateEndDateTimeValue: function (s, e) { var isAllDay = this.controls.chkAllDay.GetValue(); var date = ASPxSchedulerDateTimeHelper.TruncToDate(this.controls.edtEndDate.GetDate()); if (isAllDay) date = ASPxSchedulerDateTimeHelper.AddDays(date, 1); var time = ASPxSchedulerDateTimeHelper.ToDayTime(this.controls.edtEndTime.GetDate()); var dateTime = ASPxSchedulerDateTimeHelper.AddTimeSpan(date, time); this.appointmentInterval.SetEnd(dateTime); this.UpdateDateTimeEditors(); this.Validate(); }, OnUpdateStartDateTimeValue: function (s, e) { var date = ASPxSchedulerDateTimeHelper.TruncToDate(this.controls.edtStartDate.GetDate()); var time = ASPxSchedulerDateTimeHelper.ToDayTime(this.controls.edtStartTime.GetDate()); var dateTime = ASPxSchedulerDateTimeHelper.AddTimeSpan(date, time); this.appointmentInterval.SetStart(dateTime); this.UpdateDateTimeEditors(); if (this.controls.AppointmentRecurrenceForm1) this.controls.AppointmentRecurrenceForm1.SetStart(dateTime); this.Validate(); }, OnChkReminderCheckedChanged: function (s, e) { var isReminderEnabled = this.controls.chkReminder.GetValue(); if (isReminderEnabled) this.controls.cbReminder.SetSelectedIndex(3); else this.controls.cbReminder.SetSelectedIndex(-1); this.controls.cbReminder.SetEnabled(isReminderEnabled); }, OnChkAllDayCheckedChanged: function (s, e) { this.UpdateTimeEditorsVisibility(); var isAllDay = this.controls.chkAllDay.GetValue(); this.appointmentInterval.SetAllDay(isAllDay); this.UpdateDateTimeEditors(); }, UpdateDateTimeEditors: function () { var isAllDay = this.controls.chkAllDay.GetValue(); this.controls.edtStartDate.SetValue(this.appointmentInterval.GetStart()); var end = this.appointmentInterval.GetEnd(); if (isAllDay) { end = ASPxSchedulerDateTimeHelper.AddDays(end, -1); } this.controls.edtEndDate.SetValue(end); this.controls.edtStartTime.SetValue(this.appointmentInterval.GetStart()); this.controls.edtEndTime.SetValue(end); }, UpdateTimeEditorsVisibility: function () { var isAllDay = this.controls.chkAllDay.GetValue(); var visible = (isAllDay) ? "none" : ""; var startRoot = ASPx.GetParentById(this.controls.edtStartTime.GetMainElement(), "edtStartTimeLayoutRoot"); var endRoot = ASPx.GetParentById(this.controls.edtEndTime.GetMainElement(), "edtEndTimeLayoutRoot"); startRoot.style.display = visible; endRoot.style.display = visible; }, Validate: function () { this.isValid = ASPxClientEdit.ValidateEditorsInContainer(null); this.controls.btnOk.SetEnabled(this.isValid && this.isRecurrenceValid); }, OnRecurrenceRangeControlValidationCompleted: function (s, e) { if (!this.controls.AppointmentRecurrenceForm1) return; this.isRecurrenceValid = this.controls.AppointmentRecurrenceForm1.IsValid(); this.controls.btnOk.SetEnabled(this.isValid && this.isRecurrenceValid); } }); </script>
WebSite/CustomForms/AppointmentForm.ascx.cs(vb)
C#
using DevExpress.Utils; using DevExpress.Web; using DevExpress.Web.ASPxScheduler; using DevExpress.Web.ASPxScheduler.Internal; using DevExpress.Web.ASPxScheduler.Localization; using DevExpress.XtraScheduler; using DevExpress.XtraScheduler.Localization; using System; using System.Collections; using System.Collections.Generic; using System.Web.UI; using System.Web.UI.WebControls; public partial class AppointmentForm : SchedulerFormControl { public override string ClassName { get { return "ASPxAppointmentForm"; } } public bool CanShowReminders { get { return ((AppointmentFormTemplateContainer)Parent).Control.Storage.EnableReminders; } } public bool ResourceSharing { get { return ((AppointmentFormTemplateContainer)Parent).Control.Storage.ResourceSharing; } } public IEnumerable ResourceDataSource { get { return ((AppointmentFormTemplateContainer)Parent).ResourceDataSource; } } public bool TimeZonesEnabled { get { return ((AppointmentFormTemplateContainer)Parent).TimeZonesEnabled; } } protected override void OnLoad(EventArgs e) { base.OnLoad(e); Localize(); } void Localize() { lblSubject.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_Subject); lblLocation.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_Location); lblLabel.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_Label); lblStartDate.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_StartTime); lblEndDate.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_EndTime); lblStatus.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_Status); lblAllDay.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_AllDayEvent); lblResource.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_Resource); if (CanShowReminders) lblReminder.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_Reminder); btnOk.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_ButtonOk); btnCancel.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_ButtonCancel); btnDelete.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_ButtonDelete); if (TimeZonesEnabled) lblTimeZone.Text = ASPxSchedulerLocalizer.GetString(ASPxSchedulerStringId.Form_TimeZone); btnOk.Wrap = DefaultBoolean.False; btnCancel.Wrap = DefaultBoolean.False; btnDelete.Wrap = DefaultBoolean.False; } private void BindSubjectCombobox() { List<string> subjectList = new List<string>(); subjectList.Add("Meeting"); subjectList.Add("Business travel"); subjectList.Add("Phone call"); cbSubject.DataSource = subjectList; cbSubject.DataBind(); } public override void DataBind() { base.DataBind(); AppointmentFormTemplateContainer container = (AppointmentFormTemplateContainer)Parent; Appointment apt = container.Appointment; IAppointmentStorageBase appointmentStorage = container.Control.Storage.Appointments; IAppointmentLabel label = appointmentStorage.Labels.GetById(apt.LabelKey); IAppointmentStatus status = appointmentStorage.Statuses.GetById(apt.StatusKey); edtLabel.ValueType = apt.LabelKey.GetType(); edtLabel.SelectedIndex = appointmentStorage.Labels.IndexOf(label); edtStatus.ValueType = apt.StatusKey.GetType(); edtStatus.SelectedIndex = appointmentStorage.Statuses.IndexOf(status); PopulateResourceEditors(apt, container); BindSubjectCombobox(); cbSubject.Value = container.Subject; tbCustomInfo.Text = container.Appointment.CustomFields["ApptCustomInfo"] != null ? container.Appointment.CustomFields["ApptCustomInfo"].ToString() : ""; AppointmentRecurrenceForm1.Visible = container.ShouldShowRecurrence; if (apt.HasReminder) { cbReminder.Value = apt.Reminder.TimeBeforeStart.ToString(); chkReminder.Checked = true; } else { cbReminder.ClientEnabled = false; } if (TimeZonesEnabled) { cbTimeZone.DataSource = TimeZoneInfo.GetSystemTimeZones(); cbTimeZone.ValueField = "Id"; cbTimeZone.TextField = "DisplayName"; cbTimeZone.DataBind(); cbTimeZone.Value = container.TimeZoneId; cbTimeZone.Enabled = apt.Type == AppointmentType.Normal || apt.Type == AppointmentType.Pattern; } //btnOk.ClientSideEvents.Click = container.SaveHandler; btnCancel.ClientSideEvents.Click = container.CancelHandler; btnDelete.ClientSideEvents.Click = container.DeleteHandler; JSProperties.Add("cpHasExceptions", apt.HasExceptions); //btnDelete.Enabled = !container.IsNewAppointment; } private void PopulateResourceEditors(Appointment apt, AppointmentFormTemplateContainer container) { if (ResourceSharing) { ASPxListBox edtMultiResource = ddResource.FindControl("edtMultiResource") as ASPxListBox; if (edtMultiResource == null) return; SetListBoxSelectedValues(edtMultiResource, apt.ResourceIds); List<String> multiResourceString = GetListBoxSelectedItemsText(edtMultiResource); string stringResourceNone = SchedulerLocalizer.GetString(SchedulerStringId.Caption_ResourceNone); ddResource.Value = stringResourceNone; if (multiResourceString.Count > 0) ddResource.Value = String.Join(", ", multiResourceString.ToArray()); ddResource.JSProperties.Add("cp_Caption_ResourceNone", stringResourceNone); } else { if (!Object.Equals(apt.ResourceId, EmptyResourceId.Id)) edtResource.Value = apt.ResourceId.ToString(); else edtResource.Value = SchedulerIdHelper.EmptyResourceId; } } List<String> GetListBoxSelectedItemsText(ASPxListBox listBox) { List<String> result = new List<string>(); foreach (ListEditItem editItem in listBox.Items) { if (editItem.Selected) result.Add(editItem.Text); } return result; } void SetListBoxSelectedValues(ASPxListBox listBox, IEnumerable values) { listBox.Value = null; foreach (object value in values) { ListEditItem item = listBox.Items.FindByValue(value.ToString()); if (item != null) item.Selected = true; } } protected override void PrepareChildControls() { AppointmentFormTemplateContainer container = (AppointmentFormTemplateContainer)Parent; ASPxScheduler control = container.Control; AppointmentRecurrenceForm1.EditorsInfo = new EditorsInfo(control, control.Styles.FormEditors, control.Images.FormEditors, control.Styles.Buttons); base.PrepareChildControls(); } protected override ASPxEditBase[] GetChildEditors() { ASPxEditBase[] edits = new ASPxEditBase[] { lblSubject, cbSubject, tbCustomInfo, lblLocation, tbLocation, lblLabel, edtLabel, lblStartDate, edtStartDate, lblEndDate, edtEndDate, lblStatus, edtStatus, lblAllDay, chkAllDay, lblResource, edtResource, tbDescription, cbReminder, lblReminder, ddResource, chkReminder, GetMultiResourceEditor(), edtStartTime, edtEndTime, cbTimeZone }; return edits; } ASPxEditBase GetMultiResourceEditor() { if (ddResource != null) return ddResource.FindControl("edtMultiResource") as ASPxEditBase; return null; } protected override ASPxButton[] GetChildButtons() { ASPxButton[] buttons = new ASPxButton[] { btnOk, btnCancel, btnDelete }; return buttons; } protected override Control[] GetChildControls() { return new Control[] { ValidationContainer, AppointmentRecurrenceForm1 }; } protected override WebControl GetDefaultButton() { return btnOk; } protected override void PrepareLocalization(SchedulerLocalizationCache localizationCache) { localizationCache.Add(SchedulerStringId.Msg_RecurrenceExceptionsWillBeLost); localizationCache.Add(SchedulerStringId.Msg_Warning); } }
WebSite/Default.aspx
ASPx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <dx:ASPxScheduler ID="ASPxScheduler1" runat="server" AppointmentDataSourceID="appointmentDataSource" ClientIDMode="AutoID" OnAppointmentRowInserted="ASPxScheduler1_AppointmentRowInserted" Start="2016-09-20" OnBeforeExecuteCallbackCommand="ASPxScheduler1_BeforeExecuteCallbackCommand"> <Storage> <Appointments> <Mappings AppointmentId="Id" Start="StartTime" End="EndTime" Subject="Subject" AllDay="AllDay" Description="Description" Label="Label" Location="Location" RecurrenceInfo="RecurrenceInfo" ReminderInfo="ReminderInfo" Status="Status" Type="EventType" /> <CustomFieldMappings> <dx:ASPxAppointmentCustomFieldMapping Member="CustomInfo" Name="ApptCustomInfo" ValueType="String" /> </CustomFieldMappings> </Appointments> </Storage> <Views> <DayView> <TimeRulers> <dx:TimeRuler></dx:TimeRuler> </TimeRulers> </DayView> <WorkWeekView> <TimeRulers> <dx:TimeRuler></dx:TimeRuler> </TimeRulers> </WorkWeekView> <FullWeekView> <TimeRulers> <dx:TimeRuler></dx:TimeRuler> </TimeRulers> </FullWeekView> </Views> <OptionsForms AppointmentFormTemplateUrl="CustomForms/AppointmentForm.ascx" /> </dx:ASPxScheduler> </div> <asp:ObjectDataSource ID="appointmentDataSource" runat="server" DataObjectTypeName="CustomEvent" TypeName="CustomEventDataSource" DeleteMethod="DeleteMethodHandler" SelectMethod="SelectMethodHandler" InsertMethod="InsertMethodHandler" UpdateMethod="UpdateMethodHandler" OnObjectCreated="appointmentsDataSource_ObjectCreated" /> </form> </body> </html>
WebSite/Default.aspx.cs(vb)
C#
using System; using System.Web.UI.WebControls; using DevExpress.Web.ASPxScheduler; public partial class Default : System.Web.UI.Page { Object lastInsertedAppointmentId; protected void Page_Load(object sender, EventArgs e) { ASPxScheduler1.Storage.Appointments.AutoRetrieveId = true; } #region #setappointment CustomEventDataSource objectInstance; // Obtain the ID of the last inserted appointment from the object data source and assign it to the appointment in the ASPxScheduler storage. protected void ASPxScheduler1_AppointmentRowInserted(object sender, DevExpress.Web.ASPxScheduler.ASPxSchedulerDataInsertedEventArgs e) { e.KeyFieldValue = this.objectInstance.ObtainLastInsertedId(); } protected void appointmentsDataSource_ObjectCreated(object sender, ObjectDataSourceEventArgs e) { this.objectInstance = new CustomEventDataSource(GetCustomEvents()); e.ObjectInstance = this.objectInstance; } CustomEventList GetCustomEvents() { CustomEventList events = Session["CustomEventListData"] as CustomEventList; if (events == null) { events = new CustomEventList(); Session["CustomEventListData"] = events; } return events; } #endregion #setappointment #region #appointmentid // The following code is unnecessary if the ASPxScheduler.Storage.Appointments.AutoRetrieveId option is TRUE. protected void ASPxScheduler1_AppointmentInserted(object sender, DevExpress.XtraScheduler.PersistentObjectsEventArgs e) { SetAppointmentId(sender, e); } protected void appointmentsDataSource_Inserted(object sender, ObjectDataSourceStatusEventArgs e) { this.lastInsertedAppointmentId = e.ReturnValue; } void SetAppointmentId(object sender, DevExpress.XtraScheduler.PersistentObjectsEventArgs e) { DevExpress.Web.ASPxScheduler.ASPxSchedulerStorage storage = (DevExpress.Web.ASPxScheduler.ASPxSchedulerStorage)sender; DevExpress.XtraScheduler.Appointment apt = (DevExpress.XtraScheduler.Appointment)e.Objects[0]; storage.SetAppointmentId(apt, this.lastInsertedAppointmentId); } #endregion #appointmentid #region #beforeexecutecallbackcommand protected void ASPxScheduler1_BeforeExecuteCallbackCommand(object sender, DevExpress.Web.ASPxScheduler.SchedulerCallbackCommandEventArgs e) { if (e.CommandId == SchedulerCallbackCommandId.AppointmentSave) { e.Command = new CustomAppointmentSaveCallbackCommand((ASPxScheduler)sender); } } #endregion #beforeexecutecallbackcommand }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.