Skip to main content

Lesson 9 - Create a Custom Edit Appointment Form (legacy)

  • 8 minutes to read

Note

You are viewing documentation for the legacy WPF Scheduler control. If you’re starting a new project, we strongly recommend that you use a new control declared in the DevExpress.Xpf.Scheduling namespace. If you decide to upgrade an existing project in order to switch to the updated scheduler control, see the Migration Guidelines document.

This document describes how to substitute a standard Edit Appointment form with a custom form. This is useful when you bind a scheduler control to data with custom fields, and wish to provide end users with the capability to edit these fields in a custom Edit Appointment form. To simplify this example, disable the Appointment Recurrence feature, and prevent end users from working with recurring appointments using the Appointment Recurrence dialog.

This lesson consists of the following sections.

Specify Custom Field Mappings

  1. Open the WPF application with the SchedulerControl, which was created in Lesson 6 - Bind a Scheduler to MS SQL Server Database (legacy) of the current tutorial.
  2. Add a custom field mapping for the non-standard appointment field that has the “CustomField1” name in the datasource. To do this, configure the Storage.AppointmentStorage.CustomFieldMappings property. Click the Edit Items in This Collection Scheduler_Lesson3_MoreButton button to invoke the Collection Editor window.

    Scheduler_Lesson9_Image1

  3. In the invoked window, click the Add button to add a new Item from the Collection. Configure item parameters as illustrated in the image below.

    Scheduler_Lesson3_Image2

    Your XAML may look like the following. (If it does not, you can overwrite your code with the code below.)

    <dxsch:AppointmentStorage.CustomFieldMappings>
        <dxsch:SchedulerCustomFieldMapping 
            Member="CustomField1" 
            Name="Contact" 
            ValueType="String"/>
    </dxsch:AppointmentStorage.CustomFieldMappings>
    

Create a Custom Edit Appointment Form

  1. In the Visual Studio menu, click Project | Add User Control…

    Scheduler_Lesson9_Image2_1

    You can also press CTRL+SHIFT+A to invoke the Add New Item dialog.

    In this dialog, select the WPF group of templates, select the User Control (WPF) item, set its name to CustomAppointmentForm.xaml, and click Add.

    Scheduler_Lesson3_Image3

  2. Add controls to edit the following appointment properties.

    To add these controls and specify the appointment properties that will be edited using these controls, overwrite the CustomAppointmentForm.xaml file as follows.

    <UserControl x:Class="WpfApplication1.CustomAppointmentForm"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors" 
                 xmlns:dxsch="http://schemas.devexpress.com/winfx/2008/xaml/scheduler"
                 xmlns:dxschi="http://schemas.devexpress.com/winfx/2008/xaml/scheduler/internal"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300"
                 Loaded="UserControl_Loaded">
    
        <UserControl.Resources>
            <dxschi:TimeSpanToDateTimeConverter x:Key="timeSpanToDateTimeConverter"/>
            <dxschi:InvertedBoolConverter x:Key="invertedBoolConverter"/>
        </UserControl.Resources>
    
        <Grid Margin="14">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="80"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
    
            <!--Subject-->
            <TextBlock Margin="0,0,0,8" 
                       HorizontalAlignment="Left"
                       Text="Subject:" />
            <dxe:TextEdit Margin="0,0,0,8"
                          Grid.Column="1" Grid.ColumnSpan="2"
                          EditValue="{Binding Controller.Subject, Mode=TwoWay}"/>
    
            <!--Start-->
            <TextBlock Margin="0,0,4,4"
                       HorizontalAlignment="Left"
                       Grid.Row="1" Grid.Column="0" 
                       Text="Start:" />
            <dxe:DateEdit Margin="0,0,4,4"
                          Grid.Row="1" Grid.Column="1" 
                          EditValue="{Binding Controller.DisplayStartDate, Mode=TwoWay}"/>
            <dxe:ButtonEdit Margin="0,0,0,4"
                            Grid.Row="1" Grid.Column="2"
                            AllowDefaultButton="False"
                            MaskType="DateTime" DisplayFormatString="{Binding TimeEditMask}"
                            Mask="{Binding TimeEditMask}" 
                            EditValue="{Binding Controller.DisplayStartTime, Mode=TwoWay,
                                                Converter={StaticResource timeSpanToDateTimeConverter}}"
                            IsEnabled="{Binding ElementName=chkAllDay, Path=IsChecked,
                                                Converter={StaticResource ResourceKey=invertedBoolConverter}}"/>
    
            <!--End-->
            <TextBlock HorizontalAlignment="Left"
                       Grid.Row="2" Grid.Column="0"
                       Text="End:"/>
            <dxe:DateEdit Margin="0,0,4,4"
                          Grid.Row="2" Grid.Column="1" 
                          EditValue="{Binding Controller.DisplayEndDate, Mode=TwoWay}" />
            <dxe:ButtonEdit Margin="0,0,0,4"
                            Grid.Row="2" Grid.Column="2" 
                            AllowDefaultButton="False" MaskType="DateTime"
                            DisplayFormatString="{Binding TimeEditMask}" Mask="{Binding TimeEditMask}" 
                            EditValue="{Binding Controller.DisplayEndTime, Mode=TwoWay,
                                                Converter={StaticResource timeSpanToDateTimeConverter}}" 
                            IsEnabled="{Binding ElementName=chkAllDay, Path=IsChecked, 
                                                Converter={StaticResource ResourceKey=invertedBoolConverter}}"/>
    
            <!--All day event-->
            <dxe:CheckEdit Name="chkAllDay" Margin="0,0,0,8"
                           HorizontalAlignment="Left"
                           Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" 
                           Content="All day" IsChecked="{Binding Controller.AllDay, Mode=TwoWay}"/>
    
            <!--Label-->
            <TextBlock Margin="0,0,0,4" 
                       HorizontalAlignment="Left"
                       Grid.Row="4" Grid.Column="0"
                       Text="Label:"/>
            <dxsch:AppointmentLabelEdit Margin="0,0,0,4"
                                     Grid.Column="1" Grid.Row="4" Grid.ColumnSpan="2"
                                     Storage="{Binding Controller.Storage}"
                                     EditValue="{Binding Controller.Label, Mode=TwoWay}" />
    
            <!--Status-->
            <TextBlock Margin="0,0,0,8" 
                       HorizontalAlignment="Left" 
                       Grid.Row="5" Grid.Column="0" 
                       Text="Status:" />
            <dxsch:AppointmentStatusEdit Margin="0,0,0,8"
                                      Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="2"
                                      Storage="{Binding Controller.Storage}" 
                                      EditValue="{Binding Controller.Status, Mode=TwoWay}" />
    
            <!--Contact Info (custom field)-->
            <TextBlock 
                       HorizontalAlignment="Left"
                       Grid.Row="6" Grid.Column="0" 
                       Text="Contact:" Width="75" />
            <dxe:TextEdit Margin="0,0,0,8"
                          Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="2"
                          TextWrapping="Wrap"
                          EditValue="{Binding Controller.Contact, Mode=TwoWay}"/>
    
            <!--Buttons-->
            <StackPanel Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="2" 
                        Orientation="Horizontal" HorizontalAlignment="Right">
                <Button Margin="6,0,0,0" MinWidth="75" 
                        Content="OK" Click="Ok_button_Click" />
                <Button Margin="6,0,0,0" MinWidth="75" 
                        Content="Cancel" Click="Cancel_button_Click" />
            </StackPanel>
        </Grid>
    </UserControl>
    

Create a Controller Supporting Custom Fields

Since the application’s scheduling data contains a custom field (Contact) that should be edited and saved using the custom Edit Appointment form, it is necessary to inherit from the AppointmentFormController class, and create properties to support a mapped custom field.

Open the CustomAppointmentForm.xaml.cs (CustomAppointmentForm.xaml.vb) code-behind file and add the CustomAppointmentFormController class as follows.

using DevExpress.Xpf.Scheduler;
using DevExpress.Xpf.Scheduler.UI;
using DevExpress.XtraScheduler;
    public class CustomAppointmentFormController : AppointmentFormController {
        public CustomAppointmentFormController(SchedulerControl control, Appointment apt)
            : base(control, apt) {
        }

        public string Contact {
            get { return GetContactValue(EditedAppointmentCopy); }
            set { EditedAppointmentCopy.CustomFields["Contact"] = value; }
        }

        string SourceContact {
            get { return GetContactValue(SourceAppointment); }
            set { SourceAppointment.CustomFields["Contact"] = value; }
        }

        public override bool IsAppointmentChanged() {
            if(base.IsAppointmentChanged())
                return true;
            return SourceContact != Contact;
        }

        protected string GetContactValue(Appointment apt) {
            return Convert.ToString(apt.CustomFields["Contact"]);
        }
    }

Assign a Controller to a Custom Edit Appointment Form

To implement methods for initializing form controls, validating input, and saving the information, create a new class (a descendant of the AppointmentFormController class). To do this, add the following code to the implementation.

using DevExpress.Xpf.Scheduler;
using DevExpress.Xpf.Scheduler.UI;
using DevExpress.XtraScheduler;
    public partial class CustomAppointmentForm : UserControl {

        public CustomAppointmentFormController Controller { get; private set; }
        public SchedulerControl Control { get; private set; }
        public Appointment Appointment { get; private set; }
        public string TimeEditMask { get { return CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern; } }


        public CustomAppointmentForm(SchedulerControl control, Appointment apt) {
            InitializeComponent();
            if(control == null || apt == null)
                throw new ArgumentNullException("control");
            if(control == null || apt == null)
                throw new ArgumentNullException("apt");

            this.Control = control;
            this.Controller = new CustomAppointmentFormController(control, apt);
            this.Appointment = apt;
        }

        private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e) {
            if(Controller.IsNewAppointment)
                SchedulerFormBehavior.SetTitle(this, "New appointment");
            else
                SchedulerFormBehavior.SetTitle(this, "Edit - [" + Appointment.Subject + "]");
        }

        private void Ok_button_Click(object sender, System.Windows.RoutedEventArgs e) {
            // Save all changes made to the appointment.            
            Controller.Control.Storage.BeginUpdate();
            Controller.ApplyChanges();
            Controller.Control.Storage.EndUpdate();
            SchedulerFormBehavior.Close(this, true);
        }

        private void Cancel_button_Click(object sender, System.Windows.RoutedEventArgs e) {
            SchedulerFormBehavior.Close(this, false);
        }
    }

Replace a Standard Form with a Custom Form

  1. Handle the SchedulerControl.EditAppointmentFormShowing event to specify your custom form as the destination container and replace the default appointment editing form. To do this, select the Events tab in the Properties window, and set the EditAppointmentFormShowing event handler name to Scheduler_EditAppointmentShowing.
  2. Implement the Scheduler_EditAppointmentShowing function. To do this, add the following code to the Custom Appointment Form implementation.

    private void Scheduler_EditAppointmentFormShowing(object sender, EditAppointmentFormEventArgs e) {
        e.Form = new CustomAppointmentForm(Scheduler, e.Appointment);
    }
    

Disable the Appointment Recurrence Feature

The newly created custom Edit Appointment form does not allow end users to invoke the Appointment Recurrence form and define the recurrence rule. Thus, to disable the Appointment Recurrence feature and prevent end users from working with recurring appointments, do not specify mappings for the AppointmentMapping.RecurrenceInfo and AppointmentMapping.Type standard properties of an appointment (the AppointmentMapping.RecurrenceInfo and AppointmentMapping.Type properties). Since you specified these properties, you can now clear their values.

Scheduler_Lesson3_Image5

Result

Run the project. The following image shows the Custom Appointment Form, which will be invoked when you create an appointment.

Scheduler_Lesson9_Image6

See Also