Skip to main content

How to: Customize Editing and Recurrence Dialogs

  • 4 minutes to read

This example demonstrates how to replace built-in appointment and recurrence editors with custom windows, which provide additional functionality to meet user requirements.

Create a New Application

  1. Create a new WPF Application project and open the MainWindow.xaml file in the Visual Studio Designer.
  2. Add the SchedulerControl object to your project. You can do this by dragging the SchedulerControl item from the DX.24.2: Scheduling Toolbox tab to the canvas.

    WPFScheduler_DragDropFromToolbox

  3. Right-click the SchedulerControl object and select Layout | Reset All in the context menu to stretch the SchedulerControl so that it fills the entire window.

Create a Custom Appointment Editing Window

When the appointment window is invoked, its DataContext is set to the AppointmentWindowViewModel class instance. Create a custom window whose bindings use the properties and commands of the AppointmentWindowViewModel class.

View Example

<!--region #HospitalAppointmentWindow-->
<dx:ThemedWindow
    x:Class="CustomMvvmFormWithRecurrenceExample.HospitalAppointmentWindow"
    x:Name="AppointmentWindow"
    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/scheduling"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
    xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol"
    xmlns:dxscht="http://schemas.devexpress.com/winfx/2008/xaml/scheduling/themekeys"
    MinWidth="400"
    MinHeight="350"
    mc:Ignorable="d"
    SizeToContent="WidthAndHeight" Title="{Binding Title}">

    <dx:ThemedWindow.Resources>
        <dxsch:TimeSpanToDateTimeConverter x:Key="timeSpanToDateTimeConverter" />
    </dx:ThemedWindow.Resources>

    <dxmvvm:Interaction.Behaviors>
        <dxmvvm:CurrentWindowService ClosingCommand="{Binding WindowClosingCommand}" />
    </dxmvvm:Interaction.Behaviors>

    <DockPanel>
        <dxlc:LayoutControl
            x:Name="validationContainer"
            dxe:ValidationService.IsValidationContainer="True"
            Orientation="Vertical">
            <dxlc:LayoutGroup Orientation="Horizontal">
                <dxlc:LayoutGroup HorizontalAlignment="Stretch" Orientation="Vertical">
                    <!--Subject-->
                    <dxlc:LayoutItem Label="Patient name:">
                        <dxe:TextEdit x:Name="subjectEdit" EditValue="{Binding Subject, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
                    </dxlc:LayoutItem>
                    <!--Location-->
                    <dxlc:LayoutItem Label="Office:">
                        <dxe:TextEdit EditValue="{Binding Location, Mode=TwoWay}" />
                    </dxlc:LayoutItem>
                    <!--Start-->
                    <dxlc:LayoutItem Margin="0,10,0,0" Style="{DynamicResource {dxscht:AppointmentWindowThemeKey ResourceKey=LayoutItem_StartTime}}">
                        <DockPanel>
                            <dxe:DateEdit
                                x:Name="editorStartDate"
                                Width="150"
                                DockPanel.Dock="Left"
                                Style="{DynamicResource {dxscht:AppointmentWindowThemeKey ResourceKey=Editor_StartDate}}" />
                            <dxe:TextEdit
                                x:Name="editorStartTime"
                                Margin="4,0,0,0"
                                DockPanel.Dock="Left"
                                Style="{DynamicResource {dxscht:AppointmentWindowThemeKey ResourceKey=Editor_StartTime}}" />
                        </DockPanel>
                    </dxlc:LayoutItem>
                    <!--End-->
                    <dxlc:LayoutItem Style="{DynamicResource {dxscht:AppointmentWindowThemeKey ResourceKey=LayoutItem_EndTime}}">
                        <DockPanel>
                            <dxe:DateEdit
                                x:Name="editorEndDate"
                                Width="150"
                                DockPanel.Dock="Left"
                                Style="{DynamicResource {dxscht:AppointmentWindowThemeKey ResourceKey=Editor_EndDate}}" />
                            <dxe:TextEdit
                                x:Name="editorEndTime"
                                Margin="4,0,0,0"
                                DockPanel.Dock="Left"
                                Style="{DynamicResource {dxscht:AppointmentWindowThemeKey ResourceKey=Editor_EndTime}}" />
                        </DockPanel>
                    </dxlc:LayoutItem>
                    <!--Insurance number (custom field)-->
                    <dxlc:LayoutItem Label="Insurance number:">
                        <dxe:TextEdit EditValue="{Binding CustomFields.InsuranceNumber, Mode=TwoWay}" />
                    </dxlc:LayoutItem>
                    <!--First visit (custom field)-->
                    <dxlc:LayoutItem Label="First visit:">
                        <dxe:CheckEdit EditValue="{Binding CustomFields.FirstVisit, Mode=TwoWay}" />
                    </dxlc:LayoutItem>
                    <!--Resource's ID-->
                    <dxlc:LayoutItem Label="Doctor:">
                        <dxsch:AppointmentResourceEdit
                            EditValue="{Binding Resource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                            Scheduler="{Binding Path='(dxsch:SchedulerControl.Scheduler)', RelativeSource={RelativeSource Self}}"
                            ShowEmptyResource="False">
                            <dxmvvm:Interaction.Behaviors>
                                <dxmvvm:FocusBehavior />
                            </dxmvvm:Interaction.Behaviors>
                        </dxsch:AppointmentResourceEdit>
                    </dxlc:LayoutItem>
                    <!--Notes (custom field)-->
                    <dxlc:LayoutItem Label="Notes:">
                        <dxe:TextEdit
                            MinHeight="50"
                            HorizontalContentAlignment="Left"
                            VerticalContentAlignment="Top"
                            AcceptsReturn="True"
                            EditValue ="{Binding CustomFields.Notes, Mode=TwoWay}"
                            TextWrapping="Wrap"
                            VerticalScrollBarVisibility="Visible" />
                    </dxlc:LayoutItem>
                </dxlc:LayoutGroup>
            </dxlc:LayoutGroup>
            <!--Buttons-->
            <StackPanel
                Height="20"
                Margin="0,6,0,0"
                HorizontalAlignment="Right"
                Orientation="Horizontal">
                <Button
                    x:Name="btnOk"
                    MinWidth="75"
                    Margin="6,0,0,0"
                    Command="{Binding SaveAndCloseAppointmentCommand}"
                    Content="OK" />
                <Button
                    x:Name="btnRecurrence"
                    MinWidth="75"
                    Margin="6,0,0,0"
                    Command="{Binding EditRecurrenceCommand}"
                    Content="Recurrence" />
            </StackPanel>
        </dxlc:LayoutControl>
    </DockPanel>

</dx:ThemedWindow><!--endregion #HospitalAppointmentWindow-->

Create a Custom Recurrence Window

Create a custom window whose bindings use the properties and commands of the RecurrenceWindowViewModel class.

View Example

 <!--region #HospitalRecurrenceWindow-->
    <dx:ThemedWindow
    x:Class="CustomMvvmFormWithRecurrenceExample.HospitalRecurrenceWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
    xmlns:dxsch="http://schemas.devexpress.com/winfx/2008/xaml/scheduling"
    xmlns:dxec="http://schemas.devexpress.com/winfx/2008/xaml/editors/"
    xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
    Title="Recurrence Window"  Width="368.061" Height="380" MinWidth="450" MinHeight="380" ResizeMode="NoResize">

    <dx:ThemedWindow.Resources>
        <Style x:Key="recurrenceInfoControlStyle" TargetType="dxsch:RecurrenceInfoControlBase">
            <Setter Property="RecurrenceInfo" Value="{Binding RecurrenceInfo}"/>
            <Style.Triggers>
                <Trigger Property="Visibility" Value="Collapsed">
                    <Setter Property="RecurrenceInfo" Value="{x:Null}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </dx:ThemedWindow.Resources>

    <Grid Margin="12">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <!--Recurrence Pattern Group-->
        <GroupBox Header="Recurrence Pattern" Margin="0,12,0,0"  Grid.ColumnSpan="3">
            <DockPanel>
                <dxsch:RecurrenceTypeControl DockPanel.Dock="Left" RecurrenceTypes="{Binding AvailableRecurrenceTypes}" RecurrenceType="{Binding RecurrenceInfo.Type, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                <StackPanel Orientation="Horizontal">
                    <Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
                </StackPanel>
                <StackPanel MinHeight="90">
                    <dxsch:DailyRecurrenceControl Style="{StaticResource recurrenceInfoControlStyle}" Visibility="{Binding RecurrenceInfo.Type, Converter={dxsch:RecurrenceTypeToVisibilityConverter VisibleValue=Daily}}"/>
                    <dxsch:WeeklyRecurrenceControl Style="{StaticResource recurrenceInfoControlStyle}" Visibility="{Binding RecurrenceInfo.Type, Converter={dxsch:RecurrenceTypeToVisibilityConverter VisibleValue=Weekly}}"
                                                    FirstDayOfWeek="{Binding FirstDayOfWeek}"/>
                    <dxsch:MonthlyRecurrenceControl Style="{StaticResource recurrenceInfoControlStyle}" Visibility="{Binding RecurrenceInfo.Type, Converter={dxsch:RecurrenceTypeToVisibilityConverter VisibleValue=Monthly}}"/>
                    <dxsch:YearlyRecurrenceControl x:Name="yearlyRecurrence" Style="{StaticResource recurrenceInfoControlStyle}" Visibility="{Binding RecurrenceInfo.Type, Converter={dxsch:RecurrenceTypeToVisibilityConverter VisibleValue=Yearly}}"/>
                </StackPanel>
            </DockPanel>
        </GroupBox>
        <!--Recurrence End Group-->
        <GroupBox Grid.Row="1" Grid.ColumnSpan="3" Header="Recurrence End" Margin="0,12,0,0">
            <dxsch:RecurrenceRangeControl RecurrenceInfo="{Binding RecurrenceInfo}"/>
        </GroupBox>
    </Grid>
    <dx:ThemedWindow.DialogButtons>
        <dx:ThemedWindowDialogButton Content="{dxsch:SchedulerLocalizer StringId=ButtonCaption_OK}" Command="{Binding SaveCommand}" IsDefault="True" MinWidth="65" DialogResult="OK"/>
        <dx:ThemedWindowDialogButton Content="{dxsch:SchedulerLocalizer StringId=ButtonCaption_Cancel}" IsCancel="True" MinWidth="65" DialogResult="Cancel"/>
        <dx:ThemedWindowDialogButton Content="{dxsch:SchedulerLocalizer StringId=ButtonCaption_Delete}" Command="{Binding DeleteCommand}" MinWidth="65" DialogResult="Cancel"/>
    </dx:ThemedWindow.DialogButtons>
</dx:ThemedWindow>
<!--endregion #HospitalRecurrenceWindow-->

Replace Window Types

Set the OptionsWindows.AppointmentWindowType value to the type of a custom appointment editing window (HospitalAppointmentWindow). Set the OptionsWindows.RecurrenceWindowType value to the type of a custom recurrence window (HospitalRecurrenceWindow).

View Example

<dxsch:SchedulerControl.OptionsWindows>
    <dxsch:OptionsWindows AppointmentWindowType="{x:Type local:HospitalAppointmentWindow}" RecurrenceWindowType="{x:Type local:HospitalRecurrenceWindow}" />
</dxsch:SchedulerControl.OptionsWindows>

Tip

You can also handle the SchedulerControl.AppointmentWindowShowing event to display a custom window.

Create the AppointmentWindowViewModel Descendant

You can modify the window’s View Model behavior by creating the AppointmentWindowViewModel class descendant as illustrated in the code snippet below:

public class AppointmentWindowViewModelEx : AppointmentWindowViewModel {
    public AppointmentWindowViewModelEx(AppointmentItem appointmentItem, SchedulerControl scheduler) : base(appointmentItem, scheduler) { }
    //...
}

To use the custom AppointmentWindowViewModelEx type in the appointment window, handle the AppointmentWindowShowing event and set the e.Window.DataContext property as follows:

private void Scheduler_AppointmentWindowShowing(object sender, DevExpress.Xpf.Scheduling.AppointmentWindowShowingEventArgs e) {
    e.Window.DataContext = new AppointmentWindowViewModelEx(e.Appointment, (SchedulerControl)sender);
}

You can use this approach if you specify the window type by setting the AppointmentWindowType property.

Run the Application and Observe the Result

DXScheduler_Examples_CustomForm