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

CustomValidationAttribute

Uses a custom method for validation.

DataTypeAttribute

Specifies a particular type of data, such as e-mail address or phone number.

EnumDataTypeAttribute

Ensures that the value exists in an enumeration.

RangeAttribute

Designates minimum and maximum constraints.

RegularExpressionAttribute

Uses a regular expression to determine valid values.

RequiredAttribute

Specifies that a value must be provided.

StringLengthAttribute

Designates maximum and minimum number of characters.

ValidationAttribute

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

DataTypeAttribute

Specifies a particular type of data, such as e-mail address or phone number.

DisplayAttribute

Specifies localizable strings for data types and members that are used in the user interface.

DisplayColumnAttribute

Designates display and sorting properties when a table is used as a parent table in a foreign key relationship.

DisplayFormatAttribute

Specifies how data fields are displayed and formatted.

FilterUIHintAttribute

Designates the filtering behavior for a column.

UIHintAttribute

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

AssociationAttribute

Specifies that an entity member represents a data relationship, such as a foreign key relationship.

ConcurrencyCheckAttribute

Designates that a property participates in optimistic concurrency checks.

EditableAttribute

Specifies whether users should be able to change the value of the entity property.

KeyAttribute

Specifies one or more entity properties to use as the unique identity for the entity.

TimestampAttribute

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