
What is hidden behind the editing form of a complex object?
In this article, we continue to introduce you to the approaches implemented in the XtraScheduler scheduler. In a previous article, we talked about a data synchronizer, this time let's talk about forms.

Quite often in applications you can find forms that are designed to enter or edit objects with a large number of dependent properties. The construction of such input forms causes a “headache” for developers: the routine work of posting editors, writing initialization code, validation, event handlers ...
So how to make such forms quickly and reliably?
As an example of an edited object, take Appointment , an object representing a certain event of the scheduler. The structure of this class is shown in the diagram below: The object we selected has a fairly large set of properties. At the same time, depending on the values already set, only a certain part of the properties should be editable. In addition, the object implements a business logic for setting values - changing one property can lead to setting / zeroing another, depending on the first. Therefore, when developing a form interface, it is necessary to take into account all these factors and make the interface elements react in a certain way to a change in the state of the edited object.

To edit this object, we used the following approach: all editors on the form will edit or display not the properties of the object itself, but the properties of a certain object associated with the edited one.
You have probably already guessed that we will talk about the controller and the use of the Adapter pattern , since the form interface may differ from the interface of the object itself.
Create a controller by passing it to the constructor as a parameter the source editable object, as well as other objects necessary for editing.
In our case, changing the original object during the editing process was extremely undesirable and when you clicked on the Cancel button in the form, you would need to restore the original values of all its properties. Therefore, along with the source object, the controller also contains a copy of this object created in the constructor:
What gives a copy? While editing is performed in the form, the properties of the copy object are changed, and only when changes are applied, the properties from the copy are “rolled” onto the original object. This is done by a special controller method:
In the case of closing the form without changing the need for any action disappears, because The original object has remained untouched.
Note that with a different architecture, the scheme for applying / rolling back changes may differ from that described here and there may be no need for a copy.
Let's move on to the properties. In the controller, we implement the Facade pattern and duplicate the necessary properties from the copy object to the controller properties:
If the interfaces of input editors and object properties are incompatible, we make the necessary transformations directly in the controller properties:
If necessary, for example, to separately display and edit the date and time, we split the property of the object into two properties of the controller:
We supplement the controller with necessary service properties that are not in the edited object, but may be needed when designing the form interface:
In addition, we create in the controller a set of methods that implement all the necessary logic, including validation methods, obtaining additional objects, etc., that will request form objects.
Validation is performed in the event handlers of the form controls and the correct values are written to the controller properties. When you change the dependent properties in the controller, you need to update the data in the appropriate editors, and it is even possible to hide or make it inaccessible to input.
In WinForms, this is done as follows: we unsubscribe from the events of the editors, re-read the new data from the controller into the editors, and again subscribe to the events.
Validation of values on the web can be carried out not in form, but in a callback command, which gets access to the values of editors. After analyzing the entered values, it may be decided to close the form or not and issue an informational message.
As a result, our form controller encapsulates all the necessary functionality and, being a simple class, can be reused, whether it be WinForms, web, or WPF / SL.
If necessary, for each platform there may be a successor to the controller that implement platform-specific actions.
We will illustrate examples of using AppointmentFormControllerBase on various platforms with code fragments.
The controller instance is created in the form constructor, the editors are initialized with the controller properties, when the editor values change, the controller properties are modified, there is a code for applying the changes.
On the web, the input form is a UserControl and is loaded as a template. To "bind" the values of editors to the properties of the controller, Data Binding Expressions are used via the syntax of the form "<% #%>". A controller instance is created in the template container class and is accessible from the form via the Container property.
The ascx form file is as follows
Below is the code for the form template and container:
On this platform, the form template is represented by System.Windows.Controls.UserControl. Like WinForms, a controller instance is created in the form constructor. But the “binding” to the values of the editors is carried out in xaml through the mechanism of two-way Binding.
Using a form controller provides the following benefits:
We hope that the material presented in the article will be useful to you.

Quite often in applications you can find forms that are designed to enter or edit objects with a large number of dependent properties. The construction of such input forms causes a “headache” for developers: the routine work of posting editors, writing initialization code, validation, event handlers ...
So how to make such forms quickly and reliably?
Editable Object
As an example of an edited object, take Appointment , an object representing a certain event of the scheduler. The structure of this class is shown in the diagram below: The object we selected has a fairly large set of properties. At the same time, depending on the values already set, only a certain part of the properties should be editable. In addition, the object implements a business logic for setting values - changing one property can lead to setting / zeroing another, depending on the first. Therefore, when developing a form interface, it is necessary to take into account all these factors and make the interface elements react in a certain way to a change in the state of the edited object.

Editing method
To edit this object, we used the following approach: all editors on the form will edit or display not the properties of the object itself, but the properties of a certain object associated with the edited one.
You have probably already guessed that we will talk about the controller and the use of the Adapter pattern , since the form interface may differ from the interface of the object itself.
Create a controller by passing it to the constructor as a parameter the source editable object, as well as other objects necessary for editing.
public class AppointmentFormControllerBase : INotifyPropertyChanged {
InnerSchedulerControl innerControl;
Appointment sourceApt;
public AppointmentFormControllerBase(InnerSchedulerControl innerControl, Appointment apt) {
this.innerControl = innerControl;
this.sourceApt = apt;
//...
}
protected internal Appointment SourceAppointment { get { return sourceApt; } }
//...
}
In our case, changing the original object during the editing process was extremely undesirable and when you clicked on the Cancel button in the form, you would need to restore the original values of all its properties. Therefore, along with the source object, the controller also contains a copy of this object created in the constructor:
public AppointmentFormControllerBase(InnerSchedulerControl innerControl, Appointment apt) {
//...
CreateAppointmentCopies();
}
public Appointment EditedAppointmentCopy { get { return editedAptCopy; } }
protected internal virtual void CreateAppointmentCopies() {
editedAptCopy = sourceApt.Copy();
//...
}
What gives a copy? While editing is performed in the form, the properties of the copy object are changed, and only when changes are applied, the properties from the copy are “rolled” onto the original object. This is done by a special controller method:
public virtual void ApplyChanges() {
// …
sourceApt.BeginUpdate();
try {
ApplyChangesCore();
} finally {
sourceApt.EndUpdate();
}
}
protected internal virtual void ApplyChangesCore() {
AppointmentFormAppointmentCopyHelper helper = new AppointmentFormAppointmentCopyHelper(this);
helper.AssignSimpleProperties(editedAptCopy, sourceApt);
helper.AssignCollectionProperties(editedAptCopy, sourceApt);
}
In the case of closing the form without changing the need for any action disappears, because The original object has remained untouched.
Note that with a different architecture, the scheme for applying / rolling back changes may differ from that described here and there may be no need for a copy.
Controller interface
Let's move on to the properties. In the controller, we implement the Facade pattern and duplicate the necessary properties from the copy object to the controller properties:
public string Subject {
get { return editedAptCopy.Subject; }
set { editedAptCopy.Subject = value;
NotifyPropertyChanged("Subject"); }
}
If the interfaces of input editors and object properties are incompatible, we make the necessary transformations directly in the controller properties:
public DateTime Start {
get { return InnerControl.TimeZoneHelper.ToClientTime(editedAptCopy.Start); }
set { editedAptCopy.Start = InnerControl.TimeZoneHelper.FromClientTime(value); }
}
If necessary, for example, to separately display and edit the date and time, we split the property of the object into two properties of the controller:
public DateTime StartDate {
get { return editedAptCopy.Start.Date; }
set { editedAptCopy.Start = value.Date + editedAptCopy.Start.TimeOfDay;
NotifyPropertyChanged("StartDate"); }
}
public TimeSpan StartTime {
get { return editedAptCopy.Start.TimeOfDay; }
set { editedAptCopy.Start = editedAptCopy.Start.Date + value;
NotifyPropertyChanged("StartTime"); }
}
We supplement the controller with necessary service properties that are not in the edited object, but may be needed when designing the form interface:
public virtual bool IsNewAppointment { get { … } }
public bool CanDeleteAppointment { get { … } }
In addition, we create in the controller a set of methods that implement all the necessary logic, including validation methods, obtaining additional objects, etc., that will request form objects.
Validation is performed in the event handlers of the form controls and the correct values are written to the controller properties. When you change the dependent properties in the controller, you need to update the data in the appropriate editors, and it is even possible to hide or make it inaccessible to input.
In WinForms, this is done as follows: we unsubscribe from the events of the editors, re-read the new data from the controller into the editors, and again subscribe to the events.
protected internal virtual void edtStartDate_Validated(object sender, EventArgs e) {
controller.DisplayStart = edtStartDate.DateTime.Date + edtStartTime.Time.TimeOfDay;
UpdateIntervalControls();
}
protected internal virtual void UpdateIntervalControls() {
UnsubscribeControlsEvents();
try {
UpdateIntervalControlsCore();
}
finally {
SubscribeControlsEvents();
}
}
protected virtual void UpdateIntervalControlsCore() {
edtEndDate.EditValue = controller.DisplayEnd.Date;
edtEndTime.EditValue = new DateTime(controller.DisplayEnd.TimeOfDay.Ticks);
//...
bool enableTime = !controller.AllDay;
edtEndTime.Visible = enableTime;
edtEndTime.Enabled = enableTime;
}
Validation of values on the web can be carried out not in form, but in a callback command, which gets access to the values of editors. After analyzing the entered values, it may be decided to close the form or not and issue an informational message.
As a result, our form controller encapsulates all the necessary functionality and, being a simple class, can be reused, whether it be WinForms, web, or WPF / SL.
If necessary, for each platform there may be a successor to the controller that implement platform-specific actions.
Using a form controller
We will illustrate examples of using AppointmentFormControllerBase on various platforms with code fragments.
1. Windows Forms
The controller instance is created in the form constructor, the editors are initialized with the controller properties, when the editor values change, the controller properties are modified, there is a code for applying the changes.
public partial class AppointmentForm : DevExpress.XtraEditors.XtraForm {
readonly AppointmentFormController controller;
public AppointmentForm(SchedulerControl control, Appointment apt, bool openRecurrenceForm) {
// …
this.controller = CreateController(control, apt);
UpdateForm();
}
protected internal AppointmentFormController Controller { get { return controller; } }
protected virtual AppointmentFormController CreateController(SchedulerControl control, Appointment apt) {
return new AppointmentFormController(control, apt);
}
protected virtual void UpdateForm () {
tbSubject.Text = controller.Subject;
edtShowTimeAs.Status = controller.GetStatus();
bool resourceSharing = controller.ResourceSharing;
edtResource.Visible = !resourceSharing;
bool canEditResource = controller.CanEditResource;
edtResource.Enabled = canEditResource;
//…
}
protected internal virtual void tbSubject_EditValueChanged(object sender, EventArgs e) {
controller.Subject = tbSubject.Text;
}
protected internal virtual void OnOkButton() {
if (controller.IsConflictResolved()) {
controller.ApplyChanges();
}
//…
}
2. ASP.NET
On the web, the input form is a UserControl and is loaded as a template. To "bind" the values of editors to the properties of the controller, Data Binding Expressions are used via the syntax of the form "<% #%>". A controller instance is created in the template container class and is accessible from the form via the Container property.
The ascx form file is as follows
<%@ Control Language="C#" AutoEventWireup="true" Inherits="AppointmentForm" CodeFile="AppointmentForm.ascx.cs" %>
<%@ Register Assembly="DevExpress.Web.ASPxEditors.v10.2, … Namespace="DevExpress.Web.ASPxEditors" TagPrefix="dxe" %>
//…
//…
//…
Below is the code for the form template and container:
using System.Web.UI;
public partial class AppointmentForm : UserControl {
public override void DataBind() {
base.DataBind();
AppointmentFormTemplateContainer container = (AppointmentFormTemplateContainer)Parent;
AppointmentRecurrenceForm1.Visible = container.ShouldShowRecurrence;
//…
btnOk.ClientSideEvents.Click = container.SaveHandler;
}
public class AppointmentFormTemplateContainer : Control, IDataItemContainer, INamingContainer {
AppointmentFormController controller;
public AppointmentFormTemplateContainer(ASPxScheduler control) {
this.controller = CreateController(control, Appointment);
// …
}
public DateTime Start { get { return TimeZoneHelper.ToClientTime(Controller.EditedAppointmentCopy.Start); } }
public bool CanEditResource { get { return Controller.CanEditResource; } }
public bool ShouldShowRecurrence { get { return Controller.SourceAppointment.IsOccurrence && Controller.ShouldShowRecurrenceButton; } }
public DateTime RecurrenceStart {
get { return TimeZoneHelper.ToClientTime(Controller.EditedPattern != null ? Controller.EditedPattern.RecurrenceInfo.Start : DateTime.Now);
}
}
public string SaveHandler { get { return String.Format("function() {{ aspxAppointmentSave(\"{0}\"); }}", ControlClientId); } }
// …
}
3. WPF / SL
On this platform, the form template is represented by System.Windows.Controls.UserControl. Like WinForms, a controller instance is created in the form constructor. But the “binding” to the values of the editors is carried out in xaml through the mechanism of two-way Binding.
// …
// …
So to summarize
Using a form controller provides the following benefits:
- combining all the business logic of editing an object in one class
- the ability to expand editable properties when the interface of the object does not match the interface of the input form
- the ability to use a platform-independent controller object on different platforms and reduce the time it takes to port code from one platform to another
We hope that the material presented in the article will be useful to you.