How to: Customize the Appointment Dialog using View Model API (working with custom fields)
- 8 minutes to read
As a follow-up to How to: Customize the Appointment Dialog using View Model API (simple customization), this topic provides a step-by-step tutorial about advanced Appointment Dialog customization (working with custom fields).
In this tutorial, cascading combo boxes are used to edit the two hierarchical custom fields. The first combo box (with the “meeting”, “travel”, “phone call” values) replaces the “Subject” text box. When an end-user selects the “phone call” item from the editor’s dropdown, the “Company” and “Contact” combo boxes appear in the dialog.
To use this tutorial, start with a sample project in which the ASPxScheduler control is bound to a collection of custom objects: How to: Bind ASPxScheduler Appointment to ObjectDataSource.
Then, follow the steps below:
- Step 1 - Update Data Source
- Step 2 - Specify Custom Field Mappings
- Step 3 - Create a custom AppointmentEditDialogViewModel descendant
- Step 4 - Replace the predefined View Model with a custom one
- Step 5 - Generate a default dialog layout and add custom editors into the dialog
Step 1 - Update Data Source
Add two integer properties (“CompanyID” and “ContactID”) to the CustomAppointment class:
public int CompanyID { get { return m_company_id; } set { m_company_id = value; } }
public int ContactID { get { return m_conact_id; } set { m_conact_id = value; } }
Step 2 - Specify Custom Field Mappings
Add custom field mappings to the previously created fields using the Visual Studio Designer, markup or property grid (ASPxScheduler control ->Storage->Appointments->CustomFieldMappings).
<CustomFieldMappings>
<dxwschs:ASPxAppointmentCustomFieldMapping Name="AppointmentCompany" Member="CompanyID" ValueType="Integer" />
<dxwschs:ASPxAppointmentCustomFieldMapping Name="AppointmentContact" Member="ContactID" ValueType="Integer" />
</CustomFieldMappings>
If required, you can add mappings at runtime instead. In this case, use the following code in the code-behind file for the page that contains the ASPxScheduler control (Default.aspx.cs or Default.aspx.vb if you use Visual Basic):
ASPxScheduler1.Storage.Appointments.CustomFieldMappings.Add(new AppointmentCustomFieldMapping("AppointmentCompany", "CompanyID", FieldValueType.Integer));
ASPxScheduler1.Storage.Appointments.CustomFieldMappings.Add(new AppointmentCustomFieldMapping("AppointmentContact", "ContactID", FieldValueType.Integer));
Step 3 - Create a custom AppointmentEditDialogViewModel descendant
The AppointmentEditDialogViewModel descendant allows changing predefined editors, remove unnecessary editors and add new editors (fields) using the Load method overload.
Replace the “Subject” text box with a combo box by calling the Load method overload. In the following code, use the SetEditorTypeFor method to specify the type of the editor used for editing a specific field, and the SetDataItemsFor method to generate items for the editors intended to work with collections.
public override void Load(AppointmentFormController appointmentController) {
base.Load(appointmentController);
SetEditorTypeFor(m => m.Subject, DialogFieldEditorType.ComboBox);
SetDataItemsFor(m => m.Subject, (addItemDelegate) => {
addItemDelegate("meeting", "meeting");
addItemDelegate("travel", "travel");
addItemDelegate("phone call", "phonecall");
});
}
Add two combo boxes to edit the “Company” and “Contact” custom fields. For this, declare two corresponding properties in the CustomAppointmentEditDialogViewModel object. Note that the properties names should be the same as in the “custom field” mappings to save and load the custom field values correctly. In the code, the DialogFieldViewSettings parameters allow specifying the editor type used for editing a property value, as well as the editor caption.
[DialogFieldViewSettings(Caption = "Company", EditorType = DialogFieldEditorType.ComboBox)]
public int AppointmentCompany { get; set; }
[DialogFieldViewSettings(Caption = "Contact", EditorType = DialogFieldEditorType.ComboBox)]
public int AppointmentContact { get; set; }
Generate items for the cascading combo boxes using the SetDataItemsFor method within the AppointmentEditDialogViewModel.Load method.
List<Company> companies = Company.GenerateCompanyDataSource();
SetDataItemsFor((CustomAppointmentEditDialogViewModel m) => m.AppointmentCompany, (addItemDelegate) => {
foreach(Company comp in companies) {
addItemDelegate(comp.CompanyName, comp.CompanyID);
}
});
Implement “Contact” combo box filtering items based on a selected “Company” combo box value.
SetDataItemsFor((CustomAppointmentEditDialogViewModel m) => m.AppointmentContact, (addItemDelegate) => {
List<CompanyContact> contacts = CompanyContact.GenerateContactDataSource().Where(c => c.CompanyID == AppointmentCompany).ToList();
addItemDelegate("", 0);
foreach(CompanyContact cont in contacts) {
addItemDelegate(cont.ContactName, cont.ContactID);
}
});
Then, use the TrackPropertyChangeFor method to regenerate the “Contact” combo box items after changing the “Company” editor’s value. This method allows you to track property value changes on the server-side using a corresponding callback request. The form editors are reloaded after executing the callback request.
TrackPropertyChangeFor((CustomAppointmentEditDialogViewModel m) => m.AppointmentCompany, () => {
AppointmentContact = 0;
});
Invoke the TrackPropertyChangeFor method for the “Subject” field with an empty action to reload the dialog content. It allows changing the “Company” and “Contact” combo boxes’ visibility depending on the “Subject” editor value.
The AppointmentEditDialogViewModel object allows changing the dialog editors’ visibility and availability based on custom conditions.
Use the SetDialogElementStateConditions overload method to customize the editors’ visibility on the form as follows:
- Hiding the “Location”, “All-Day” and “Reminder” editors on the edit form.
- Showing “Company” and “Contact” editors only when an end-user selects the “phone call” item in the “Subject” editor’s dropdown.
- Showing the “Contact” editor only when an end-user selects an item in the “Company” combo box.
public override void SetDialogElementStateConditions() {
base.SetDialogElementStateConditions();
SetItemVisibilityCondition("Location", false);
SetItemVisibilityCondition(vm => vm.IsAllDay, false);
SetItemVisibilityCondition(vm => vm.Reminder, false);
SetEditorEnabledCondition((CustomAppointmentEditDialogViewModel vm) => vm.AppointmentContact, AppointmentCompany > 0);
SetItemVisibilityCondition((CustomAppointmentEditDialogViewModel vm) => vm.AppointmentContact, Subject == "phonecall");
SetItemVisibilityCondition((CustomAppointmentEditDialogViewModel vm) => vm.AppointmentCompany, Subject == "phonecall");
}
The custom AppointmentEditDialogViewModel descendant’s complete code for the described scenario is illustrated below:
public class CustomAppointmentEditDialogViewModel : AppointmentEditDialogViewModel {
[DialogFieldViewSettings(Caption = "Company", EditorType = DialogFieldEditorType.ComboBox)]
public int AppointmentCompany { get; set; }
[DialogFieldViewSettings(Caption = "Contact", EditorType = DialogFieldEditorType.ComboBox)]
public int AppointmentContact { get; set; }
public override void Load(AppointmentFormController appointmentController) {
base.Load(appointmentController);
SetEditorTypeFor(m => m.Subject, DialogFieldEditorType.ComboBox);
SetDataItemsFor(m => m.Subject, (addItemDelegate) => {
addItemDelegate("meeting", "meeting");
addItemDelegate("travel", "travel");
addItemDelegate("phone call", "phonecall");
});
List<Company> companies = Company.GenerateCompanyDataSource();
SetDataItemsFor((CustomAppointmentEditDialogViewModel m) => m.AppointmentCompany, (addItemDelegate) => {
foreach(Company comp in companies) {
addItemDelegate(comp.CompanyName, comp.CompanyID);
}
});
SetDataItemsFor((CustomAppointmentEditDialogViewModel m) => m.AppointmentContact, (addItemDelegate) => {
List<CompanyContact> contacts = CompanyContact.GenerateContactDataSource().Where(c => c.CompanyID == AppointmentCompany).ToList();
addItemDelegate("", 0);
foreach(CompanyContact cont in contacts) {
addItemDelegate(cont.ContactName, cont.ContactID);
}
});
TrackPropertyChangeFor((CustomAppointmentEditDialogViewModel m) => m.AppointmentCompany, () => {
AppointmentContact = 0;
});
TrackPropertyChangeFor(m => m.Subject, () => { });
}
public override void SetDialogElementStateConditions() {
base.SetDialogElementStateConditions();
SetItemVisibilityCondition("Location", false);
SetItemVisibilityCondition(vm => vm.IsAllDay, false);
SetItemVisibilityCondition(vm => vm.Reminder, false);
SetEditorEnabledCondition((CustomAppointmentEditDialogViewModel vm) => vm.AppointmentContact, AppointmentCompany > 0);
SetItemVisibilityCondition((CustomAppointmentEditDialogViewModel vm) => vm.AppointmentContact, Subject == "phonecall");
SetItemVisibilityCondition((CustomAppointmentEditDialogViewModel vm) => vm.AppointmentCompany, Subject == "phonecall");
}
}
Step 4 - Replace the predefined View Model with a custom one
To replace the predefined View Model with a custom one, use the following code (for example, within the Page.Init event handler):
var dialog = ASPxScheduler1.OptionsForms.DialogLayoutSettings.AppointmentDialog;
dialog.ViewModel = new CustomAppointmentEditDialogViewModel();
Step 5 - Generate a default dialog layout and add custom editors into the dialog
The AppointmentDialog.GenerateDefaultLayoutElements method generates the default dialog layout and can also be used to reorder custom editors on the dialog form.
To locate the editors below the “Description” field on the form, do the following:
protected void Page_Init(object sender, EventArgs e) {
var dialog = ASPxScheduler1.OptionsForms.DialogLayoutSettings.AppointmentDialog;
dialog.ViewModel = new CustomAppointmentEditDialogViewModel();
dialog.GenerateDefaultLayoutElements();
var companies = dialog.LayoutElements.CreateField((CustomAppointmentEditDialogViewModel m) => m.AppointmentCompany);
var contacts = dialog.LayoutElements.CreateField((CustomAppointmentEditDialogViewModel m) => m.AppointmentContact);
dialog.InsertBefore(companies, dialog.FindLayoutElement("Description"));
dialog.InsertAfter(contacts, companies);
}