How to: Customize Data Field Appearance and Behavior For Non-Intrinsic Data Types in the Data Model

When you are working with ASP.NET Dynamic Data, you can use the System.ComponentModel.DataAnnotations.DataTypeAttribute attribute to assign a data type to a data-model field. This is useful if you want to assign a type that is more specific to that data field than the CLR type that is inferred by Dynamic Data.

For example, you can mark a text field that contains e-mail addresses as an e-mail type, which would be defined as a specific type of text. The text field template that processes the field can use the information that is provided by this attribute to create special UI for displaying and editing the e-mail type. A text field that is marked with the EmailAddress() attribute might be displayed as a System.Web.UI.WebControls.HyperLink control.

You can also use a custom field template together with the UIHint attribute to specify specialized handling for specific data types. The DataTypeAttribute attribute lets you use one field template for several types.

The decision to use the DataTypeAttribute attribute or the UIHint attribute is often one of style and convenience. For information about how to use the UIHint property, see How to: Customize ASP.NET Dynamic Data Default Field Templates.

This topic describes how to use the DataTypeAttribute attribute.

To associate data type attributes with a data field

  1. Open the ASP.NET Web site where you want to customize data fields.

    Note

    The Web site must be configured for Dynamic Data.

  2. In Solution Explorer, right-click the App_Code folder, and then click Add New Item.

  3. Under Installed templates, click Class.

  4. Enter a name for the file in the Name box.

    The name of the class that you create must match the entity class name that represents the table. For example, if you are working with the Customer table, name the class Customer.

  5. Add the Partial keyword in Visual Basic or the partial keyword in Visual C# to the class definition to make it a partial class.

  6. Add references to the System.ComponentModel and System.ComponentModel.DataAnnotations namespaces by using the Imports keyword in Visual Basic or the using keyword in Visual C#, as shown in the following example:

    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    
    Imports System.ComponentModel
    Imports System.ComponentModel.DataAnnotations 
    
  7. Add a property accessor for each data field that you want to provide attributes for.

    The following example shows how to create property accessors for three properties that correspond to fields in the Customer table.

    public class CustomerMetaData {
    
        public object PasswordHash { get; set; }
        public object PasswordSalt { get; set; }
        public object ModifiedDate { get; set; }    
    }
    
    Public Class CustomerMetaData
    
        Public PasswordHash As Object
        Public PasswordSalt As Object
        Public ModifiedDate As Object
    End Class
    
  8. Create another class that will act as the associated metadata class for the partial class. You can give the class any name that is not already being used. For example, you can create a class that has the name CustomerMetaData as the associated metadata class for the Customer class.

  9. Add the MetadataTypeAttribute attribute to the partial class definition. For the attribute's parameter, use the name of the associated metadata class that you created in the previous step.

    [MetadataType(typeof(CustomerMetaData))]
    public partial class Customer {
    
    }
    
    <MetadataType(GetType(CustomerMetaData))> _
    Partial Public Class Customer
    
    End Class
    
  10. In the metadata class, add DataAnnotations attributes to each field whose display or behavior you want to modify.

    The following example shows a finished partial class for the Customer table, and an associated metadata class named CustomerMetaData. The metadata class contains public class fields that match database fields. The PasswordHash and PasswordSalt fields are marked with the ScaffoldColumnattribute attribute, which is set to false. This prevents the fields from being displayed by Dynamic Data. The ModifiedDate field is marked with the DataType attribute whose value is set to DataType.Date. This specifies that the data for this field is displayed by using the short date format.

    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    
    [MetadataType(typeof(CustomerMetaData))]
    public partial class Customer { }
    
    public class CustomerMetaData {
    
       [ScaffoldColumn(false)]
        public object PasswordHash { get; set; }
    
        [ScaffoldColumn(false)]
        public object PasswordSalt { get; set; }
    
        [DataTypeAttribute(DataType.Date)]
        public object ModifiedDate { get; set; }   
    
    }
    
    Imports Microsoft.VisualBasic
    Imports System.ComponentModel
    Imports System.ComponentModel.DataAnnotations
    
    <MetadataType(GetType(CustomerMetaData))> _
    Partial Public Class Customer
    
    End Class
    
    Public Class CustomerMetaData
    
            <ScaffoldColumn(False)> _
        Public PasswordSalt As Object
    
        <DataTypeAttribute(DataType.Date)> _
    Public PasswordSalt As Object
    
        <DataTypeAttribute(DataType.Date)> _
    Public ModifiedDate As Object  
    End Class
    
  11. To make sure that the partial class, metadata class, and attributes are working, run the application and display the table.

To modify a field template to use customized data attributes

  1. Open the field template that you want to customize. If you want to customize a built-in template, open the field template that corresponds to the data type that Dynamic Data maps the data to.

    For example, if you are customizing the field template that is used to display strings, open Text.ascx in the DynamicData\FieldTemplates directory.

  2. If required, change the markup.

  3. In the code-behind file, override the OnDataBinding method, which is called when the field template control gets the data to display. In the method, get the attribute or attributes for the current data field from the MetadataAttributes property of the FieldTemplateUserControl class that the field template derives from. You can then format or process the data according to what attributes the field is marked with.

    The following example shows the code that can be used in the Text.ascx field template to display the data fields that were modified earlier in this topic.

    Imports System
    Imports System.Data
    Imports System.Configuration
    Imports System.Collections
    Imports System.Collections.Specialized
    Imports System.Linq
    Imports System.Web
    Imports System.Web.Security
    Imports System.Web.UI
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.WebControls.WebParts
    Imports System.Web.UI.HtmlControls
    Imports System.Xml.Linq
    Imports System.Web.DynamicData
    Imports System.ComponentModel.DataAnnotations
    
    Partial Class TextField
        Inherits System.Web.DynamicData.FieldTemplateUserControl
    
        Private Function getNavUrl() As String
            Dim metadata = MetadataAttributes.OfType(Of DataTypeAttribute).FirstOrDefault()
            If (metadata Is Nothing) Then
                Return FieldValueString
            End If
    
            Dim url As String = FieldValueString
    
            Select Case metadata.DataType
    
                Case DataType.Url
                    url = FieldValueString
                    If (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) Or _
                       url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) Then
                        Return url
                    End If
    
                    Return "http://" + FieldValueString
    
                Case DataType.EmailAddress
                    Return "mailto:" + FieldValueString
    
                Case Else
                    Throw New Exception("Unknow DataType")
    
            End Select
    
        End Function
    
    
        Protected Overrides Sub OnDataBinding(ByVal e As System.EventArgs)
            MyBase.OnDataBinding(e)
    
            If (String.IsNullOrEmpty(FieldValueString)) Then
                Return
            End If
    
            Dim metadata = MetadataAttributes.OfType(Of DataTypeAttribute).FirstOrDefault()
    
            If (metadata Is Nothing Or String.IsNullOrEmpty(FieldValueString)) Then
                Dim literal As New Literal()
                literal.Text = FieldValueString
                Controls.Add(literal)
                Return
            End If
    
            If (metadata.DataType = DataType.Url Or _
                metadata.DataType = DataType.EmailAddress) Then
    
                Dim hyperlink As New HyperLink
                hyperlink.Text = FieldValueString
                hyperlink.NavigateUrl = getNavUrl()
                hyperlink.Target = "_blank"
                Controls.Add(hyperlink)
                Return
    
            End If
    
            If (metadata.DataType = DataType.Custom And _
                 String.Compare(metadata.CustomDataType, "BoldRed", True) = 0) Then
                Dim lbl As New Label()
                lbl.Text = FieldValueString
                lbl.Font.Bold = True
                lbl.ForeColor = System.Drawing.Color.Red
                Controls.Add(lbl)
            End If
    
        End Sub
    
    End Class
    
    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Xml.Linq;
    using System.Web.DynamicData;
    using System.ComponentModel.DataAnnotations;
    
    public partial class TextField : System.Web.DynamicData.FieldTemplateUserControl {
    
        string getNavUrl() {
    
            var metadata = MetadataAttributes.OfType<DataTypeAttribute>().FirstOrDefault();
            if (metadata == null)
                return FieldValueString; 
    
            switch (metadata.DataType) {
    
                case DataType.Url:
                    string url = FieldValueString;
                    if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
                        url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
                        return url;
    
                    return "http://" + FieldValueString;
    
    
                case DataType.EmailAddress:
                    return "mailto:" + FieldValueString;
    
                default:
                    throw new Exception("Unknown DataType");
            }
        }
    
        protected override void OnDataBinding(EventArgs e) {
            base.OnDataBinding(e);
    
            if (string.IsNullOrEmpty(FieldValueString))
                return;
    
            var metadata = MetadataAttributes.OfType<DataTypeAttribute>().FirstOrDefault();
    
            if (metadata == null || string.IsNullOrEmpty(FieldValueString)) {
                Literal literal = new Literal();
                literal.Text = FieldValueString;
                Controls.Add(literal);
                return;
            }
    
            if (metadata.DataType == DataType.Url ||
                metadata.DataType == DataType.EmailAddress) {
    
                HyperLink hyperlink = new HyperLink();
                hyperlink.Text = FieldValueString;
                hyperlink.NavigateUrl = getNavUrl();
                hyperlink.Target = "_blank";
                Controls.Add(hyperlink);
                return;
            }
    
            if (metadata.DataType == DataType.Custom &&
               string.Compare(metadata.CustomDataType, "BoldRed", true) == 0) {
                Label lbl = new Label();
                lbl.Text = FieldValueString;
                lbl.Font.Bold = true;
                lbl.ForeColor = System.Drawing.Color.Red;
                Controls.Add(lbl);
            }
    
        }
    
    }
    

    The code gets the attributes for the current field. It tests the attributes and runs different logic, depending on which attribute the field was marked with. For example, the code can determine that the field is marked with the custom BoldRed attribute by testing for Custom() and then testing that the CustomDataType() property is "BoldRed". If it is, the code creates UI to display the data in a Label control that is styled as red text.

Example

The following example shows how to customize data field appearance and behavior for non-intrinsic data types. The code customizes how Dynamic Data displays the EmailAddress, SalesPerson, and LastName fields from the Customer table of the AdventureWorksLT database.

Imports System.ComponentModel
Imports System.ComponentModel.DataAnnotations

<MetadataType(GetType(CustomerMetaData))> _
Partial Public Class Customer

End Class


Public Class CustomerMetaData

    <ScaffoldColumn(False)> _
    Public PasswordHash As Object

    <ScaffoldColumn(False)> _
    Public PasswordSalt As Object

    <DataTypeAttribute(DataType.Date)> _
    Public ModifiedDate As Object

    <DataTypeAttribute(DataType.EmailAddress)> _
    Public EmailAddress As Object

    <DataTypeAttribute(DataType.Url)> _
    Public SalesPerson As Object


    <DataTypeAttribute("BoldRed")> _
    <DisplayName("Last")> _
    Public ReadOnly Property LastName() As Object
        Get
            Return ""
        End Get
    End Property

End Class
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

[MetadataType(typeof(CustomerMetaData))]
public partial class Customer {
}

public class CustomerMetaData {

    [ScaffoldColumn(false)]
    public object PasswordHash { get; set; }

    [ScaffoldColumn(false)]
    public object PasswordSalt { get; set; }

    [DataTypeAttribute(DataType.Date)]
    public object ModifiedDate { get; set; }

    [DataTypeAttribute(DataType.EmailAddress)]
    public object EmailAddress { get; set; }

    [DataTypeAttribute(DataType.Url)]
    public object SalesPerson { get; set; }

    [DisplayName("Last")]
    [DataTypeAttribute("BoldRed")]
    [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$", 
        ErrorMessage = "Characters are not allowed.")]

    public object LastName { get; set; }
} 

In the example, the DataTypeAttribute attribute is set to EmailAddress() for the EmailAddress property. The DataTypeAttribute attribute is set to Url() for the SalesPerson property, and the DataTypeAttribute attribute is set to BoldRed for the LastName property. BoldRed is defined as a custom property.

Compiling the Code

To compile the example code, you need the following:

  • Microsoft Visual Studio 2008 Service Pack 1 or Visual Web Developer 2008 Express Edition Service Pack 1. 

  • The AdventureWorksLT sample database. For information about how to download and install the SQL Server sample database, see Microsoft SQL Server Product Samples: Database on the CodePlex site. Make sure that you install the correct version of the sample database for the version of SQL Server that you are running (Microsoft SQL Server 2005 or Microsoft SQL Server 2008).

  • A dynamic data-driven Web site. This enables you to create a data context for the database and to create the class that contains the data field to customize and the methods to override. For more information, see Walkthrough: Creating a New Dynamic Data Web Site using Scaffolding.

See Also

Tasks

How to: Customize ASP.NET Dynamic Data Default Field Templates

Change History

Date

History

Reason

July 2008

Added topic.

SP1 feature change.