Quantcast
Channel: Developer Express Inc.
Viewing all articles
Browse latest Browse all 2437

DevExpress MVVM Framework. Using DataAnnotation attributes and DevExpress Fluent API.

$
0
0

000

Imagine configuring editing and validation settings from one place – eliminating the need to separately configure each data view (XAML). DataAnnotation attributes allow you to do exactly that.
Beginning with version 13.2, a consistent set of functionalities were implemented across all our controls. Originally, GridControl provided limited support for attributes and cell editors could not be configured by attributes specifying an editor data type. Bands could not be set at the data source level. Moreover, DataAnnotation attributes did not exist for PropertyGridControl. Now, all of our components provide similar functionality at design time, runtime, and the Scaffolding Wizard. We also introduced a flexible alternative in version 13.2 – the DevExpress Fluent API.

Which attributes are supported?

DevExpress editors, GridControl, DataLayoutControl, Scaffolding Wizards and other components support nearly every standard attribute in the System.ComponentModel.DataAnnotations namespace.

We also implemented a number of our own attributes for defining masks. These attributes are available from the DevExpress.Xpf.Mvvm.DataAnnotations namespace.

To learn more about the available attributes, see the following help topic: Data Annotation Attributes.

Common decoration patterns

  • In a model class

The simplest way to assign an attribute is to explicitly declare it on a property of a model class:

publicclass Point {
[Display(Name = "Abscissa")]
publicdouble X { get; set; }
[Display(Name = "Ordinate")]
publicdouble Y { get; set; }
}

Although this approach is simple and descriptive, your code may become difficult to read when there are many properties. You may also find your model class does not allow attribute decorations. For example, a WCF Data Service or the Entity Framework Database First approach may automatically generate model classes.

  • In a metadata class

Declare a metadata class and assign it to a model via the MetadataType attribute:

[MetadataType(typeof(PointMetadata))]
publicclass Point {
publicdouble X { get; set; }
publicdouble Y { get; set; }
}
publicclass PointMetadata {
[Display(Name = "Abscissa")]
publicdouble X { get; set; }
[Display(Name = "Ordinate")]
publicdouble Y { get; set; }
}

You can extend this approach to automatically generated classes using partial classes.

The obvious disadvantage is the metadata class is both difficult to support and error prone. For instance, you will not receive an error at compile time if you rename a model property and forget to do the same for its metadata class.

  • DevExpress Fluent API

Considering these disadvantages, we developed a flexible solution – the DevExpress Fluent API. The following code is written with the DevExpress Fluent API:

[MetadataType(typeof(PointMetadata))]
publicclass Point {
publicdouble X { get; set; }
publicdouble Y { get; set; }
}
publicstaticclass PointMetadata {
publicvoid BuildMetadata(MetadataBuilder<Point> builder) {
builder.Property(x => x.X).DisplayName("Abscissa");
builder.Property(x => x.Y).DisplayName("Ordinate");
}
}

Since the DevExpress Fluent API does not allow setting attributes on non-existent properties, renaming properties becomes easy.

It’s sometimes convenient to arrange properties in groups. The classic approach for this is to set a Display Attribute on the grouped properties. With the DevExpress Fluent API, properties can be grouped as follows:

builder
.Group("General Info")
.ContainsProperty(x => x.FirstName)
.ContainsProperty(x => x.LastName)
.EndGroup()
.Group("Contacts")
.ContainsProperty(x => x.Email)
.ContainsProperty(x => x.Phone)
.EndGroup()
.Group("Address")
.ContainsProperty(x => x.City)
.ContainsProperty(x => x.Zip)
.EndGroup();

While the Fluent API is valuable in complex scenarios, in simple cases it still makes sense to use one of the standard approaches.

Using attributes with DevExpress controls

We have a few demos that clearly demonstrate how to use attributes with our controls: “Data Grid - Smart Columns Generation”, “Layout Manager - Data Layout Control”, “Property Grid - DataAnnotation Attributes”, “Property Grid - DataAnnotation Attributes (Fluent API)”. Please refer to them to see our controls in action.

We prepared two samples. The first sample (E5179) illustrates how to use attributes with Entity Framework Code First, the second (E5180) uses the DevExpress Fluent API. The samples are very similar; below you will see a screenshot of how they look:

2014-03-31_1501 - Copy

  • GridControl

To configure the GridControl layout in for the underlying data types and attributes, set the GridControl.EnableSmartColumnsGeneration property to True. In this case, GridControl will automatically generates columns at runtime and customize them according to DataAnnotation and DevExpress Fluent API usage.

You can also generate columns at design time based on the data source objects. GridControl’s SmartTag provides the “Generate Columns” action for this purpose:

2014-03-31_1505_1

Generated columns have their IsSmart property set to True. It means that these columns are configured automatically. In the meantime, you are free to set any property manually – your settings will override the automatic settings.

You can call the TableLayout method of the Fluent API to define a grouping applicable only for GridControls:

builder.TableLayout()
.Group("Personal Data")
.ContainsProperty(x => x.FirstName)
.ContainsProperty(x => x.LastName)
.EndGroup();

The DevExpress Scaffolding Wizard also generates columns with IsSmart=True. To avoid generating columns for some properties, set the ScaffoldColumn(false) attribute for them:

[ScaffoldColumn(false)]
publicstring LastName { get; set; }

  • DataLayoutControl

With the Fluent API, you can configure groups by calling the Group method as we did for GridControl. If you like, it's also possible to define a grouping exclusively for DataLayoutControl using the DataFormLayout method. The following code snippet demonstrates this:

builder.DataFormLayout()
.GroupBox("General Info")
.ContainsProperty(x => x.FirstName)
.ContainsProperty(x => x.LastName)
.ContainsProperty(x => x.CreditCardNumber)
.EndGroup()
.GroupBox("Contacts")
.ContainsProperty(x => x.Email)
.ContainsProperty(x => x.Phone)
.EndGroup()
.GroupBox("Address")
.ContainsProperty(x => x.Address)
.ContainsProperty(x => x.City)
.ContainsProperty(x => x.State)
.ContainsProperty(x => x.Zip)
.EndGroup();

The Scaffolding Wizard generates a simple LayoutControl with LayoutGroup and LayoutItem objects. You can customize them in the designer as you like.

  • PropertyGridControl

PropertyGridControl automatically generates rows for the defined attributes.

PropertyGridControl provides the capability to initialize properties at runtime. In this scenario, you could opt for a custom item initializer. For this, assign the InstanceInitializer attribute to the required property.

For example:

[InstanceInitializer(typeof(Item1), "Item1")]
publicobject Item { get; set; }

At runtime, a user will see an additional “Item1” button in the Property Menu. When s/he presses this button, the Item property will be set to a new Item1 class instance.

Some tricks

  • It is possible to not create a metadata builder class; instead, place the BuildMetadata method directly in a data class:
publicclass Point {
publicdouble X { get; set; }
publicdouble Y { get; set; }
publicstaticvoid BuildMetadata(MetadataBuilder<Point> builder) {
builder.Property(x => x.X).DisplayName("Abscissa");
builder.Property(x => x.Y).DisplayName("Ordinate");
}
}

  • If you have a library which you can’t modify, use MetadataLocator to register metadata classes on startup:
MetadataLocator.Default = MetadataLocator.Create()
.AddMetadata<Metadata>();
publicclass Metadata {
publicstaticvoid BuildMetadata(MetadataBuilder<Employee> builder) {
builder.Property(x => x.FullName).ReadOnly();
}
publicstaticvoid BuildMetadata(MetadataBuilder<Team> builder) {
builder.Property(x => x.Id).ReadOnly();
}
}

  • Metadata for generic classes:
[MetadataType(typeof(BaseGenericClassMetadata<>))]
publicclass BaseGenericClass<T1> {
publicint BaseProperty1 { get; set; }
publicint BaseProperty2 { get; set; }
publicint BaseProperty3 { get; set; }
publicint BaseProperty4 { get; set; }
}
publicclass BaseGenericClassMetadata<T1> {
publicstaticvoid BuildMetadata(MetadataBuilder<BaseGenericClass<T1>> builder) {
builder.Property(x => x.BaseProperty2).ReadOnly();
}
publicstaticvoid BuildBaseMetadata<T>(MetadataBuilder<T>
builder) where T : BaseGenericClass<T1> {
builder.Property(x => x.BaseProperty4).ReadOnly();
}
}

 

That’s all. Thank you for your time.

OTHER RELATED ARTICLES:

  1. Getting Started with DevExpress MVVM Framework. Commands and View Models.
  2. DevExpress MVVM Framework. Introduction to Services, DXMessageBoxService and DialogService.
  3. DevExpress MVVM Framework. Interaction of ViewModels. IDocumentManagerService.
  4. DevExpress MVVM Framework. Introduction to POCO ViewModels.
  5. DevExpress MVVM Framework. Interaction of ViewModels. Messenger.
  6. DevExpress MVVM Framework. Using Scaffolding Wizards for building Views.
  7. DevExpress MVVM Framework. Data validation. Implementing IDataErrorInfo.
  8. THIS POST: DevExpress MVVM Framework. Using DataAnnotation attributes and DevExpress Fluent API.

Viewing all articles
Browse latest Browse all 2437

Latest Images

Trending Articles



Latest Images