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
- Create a Custom Edit Appointment Form
- Create a Controller Supporting Custom Fields
- Assign a Controller to a Custom Edit Appointment Form
- Replace a Standard Form with a Custom Form
- Disable the Appointment Recurrence Feature
- Result
Specify Custom Field Mappings
- 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.
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 button to invoke the Collection Editor window.
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.
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
In the Visual Studio menu, click Project | Add User Control…
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.
Add controls to edit the following appointment properties.
- Subject - the TextEdit control
- Start, End - DateEdit controls for data values and ButtonEdit controls for time values
- AllDay - the CheckEdit control
- Label - the AppointmentLabelEdit control
- Status - the AppointmentStatusEdit control
- My Custom Filed (custom field) - the TextEdit control
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
- 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.
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.
Result
Run the project. The following image shows the Custom Appointment Form, which will be invoked when you create an appointment.