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.

Imports DevExpress.Xpf.Scheduler
Imports DevExpress.Xpf.Scheduler.UI
Imports DevExpress.XtraScheduler
    Public Class CustomAppointmentFormController
        Inherits AppointmentFormController

        Public Sub New(ByVal control As SchedulerControl, ByVal apt As Appointment)
            MyBase.New(control, apt)
        End Sub

        Public Property Contact() As String
            Get
                Return GetContactValue(EditedAppointmentCopy)
            End Get
            Set(ByVal value As String)
                EditedAppointmentCopy.CustomFields("Contact") = value
            End Set
        End Property

        Private Property SourceContact() As String
            Get
                Return GetContactValue(SourceAppointment)
            End Get
            Set(ByVal value As String)
                SourceAppointment.CustomFields("Contact") = value
            End Set
        End Property

        Public Overrides Function IsAppointmentChanged() As Boolean
            If MyBase.IsAppointmentChanged() Then
                Return True
            End If
            Return SourceContact <> Contact
        End Function

        Protected Function GetContactValue(ByVal apt As Appointment) As String
            Return Convert.ToString(apt.CustomFields("Contact"))
        End Function
    End Class

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.

Imports DevExpress.Xpf.Scheduler
Imports DevExpress.Xpf.Scheduler.UI
Imports DevExpress.XtraScheduler
    Partial Public Class CustomAppointmentForm
        Inherits UserControl

        Private privateController As CustomAppointmentFormController
        Public Property Controller() As CustomAppointmentFormController
            Get
                Return privateController
            End Get
            Private Set(ByVal value As CustomAppointmentFormController)
                privateController = value
            End Set
        End Property
        Private privateControl As SchedulerControl
        Public Property Control() As SchedulerControl
            Get
                Return privateControl
            End Get
            Private Set(ByVal value As SchedulerControl)
                privateControl = value
            End Set
        End Property
        Private privateAppointment As Appointment
        Public Property Appointment() As Appointment
            Get
                Return privateAppointment
            End Get
            Private Set(ByVal value As Appointment)
                privateAppointment = value
            End Set
        End Property
        Public ReadOnly Property TimeEditMask() As String
            Get
                Return CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern
            End Get
        End Property


        Public Sub New(ByVal control As SchedulerControl, ByVal apt As Appointment)
            InitializeComponent()
            If control Is Nothing OrElse apt Is Nothing Then
                Throw New ArgumentNullException("control")
            End If
            If control Is Nothing OrElse apt Is Nothing Then
                Throw New ArgumentNullException("apt")
            End If

            Me.Control = control
            Me.Controller = New CustomAppointmentFormController(control, apt)
            Me.Appointment = apt
        End Sub

        Private Sub UserControl_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
            If Controller.IsNewAppointment Then
                SchedulerFormBehavior.SetTitle(Me, "New appointment")
            Else
                SchedulerFormBehavior.SetTitle(Me, "Edit - [" & Appointment.Subject & "]")
            End If
        End Sub

        Private Sub Ok_button_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
            ' Save all changes made to the appointment.            
            Controller.Control.Storage.BeginUpdate()
            Controller.ApplyChanges()
            Controller.Control.Storage.EndUpdate()
            SchedulerFormBehavior.Close(Me, True)
        End Sub

        Private Sub Cancel_button_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
            SchedulerFormBehavior.Close(Me, False)
        End Sub
    End Class

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 Sub Scheduler_EditAppointmentFormShowing(ByVal sender As Object, ByVal e As EditAppointmentFormEventArgs)
        e.Form = New CustomAppointmentForm(Scheduler, e.Appointment)
    End Sub
    

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