Skip to main content

How to: Customize the Appointment Dialog using View Model API (working with custom fields)

  • 8 minutes to read

Note

The information in this topic applies to DevExpress ASP.NET Scheduler version 17.2 and later.

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, bind the Scheduler extension to a collection of custom objects. Then, follow the steps below:

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 at runtime using the following code:

appointmentStorage.CustomFieldMappings.Add(new DevExpress.Web.ASPxScheduler.ASPxAppointmentCustomFieldMapping("AppointmentCompany", "CompanyID"));
appointmentStorage.CustomFieldMappings.Add(new DevExpress.Web.ASPxScheduler.ASPxAppointmentCustomFieldMapping("AppointmentContact", "ContactID")); 

Step 3 - Create a custom AppointmentEditDialogViewModel descendant

The AppointmentEditDialogViewModel descendant allows you to change 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.

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 you to specify 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 you to change the “Company” and “Contact” combo boxes’ visibility based on the “Subject” editor value.

TrackPropertyChangeFor(m => m.Subject, () => { });

The AppointmentEditDialogViewModel object allows you to change 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 = settings.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:

var dialog = settings.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);