Skip to main content
A newer version of this page is available. .

How To: Implement a Custom Inplace Editor for Appointments

  • 11 minutes to read

The following example demonstrates how to customize an inplace editor. An inplace editor (sometimes called an inline editor, since it is intended for inline editing) is activated when an end-user adds a new appointment by pressing the Enter key in selected cells, or edits the appointment’s Appointment.Subject by pressing the F2 key when the appointment is selected, or by single-clicking it (a double click opens the Appointment Editing Form).

An inplace editor in the Scheduler is different from an inplace editor in the Data Grid because the inplace editor in the Scheduler is a separate form that can be shown in any location. Inplace editors for the Scheduler do not use repository item collections. To display an inplace editor to the user, handle the SchedulerControl.InplaceEditorShowing event, and specify the editor using the InplaceEditorEventArgs.InplaceEditor property or the InplaceEditorEventArgs.InplaceEditorEx property. The inplace editor must implement the ISchedulerInplaceEditor or the ISchedulerInplaceEditorEx interface.

Note

You can set restrictions on inplace editing by setting the SchedulerOptionsCustomization.AllowInplaceEditor property value. For example, set the property to UsedAppointmentType.None to disable inline editing, or set it to UsedAppointmentType.Custom to raise the SchedulerControl.AllowInplaceEditor event, which enables you to decide whether or not to invoke the editor.

To implement a custom inplace editor and use it instead of the default inplace editor, follow the instructions below.

Create an Inplace Editor Form Layout

The following steps assume that you create the project in C#. However, if you choose a Visual Basic project, the actions will be the same.

  1. In the Visual Studio Designer, press CTRL+SHIFT+A to invoke the Add New Item dialog. In this dialog, select DevExpress v18.1 Template Gallery and click Add.

    You can also invoke this dialog by selecting the DevExpress -> All Platforms -> New Item… menu in the Visual Studio esigner.

    InplaceEditor - AddNewItem

  2. In the DevExpress Template Gallery window, select Form and specify MyInplaceEditor as the name of the new form. Click Add Item.

    Inplace Editor - XtraForm

  3. Locate the LayoutControl component in the Toolbox and drop it onto the form. In the Properties grid, set the LayoutControl’s Dock property to Fill, so that the control occupies the entire form. For more information on the Layout component, refer to the Tutorial: Creating a Simple Data Layout document.

    InplaceEditor - DropLayoutControl

  4. Add a control that will be used to edit the Appointment.Subject property. To accomplish this, locate the TextEdit control in the Toolbox and drop it onto the LayoutControl.

    InplaceEditor - DropTextEdit

  5. Change the text of the newly created layoutControlItem1 to Subject:. Change the name of the textEdit1 control to edtSubject.

    InplaceEditor - edtSubject

  6. Add a control that will be used to edit the Appointment.LabelId property. Locate the AppointmentLabelEdit control in the Toolbox and drop it onto the LayoutControl. Change the text of the newly created layoutControlItem2 to Label:.

    InplaceEditor - AddAppointmentLabelEdit

  7. Add a control that will be used to edit the Appointment.Description property. Locate the MemoEdit control in the Toolbox and drop it onto the LayoutControl. Change the text of the newly created layoutControlItem3 to Description:. Change the name of the memoEdit1 control to edtDescription.

    InplaceEditor - Add_edtDescription

  8. To improve the form layout, do the following.

    • Hide the Control/System menu box by setting the ControlBox property to false.
    • Set the Text property to an empty string to hide the form’s caption.
    • Specify the minimum size of the form using the MinimumSize property. Set the minimum width to 132, and set the minimum height to 192.
    • Set the StartPosition property to Manual.
    • Set the BaseLayoutItem.TextLocation for each LayoutControlItem to Top.

    The result is shown below.

    InplaceEditor - ResultingForm

Add the Code to Implement the Edit Functionality

Open the MyInplaceEditor.cs code file and replace the content of the public partial class MyInplaceEditor with the following code. Add using (Imports for Visual Basic) namespace directive for the DevExpress.XtraScheduler namespace.

public partial class MyInplaceEditor : DevExpress.XtraEditors.XtraForm {
    Appointment appointment;
    SchedulerControl control;

    public MyInplaceEditor(SchedulerInplaceEditorEventArgs inplaceEditorArgs) {
        InitializeComponent();
        this.Text = string.Empty;
        this.ControlBox = false;
        this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
        this.FormBorderEffect = DevExpress.XtraEditors.FormBorderEffect.None;
        SubscribeKeyDownEvents();
        this.control = inplaceEditorArgs.Control;
        if (inplaceEditorArgs.UseFullCellEditor) {
            this.Bounds = control.RectangleToScreen(inplaceEditorArgs.Bounds);
            DevExpress.Skins.Skin currentSkin = DevExpress.Skins.SchedulerSkins.GetSkin(control.LookAndFeel);
            DevExpress.Skins.SkinElement element = currentSkin[DevExpress.Skins.SchedulerSkins.SkinAllDayAreaSelected];
            this.BackColor = element.Color.BackColor;
            this.ForeColor = Color.White;
        }
        else {
            this.Bounds = AdjustEditorBounds(inplaceEditorArgs.Bounds);
            this.BackColor = inplaceEditorArgs.BackColor;
        }            
    }

    public event EventHandler CommitChanges;
    public event EventHandler RollbackChanges;

    private void SubscribeKeyDownEvents() {
        appointmentLabelEdit1.KeyDown += new KeyEventHandler(AppointmentLabelEdit_KeyDown);
        edtSubject.KeyDown += new KeyEventHandler(Editor_KeyDown);
        edtDescription.KeyDown += new KeyEventHandler(Editor_KeyDown);
    }

    // Create a KeyDown event handler.
    // If the Enter key is pressed, save changes. If the ESC key is pressed, cancel changes.
    void Editor_KeyDown(object sender, KeyEventArgs e) {
        switch (e.KeyCode) {
            case Keys.Enter:
                e.Handled = true;
                OnCommitChanges();
                break;
            case Keys.Escape:
                e.Handled = true;
                OnRollbackChanges();
                break;
        }
    }

    public void AppointmentLabelEdit_KeyDown(object sender, KeyEventArgs e) {
        if (!appointmentLabelEdit1.IsPopupOpen)
            Editor_KeyDown(sender, e);
    }

    void OnCommitChanges() {
        if (CommitChanges != null)
            CommitChanges(this, EventArgs.Empty);
    }

    void OnRollbackChanges() {
        if (RollbackChanges != null)
            RollbackChanges(this, EventArgs.Empty);
    }
    protected override void OnShown(EventArgs e) {
        // Correct the text editor selection, which may result in overwriting the first typed character.
        SchedulerStorage storage = control.Storage;
        if (storage.Appointments.IsNewAppointment(appointment)) {
            edtSubject.SelectionLength = 0;
            edtSubject.SelectionStart = edtSubject.Text.Length;
        }
        base.OnShown(e);
    }

    // Fill the controls with appointment data. 
    public void FillForm(SchedulerControl control, Appointment appointment) {
        this.appointment = appointment;
        SchedulerStorage storage = control.Storage;
        this.appointmentLabelEdit1.Storage = control.Storage; 
        this.appointmentLabelEdit1.AppointmentLabel = storage.Appointments.Labels.GetById(appointment.LabelKey);
        this.edtSubject.Text = appointment.Subject;
        this.edtDescription.Text = appointment.Description;
    }
    // Save changes to the appointment.
    public void ApplyChanges() {
        appointment.Subject = edtSubject.Text;
        appointment.Description = edtDescription.Text;
        appointment.LabelKey = control.Storage.Appointments.Labels.IndexOf(appointmentLabelEdit1.AppointmentLabel);
    }


    // Set the size of inplace editor 
    private Rectangle AdjustEditorBounds(Rectangle editorBounds) {
        Rectangle screenControlBounds = control.Parent.RectangleToScreen(control.Bounds);
        editorBounds.Offset(0, -3);
        Rectangle screenEditorBounds = control.RectangleToScreen(editorBounds);

        Size preferredSize = GetPreferredSize(editorBounds.Size);
        int height = Math.Max(preferredSize.Height, editorBounds.Height);
        int width = preferredSize.Width;

        Rectangle rect = screenEditorBounds;
        rect.Offset(6, 0);

        int maxBottom = Math.Min(screenControlBounds.Bottom, rect.Top + height);
        int top = maxBottom - height;

        Rectangle result = new Rectangle(rect.Left, top, width, height);
        if (screenControlBounds.Right < rect.Right) {
            int horzOffset = control.ActiveView is DayView ? 12 : 8;
            result = new Rectangle(screenEditorBounds.Left - width - horzOffset, top, width, height);
        }
        return result;
    }
}

Implement an Inplace Editor Control

The Scheduler requires an object that implements the ISchedulerInplaceEditorEx interface to use it as an inplace editor. Thus, you need to create a container object for your custom form. This container should implement the ISchedulerInplaceEditorEx interface (the container is also responsible for instantiating and positioning the MyInplaceEditor form).

To create a container class, add a new class file named MyInplaceEditorControl to the project. Copy and paste the following code to replace the default class content.

public class MyInplaceEditorControl : ISchedulerInplaceEditorEx {
    MyInplaceEditor editor;
    Appointment appointment;
    SchedulerControl control;

    public MyInplaceEditorControl(SchedulerInplaceEditorEventArgs inplaceEditorArgs) {
        this.appointment = inplaceEditorArgs.ViewInfo.Appointment;
        this.control = inplaceEditorArgs.Control;
        CreateEditor(inplaceEditorArgs);
    }

    MyInplaceEditor Editor { get { return editor; } }
    Appointment Appointment { get { return appointment; } }
    SchedulerControl Control { get { return control; } }

    public event EventHandler CommitChanges;
    public event EventHandler RollbackChanges;

    void CreateEditor(SchedulerInplaceEditorEventArgs inplaceEditorArgs) {
        this.editor = new MyInplaceEditor(inplaceEditorArgs);
    }

    // Interface implementation 
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    ~MyInplaceEditorControl() {
        Dispose(false);
    }
    protected virtual void Dispose(bool disposing) {
        if (disposing) {
            if (Editor != null) {
                Editor.Dispose();
                this.editor = null;
            }
            this.appointment = null;
        }
    }
    public virtual void Activate() {
        Editor.FillForm(control, appointment);
        SubscribeEditorEvents();
        Editor.Show(Control.FindForm());
    }
    public virtual void Deactivate() {
        UnsibscribeEditorEvents();
        Editor.Close();
    }
    public virtual void ApplyChanges() {
        Editor.ApplyChanges();
    }
    protected internal virtual void SubscribeEditorEvents() {
        Editor.FormClosed += new FormClosedEventHandler(Editor_FormClosed);
        Editor.Deactivate += new EventHandler(Editor_Deactivate);
        Editor.CommitChanges += new EventHandler(Editor_CommitChanges);
        Editor.RollbackChanges += new EventHandler(Editor_RollbackChanges);
    }

    protected internal virtual void UnsibscribeEditorEvents() {
        Editor.FormClosed -= new FormClosedEventHandler(Editor_FormClosed);
        Editor.Deactivate -= new EventHandler(Editor_Deactivate);
        Editor.CommitChanges -= new EventHandler(Editor_CommitChanges);
        Editor.RollbackChanges -= new EventHandler(Editor_RollbackChanges);
    }
    void Editor_FormClosed(object sender, FormClosedEventArgs e) {
        OnCommitChanges();
    }
    void Editor_Deactivate(object sender, EventArgs e) {
        OnCommitChanges();
    }
    void Editor_RollbackChanges(object sender, EventArgs e) {
        OnRollbackChanges();
    }
    void Editor_CommitChanges(object sender, EventArgs e) {
        OnCommitChanges();
    }
    protected internal virtual void TextBox_LostFocus(object sender, EventArgs e) {
        Editor.Close();
        OnCommitChanges();
    }
    protected internal virtual void OnRollbackChanges() {
        if (RollbackChanges != null)
            RollbackChanges(this, EventArgs.Empty);
    }
    protected internal virtual void OnCommitChanges() {
        RaiseCommitChanges();
    }
    protected internal virtual void RaiseCommitChanges() {
        if (CommitChanges != null)
            CommitChanges(this, EventArgs.Empty);
    }
}

Add the Code to Display a Custom Inplace Editor

To display a custom form instead of the default inplace editor, subscribe to the SchedulerControl.InplaceEditorShowing event and handle it as follows.

private void SchedulerControl1_InplaceEditorShowing(object sender, InplaceEditorEventArgs e) {
    e.InplaceEditorEx = new MyInplaceEditorControl(e.SchedulerInplaceEditorEventArgs);
}

Run the Project

Run the project, and type in the Scheduler view to create a new appointment or click an existing appointment. The inplace editor appears as illustrated in the picture below.

Inplace Editor - Result