For those unaware of our WinForms Data Layout Control, here is a quick summary of its newest feature (Data Annotation Attributes) and how you can build forms and layout individual controls directly from your data source. As I hope you'll discover, this tool is extremely useful - especially when working with detail forms...
Once bound to a data source, the Layout Control retrieves data fields automatically and builds its layout with the appropriate editors (based on the underlying data type). By default, when binding to a simple business object (see Person class below), the DevExpress Data Layout Control automatically generates a linear layout, wherein layout items (controls) are arranged in one column.
public class Person
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public Image Image { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Skype { get; set; }
public string AddressLine1 { get; set;
}
public string AddressLine2 { get; set;
}
public string Description { get; set; }
public DateTime BirthDate { get; set; }
public Gender Gender { get; set; }
public string Group { get; set; }
public DateTime HireDate { get; set; }
public decimal Salary { get; set; }
public string Title { get; set; }
}
With our most recent update, the Data Layout Control ships with Data Annotation Attribute support (https://documentation.devexpress.com/#WindowsForms/CustomDocument114039) - useful for the automatic generation of complex layouts from a business object. To get started with this new capability, you simply need to mark the bound object's properties using dedicated attributes and assign the object as the control's data source. When building the layout, the control will automatically recognize these attributes and will provide display names and specify layout order item, arrange items into groups and tabbed groups, specify a control's read-only state, assign masks to editors, etc.
Let's dig into this new feature and use specific annotation attributes in the Person class to see the feature in action. First thing we'll do is prevent the ID field from being automatically generated by the Data Layout Control. We'll apply the Display attribute with the AutoGenerateField parameter set to false for the ID field.
[Key,Display(AutoGenerateField
=false)]
public int ID {
get; set; }
To assign custom captions to layout items, we'll use Display attribute with the Name parameter. For the Image field, we';; specify an empty string to hide the label...
[Display(Name ="Last name :")]
public string
LastName { get; set; }
[Display(Name ="First name:")]
public string FirstName { get; set; }
[Display(Name = "")]
public Image Image { get; set;
}
By default, single line text editors are generated by the Data Layout Control for editing a bound object's fields. You can change editor types used via the DataType attribute. For instance, the following code specifies use of a multiline Memo editor for the Description field.
[Display(Name = "About :"),
DataType(DataType.MultilineText)]
public string Description { get; set; }
And here's the form...
The Display attribute with GroupName parameter allows you to combine items into groups (borderless, standard or tabbed groups). The following code positions the Email and Skype fields into a borderless group inside the Job tab.
const string EmailAndSkype ="<Root>/<Photo->/<FirstAndLastName>/{Tabs}/Job/<EmailAndSkype->";
[Display(Name = "E-Mail :", GroupName =
EmailAndSkype)]
public string Email { get; set; }
[Display(Name = "Skype :", GroupName =
EmailAndSkype)]
public string Skype { get; set; }
The form will now look as follows...
Notice that GroupName parameter addresses a target group that is nested in another group. Group names are separated by the slash character. By default, the Layout Control adds auto-generated items to the bottom of the parent group in a vertical orientation and to the right in a horizontal orientation. Of course, you may want to arrange items in the generated layout using a custom order. To do this, you'd apply the Display attribute with the Order parameter.
Display(Name ="", GroupName = Photo, Order=0)]
public Image Image { get; set; }
A look at the final form...
I hope you can see how easy it is to create a ready-to-use form layout by applying Data Annotation attributes to the data object's fields. In addition to layout-aware and editor-specific attributes, the Data Layout Control supports a set of data validation attributes (StringLengthAttribute, RangeAttribute, RequiredAttribute, etc.). In the following example, the Range attribute is used to check whether the BirthDate value is in the specified interval.
[Range(typeof(DateTime),"1/1/1900", "1/1/2000", ErrorMessage = "Birthday is
out of Range")]
public DateTime BirthDate { get; set; }
Here is the complete code sample used in this demo:
public class Person {
const
string RootGroup = "<Root>";
const
string Photo = RootGroup + "/" + "<Photo->";
const
string FirstNameAndLastName = Photo + "/" +"<FirstAndLastName>";
const
string TabbedGroup = FirstNameAndLastName + "/" + "{Tabs}";
const
string ContactGroup = TabbedGroup + "/" + "Contact";
const
string BDateAndGender = ContactGroup + "/" +"<BDateAndGender->";
const
string HomeAddressAndPhone = ContactGroup + "/" +"<HomeAddressAndPhone->";
const
string JobGroup = TabbedGroup + "/" + "Job";
const
string HDateAndSalary = JobGroup + "/" +"<HDateAndSalary->";
const
string EmailAndSkype = JobGroup + "/" +"<EmailAndSkype->";
const
string GroupAndTitle = JobGroup + "/" +"<GroupAndTitle->";
[Key,Display(AutoGenerateField
=false)]
public int
ID { get; set; }
[Display(Name = "Last name :", GroupName =
FirstNameAndLastName, Order = 2)]
public
string LastName { get; set; }
[Display(Name = "First name:", GroupName =
FirstNameAndLastName, Order = 1)]
public
string FirstName { get; set; }
[Display(Name = "", GroupName = Photo, Order=0)]
public
Image Image { get; set; }
[Display(Name = "Phone :", GroupName = HomeAddressAndPhone)]
public
string Phone { get; set; }
[Display(Name
= "E-Mail :", GroupName = EmailAndSkype, Order=5)]
public
string Email { get; set; }
[Display(Name = "Skype :", GroupName = EmailAndSkype)]
public
string Skype { get; set; }
[Display(Name = "Home address :", GroupName = HomeAddressAndPhone)]
public
string AddressLine1 { get; set; }
[Display(Name = "Work address :", GroupName = JobGroup)]
public
string AddressLine2 { get; set; }
[Display(Name = "About :", GroupName = RootGroup),
DataType(DataType.MultilineText)]
public
string Description { get; set; }
[Range(typeof(DateTime), "1/1/1900", "1/1/2000",
ErrorMessage = "Birthday is out of Range")]
[Display(Name = "Birthday :", GroupName = BDateAndGender,
Order = 3)]
public
DateTime BirthDate { get; set; }
[Display(Name = "Gender :", GroupName = BDateAndGender)]
public
Gender Gender { get; set; }
[Display(Name = "Group :", GroupName = GroupAndTitle, Order =
6)]
public
string Group { get; set; }
[Display(Name = "Hire date :",GroupName=HDateAndSalary,
Order=4)]
public
DateTime HireDate { get; set; }
[Display(Name = "Salary :", GroupName = HDateAndSalary)]
public
decimal Salary { get; set; }
[Display(Name = "Title :", GroupName = GroupAndTitle)]
public
string Title { get; set; }
}
And before I forget, here's an image of a form layout generated from a business object using our Layout Control -- with data validation enabled.