WinForms Data Layout Control: Data Annotation Attributes (Shipping in v15.1)

Thinking Out Loud
08 June 2015

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; }
    }

DevExpress Form Layout Control

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...

DevExpress Form Layout - Attributes


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...

DevExpress Form Layout - Detail Form with Groups

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.
DevExpress Form Layout Control - Data Validation


7 comment(s)
Anders Wang
Anders Wang

Hi,

How to dynamically generate this annotations?Because, for an business object, may be used at many places. Different UI may have different annotation requirement.

8 June, 2015
Neven
Neven

How to implement localization in this scenario?

9 June, 2015
Steve Sharkey
Steve Sharkey

I assume it can still be cusomised by the user? If I wanted to omit a field from the data layout is there an attribute to do that?

9 June, 2015
Wojciech
Wojciech

Hi.

Is there any possibility to combine this functionality w XPO Objects? For example automatically adding attributes for DisplayName or StringLengthAttribute based on *.xpo file?

9 June, 2015
Roman Kalachik (DevExpress)
Roman Kalachik (DevExpress)

@Anders, annotation attributes cannot be used this way.

@Nevren, please refer to this article stackoverflow.com/.../localization-of-displaynameattribute

@Steve, we have shown how to hide the ID field from the layout. Does it work for you? Would you please create a ticket in our Support Center, so we will discuss your scenario in detail?

10 June, 2015
Dmitry Babich (DevExpress)
Dmitry Babich (DevExpress)

@Wojciech This idea sounds good. We do have plans to make some improvements in this direction in the future, so we will consider adding these attributes' support as well.

10 June, 2015
Wojciech
Wojciech

@Dimitros

We use XPO extensively and automatically generating some Data Annotation based on fields that are already present in XPO schema will be a big help.

Could you please inform me when I can expect that functionality?

Or maybe create some new feature ticket?

11 June, 2015

Please login or register to post comments.