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 EmailAddress the 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. Marking the property with the EmailAddress attribute will cause an e-mail link to be created automatically. The example also shows how to fix up a URL. For the AdventureWorksLT database, the example code is required in order to create a well-formed URL.

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. Create class within the entity partial 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.

  8. 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. The following example shows how to create property accessors for three properties that correspond to fields in the Customer table.

    [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; }   
    
    }
    
    Public 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
    
  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 System.ComponentModel.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("https://", StringComparison.OrdinalIgnoreCase) Or _
                       url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) Then 
                        Return url
                    End If 
    
                    Return "https://" + 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("https://", StringComparison.OrdinalIgnoreCase) ||
                        url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
                        return url;
    
                    return "https://" + 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 attribute.

Compiling the Code

To compile the example code, you need the following:

See Also

Tasks

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