Using Data Annotations to Customize Data Classes
Microsoft Silverlight will reach end of support after October 2021. Learn more.
When you use data classes (also known as entity classes) in your Silverlight application, you can apply attributes to the class or members that specify validation rules, specify how the data is displayed, and set relationships between classes. The System.ComponentModel.DataAnnotations namespace contains the classes that are used as data attributes. By applying these attributes on the data class or member, you centralize the data definition and do not have to re-apply the same rules in multiple places.
The data annotation attributes fall into three categories: validation attributes, display attributes, and data modeling attributes. This topic describes these attributes and provides examples.
Validation Attributes
The System.ComponentModel.DataAnnotations namespace contains the following attributes which are used to enforce validation rules for data applied to the class or member:
Validation Attribute |
Description |
---|---|
Uses a custom method for validation. |
|
Specifies a particular type of data, such as e-mail address or phone number. |
|
Ensures that the value exists in an enumeration. |
|
Designates minimum and maximum constraints. |
|
Uses a regular expression to determine valid values. |
|
Specifies that a value must be provided. |
|
Designates maximum and minimum number of characters. |
|
Serves as base class for validation attributes. |
All validation attributes derive from the ValidationAttribute class. The logic to determine if a value is valid is implemented in the overridden IsValid method. The Validate method calls the IsValid method and throws a ValidationException if the value is not valid.
To create customized validation checks, you can either create a class that derives from the ValidationAttribute class or create a method that performs the validation check and reference that method when applying the CustomValidationAttribute to the data member. When you create a class that derives from ValidationAttribute, override the IsValid method to provide the logic for your customized validation check.
Currently, the DataGrid control is the only control that automatically applies validation attributes. For an example of using a DataGrid control with a class that contains validation attributes, see the Data Grid Example below. When you do not use the DataGrid control, you must manually validate the values.
Manually Validating Values
When you do not use the DataGrid control to provide the interface for editing data, the validation attributes are not automatically applied. However, you can manually apply the validation test by using the Validator class. You can call the ValidateProperty method on the set accessor of a property to check the value against the validation attributes for the property. You must also set both ValidatesOnExceptions and NotifyOnValidationError properties to true when data binding to receive validation exceptions from validation attributes. For an example of manually applying validation, see the Data Binding Example below.
Display Attributes
The System.ComponentModel.DataAnnotations namespace contains the following attributes which are used to specify how data from the class or member is displayed:
Display Attribute |
Description |
---|---|
Specifies a particular type of data, such as e-mail address or phone number. |
|
Specifies localizable strings for data types and members that are used in the user interface. |
|
Designates display and sorting properties when a table is used as a parent table in a foreign key relationship. |
|
Specifies how data fields are displayed and formatted. |
|
Designates the filtering behavior for a column. |
|
Designates the control and values to use to display the associated entity member. |
The display attributes are automatically applied when used with the DataGrid control. You can manually retrieve display attribute values when data binding by using controls such as Label and DescriptionViewer. For an example of using the Label and DescriptionViewer controls with display attributes, see the Data Binding Example below.
The DataTypeAttribute attribute specifies a particular type for a data member and therefore is used as both a validation attribute and display attribute.
Data Modeling Attributes
The System.ComponentModel.DataAnnotations namespace contains the following attributes which are used to specify the intended use of data members and the relationships between data classes:
Data Modeling Attribute |
Description |
---|---|
Specifies that an entity member represents a data relationship, such as a foreign key relationship. |
|
Designates that a property participates in optimistic concurrency checks. |
|
Specifies whether users should be able to change the value of the entity property. |
|
Specifies one or more entity properties to use as the unique identity for the entity. |
|
Designates a member as a time stamp value for data versioning. |
DataGrid Example
This example shows a DataGrid control that enables the user to view and edit data from a data class. Data annotations are applied to the data class to specify display and validation behavior. The validation and display attributes are automatically applied when used with the DataGrid control.
The following example shows the data class named Product with data annotations applied to the members.
Public Class Product
Private ProductIDValue As Integer
Private ProductNameValue As String
Private ListPriceValue As Double
Private ColorValue As ProductColor
Private InStockValue As Boolean
<Display(Name:="Product Number")> _
<Range(0, 5000)> _
Public Property ProductID() As Integer
Get
Return ProductIDValue
End Get
Set(ByVal value As Integer)
ProductIDValue = value
End Set
End Property
<Display(Name:="Name")> _
<Required()> _
Public Property ProductName() As String
Get
Return ProductNameValue
End Get
Set(ByVal value As String)
ProductNameValue = value
End Set
End Property
<Display(Name:="Price")> _
<DataType(DataType.Currency)> _
Public Property ListPrice() As Double
Get
Return ListPriceValue
End Get
Set(ByVal value As Double)
ListPriceValue = value
End Set
End Property
<EnumDataType(GetType(ProductColor))> _
Public Property Color() As ProductColor
Get
Return ColorValue
End Get
Set(ByVal value As ProductColor)
ColorValue = value
End Set
End Property
<Display(Name:="Available")> _
Public Property InStock() As Boolean
Get
Return InStockValue
End Get
Set(ByVal value As Boolean)
InStockValue = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal _productID As Integer, ByVal _productName As String, _
ByVal _listPrice As Double, ByVal _color As ProductColor, _
ByVal _inStock As Boolean)
Me.ProductID = _productID
Me.ProductName = _productName
Me.ListPrice = _listPrice
Me.Color = _color
Me.InStock = _inStock
End Sub
End Class
Public Enum ProductColor
Red
White
Purple
Blue
End Enum
public class Product
{
[Display(Name="Product Number")]
[Range(0, 5000)]
public int ProductID { get; set; }
[Display(Name="Name")]
[Required]
public string ProductName { get; set; }
[Display(Name="Price")]
[DataType(DataType.Currency)]
public double ListPrice { get; set; }
[EnumDataType(typeof(ProductColor))]
public ProductColor Color { get; set; }
[Display(Name="Available")]
public bool InStock { get; set; }
public Product() { }
public Product(int _productID, string _productName,
double _listPrice, ProductColor _color, bool _inStock)
{
this.ProductID = _productID;
this.ProductName = _productName;
this.ListPrice = _listPrice;
this.Color = _color;
this.InStock = _inStock;
}
}
public enum ProductColor
{
Red, White, Purple, Blue
}
The following examples show the page and code-behind file for displaying the data class in a data grid.
<Grid x:Name="LayoutRoot">
<ScrollViewer x:Name="PageScrollViewer">
<StackPanel x:Name="ContentStackPanel">
<TextBlock x:Name="HeaderText" Text="Products"/>
<sdk:DataGrid x:Name="DataGrid1" Foreground="Black" AutoGenerateColumns="True">
</sdk:DataGrid>
</StackPanel>
</ScrollViewer>
</Grid>
Partial Public Class ProductPage
Inherits Page
Dim AvailableProducts As ObservableCollection(Of Product)
Public Sub New()
InitializeComponent()
AvailableProducts = New ObservableCollection(Of Product)()
AvailableProducts.Add(New Product(1, "Bike", 500, ProductColor.Red, True))
AvailableProducts.Add(New Product(2, "Chair", 250, ProductColor.White, True))
AvailableProducts.Add(New Product(3, "Plate", 20, ProductColor.Purple, False))
AvailableProducts.Add(New Product(4, "Kite", 15, ProductColor.Blue, True))
DataGrid1.ItemsSource = AvailableProducts
End Sub
End Class
public partial class ProductPage : Page
{
ObservableCollection<Product> AvailableProducts;
public ProductPage()
{
InitializeComponent();
AvailableProducts = new ObservableCollection<Product>();
AvailableProducts.Add(new Product(1, "Bike", 500, ProductColor.Red, true));
AvailableProducts.Add(new Product(2, "Chair", 250, ProductColor.White, true));
AvailableProducts.Add(new Product(3, "Plate", 20, ProductColor.Purple, false));
AvailableProducts.Add(new Product(4, "Kite", 15, ProductColor.Blue, true));
DataGrid1.ItemsSource = AvailableProducts;
}
}
Data Binding Example
This example demonstrates a ValidationSummary control that displays errors for three TextBox controls on a form. The validation rules are specified in attributes in the Customer class and manually applied in the set accessor. The ValidatesOnExceptions and NotifyOnValidationError properties are set to true in the data binding.
<!-- NOTE:
By convention, the sdk prefix indicates a URI-based XAML namespace declaration
for Silverlight SDK client libraries. This namespace declaration is valid for
Silverlight 4 only. In Silverlight 3, you must use individual XAML namespace
declarations for each CLR assembly and namespace combination outside the scope
of the default Silverlight XAML namespace. For more information, see the help
topic "Prefixes and Mappings for Silverlight Libraries".
-->
<UserControl x:Class="ValidationSample.MainPage"
xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot" Margin="15" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="300"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="35"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<!-- Unbound Date of Birth field -->
<sdk:Label Content="Date of Birth" IsRequired="True" Margin="5" />
<StackPanel Orientation="Horizontal" Grid.Column="1">
<sdk:DatePicker Height="23" />
<sdk:DescriptionViewer Description="Please enter your date of birth."/>
</StackPanel>
<!-- ID Number field -->
<sdk:Label Grid.Row="1" Margin="5"
Target="{Binding ElementName=tbIdNumber}" />
<StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="1">
<TextBox x:Name="tbIdNumber" Height="23" Width="100"
Text="{Binding IdNumber, Mode=TwoWay,
ValidatesOnExceptions=true, NotifyOnValidationError=true}" />
<sdk:DescriptionViewer Target="{Binding ElementName=tbIdNumber}"/>
</StackPanel>
<!-- Name field -->
<sdk:Label Grid.Row="2" Margin="5"
Target="{Binding ElementName=spName}"
PropertyPath="FirstName" />
<StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="2">
<StackPanel x:Name="spName" Orientation="Horizontal" >
<TextBox x:Name="tbFirstName" Text="{Binding FirstName, Mode=TwoWay,
ValidatesOnExceptions=true, NotifyOnValidationError=true}"
Height="23" Width="100" />
<TextBox x:Name="tbLastName" Text="{Binding LastName, Mode=TwoWay,
ValidatesOnExceptions=true, NotifyOnValidationError=true}"
Height="23" Width="100" />
</StackPanel>
<sdk:DescriptionViewer Target="{Binding ElementName=spName}"
PropertyPath="FirstName"/>
</StackPanel>
<!-- ValidationSummary -->
<sdk:ValidationSummary Grid.ColumnSpan="2" Grid.Row="3" />
</Grid>
</UserControl>
Imports System.ComponentModel.DataAnnotations
Imports System.Windows.Controls
Partial Public Class MainPage
Inherits UserControl
Public Sub New()
InitializeComponent()
Me.DataContext = New Customer("J", "Smith", 12345)
End Sub
End Class
Public Class Customer
' Private data members.
Private m_IdNumber As Integer
Private m_FirstName As String
Private m_LastName As String
Public Sub New(ByVal firstName As String, ByVal lastName As String, ByVal id As Integer)
Me.IdNumber = id
Me.FirstName = firstName
Me.LastName = lastName
End Sub
' Public properties.
<Display(Name:="ID Number", Description:="Enter an integer between 0 and 99999.")> _
<Range(0, 99999)> _
Public Property IdNumber() As Integer
Get
Return m_IdNumber
End Get
Set(ByVal value As Integer)
Validator.ValidateProperty(value, _
New ValidationContext(Me, Nothing, Nothing) With {.MemberName = "IdNumber"})
m_IdNumber = value
End Set
End Property
<Display(Name:="Name", Description:="First + Last Name.")> _
<Required(ErrorMessage:="First Name is required.")> _
<RegularExpression("^[a-zA-Z''-'\s]{1,40}$", ErrorMessage:= _
"Numbers and special characters are not allowed in the name.")> _
Public Property FirstName() As String
Get
Return m_FirstName
End Get
Set(ByVal value As String)
Validator.ValidateProperty(value, _
New ValidationContext(Me, Nothing, Nothing) With {.MemberName = "FirstName"})
m_FirstName = value
End Set
End Property
<Required(ErrorMessage:="Last Name is required.")> _
<RegularExpression("^[a-zA-Z''-'\s]{1,40}$", ErrorMessage:= _
"Numbers and special characters are not allowed in the name.")> _
<StringLength(8, MinimumLength:=3, ErrorMessage:= _
"Last name must be between 3 and 8 characters long.")> _
Public Property LastName() As String
Get
Return m_LastName
End Get
Set(ByVal value As String)
Validator.ValidateProperty(value, _
New ValidationContext(Me, Nothing, Nothing) With {.MemberName = "LastName"})
m_LastName = value
End Set
End Property
End Class
using System.ComponentModel.DataAnnotations;
using System.Windows.Controls;
namespace ValidationSample
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.DataContext = new Customer("J", "Smith", 12345);
}
}
public class Customer
{
// Private data members.
private int m_IdNumber;
private string m_FirstName;
private string m_LastName;
public Customer(string firstName, string lastName, int id)
{
this.IdNumber = id;
this.FirstName = firstName;
this.LastName = lastName;
}
// Public properties.
[Display(Name = "ID Number", Description = "Enter an integer between 0 and 99999.")]
[Range(0, 99999)]
public int IdNumber
{
get { return m_IdNumber; }
set
{
Validator.ValidateProperty(value,
new ValidationContext(this, null, null) { MemberName = "IdNumber" });
m_IdNumber = value;
}
}
[Display(Name="Name", Description="First Name + Last Name.")]
[Required(ErrorMessage = "First Name is required.")]
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage =
"Numbers and special characters are not allowed in the name.")]
public string FirstName
{
get { return m_FirstName; }
set
{
Validator.ValidateProperty(value,
new ValidationContext(this, null, null) { MemberName = "FirstName" });
m_FirstName = value;
}
}
[Required(ErrorMessage = "Last Name is required.")]
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage =
"Numbers and special characters are not allowed in the name.")]
[StringLength(8, MinimumLength = 3, ErrorMessage =
"Last name must be between 3 and 8 characters long.")]
public string LastName
{
get { return m_LastName; }
set
{
Validator.ValidateProperty(value,
new ValidationContext(this, null, null) { MemberName = "LastName" });
m_LastName = value;
}
}
}
}