如何:在数据模型中为非内部数据类型自定义数据字段的外观和行为

更新: 2008 年 7 月

在处理 ASP.NET 动态数据时,可以使用 System.ComponentModel.DataAnnotations.DataTypeAttribute 属性为数据模型字段指派一个数据类型。如果指派一个特定于该数据字段的类型,而不是由动态数据推断的 CLR 类型,将会很有用。

例如,可以将一个包含电子邮件地址的文本字段标记为电子邮件类型,该类型将被定义为文本的特定类型。处理字段的文本字段模板可以使用该属性提供的信息来创建特殊 UI,以显示和编辑电子邮件类型。使用 EmailAddress() 属性标记的文本字段可能显示为一个 System.Web.UI.WebControls.HyperLink 控件。

还可以与 UIHint 属性一起使用一个自定义字段模板,为特定的数据类型指定专用处理。使用 DataTypeAttribute 属性可以将一个字段模板用于多个类型。

决定是使用 DataTypeAttribute 属性还是使用 UIHint 属性通常是出于习惯或为了方便。有关如何使用 UIHint 属性的信息,请参见如何:自定义 ASP.NET 动态数据默认的字段模板

本主题介绍如何使用 DataTypeAttribute 属性。

将数据类型属性与数据字段关联

  1. 打开要在其中自定义数据字段的 ASP.NET 网站。

    Cc668199.alert_note(zh-cn,VS.90).gif说明:

    必须将网站配置为用于动态数据。

  2. 在**“解决方案资源管理器”中,右击 App_Code 文件夹,然后单击“添加新项”**。

  3. 在**“已安装的模板”下单击“类”**。

  4. 在**“名称”**框中输入文件名。

    创建的类名必须与表示表的实体类的名称匹配。例如,如果要使用 Customer 表,则将类命名为 Customer。

  5. 将 Visual Basic 中的 Partial 关键字或 Visual C# 中的 partial 关键字添加到类定义中,使其成为分部类。

  6. 使用 Visual Basic 中的 Imports 关键字或 Visual C# 中的 using 关键字,将引用添加到 System.ComponentModelSystem.ComponentModel.DataAnnotations 命名空间中,如下面的示例所示:

    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    
    Imports System.ComponentModel
    Imports System.ComponentModel.DataAnnotations 
    
  7. 为要为其提供属性的每个数据字段都添加一个属性访问器。

    下面的示例演示如何为与 Customer 表中字段对应的三个属性创建属性访问器。

    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. 创建另一个类,用作分部类的关联元数据类。可以为该类指定任何尚未使用的名称。例如,可以创建一个名称为 CustomerMetaData 的类,作为 Customer 类的关联元数据类。

  9. MetadataTypeAttribute 属性添加到分部类定义。对于此属性的参数,请使用上一步中创建的关联元数据类的名称。

    [MetadataType(typeof(CustomerMetaData))]
    public partial class Customer {
    
    }
    
    <MetadataType(GetType(CustomerMetaData))> _
    Partial Public Class Customer
    
    End Class
    
  10. 在元数据中,将 DataAnnotations 属性添加到每个要修改显示或行为的字段上。

    下面的示例演示 Customer 表的一个已完成的分部类,以及一个名为 CustomerMetaData 的关联元数据类。此元数据类包含与数据库字段匹配的公共类字段。PasswordHash 和 PasswordSalt 字段使用设置为 false 的 ScaffoldColumnattribute 属性进行标记。这可以防止字段被动态数据显示出来。ModifiedDate 字段是使用 DataType 属性进行标记的,其值设置为 DataType.Date。这指定了该字段的数据将通过短日期格式显示。

    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. 为确保分部类、元数据类和属性正常运行,请运行应用程序并显示表。

修改字段模板以使用自定义的数据属性

  1. 打开要自定义的字段模板。如果要自定义一个内置模板,请打开与动态数据映射数据的目标数据类型相对应的字段模板。

    例如,如果要自定义用来显示字符串的字段模板,则在 DynamicData\FieldTemplates 目录中打开 Text.ascx。

  2. 根据需要更改标记。

  3. 在代码隐藏文件中重写 OnDataBinding 方法,当字段模板控件获取要显示的数据时将调用该方法。在该方法中,从派生字段模板的 FieldTemplateUserControl 类的 MetadataAttributes 属性 (Property) 中,获取当前数据字段的属性 (Attribute)。然后根据标记字段的属性可以格式化或处理数据。

    下面的示例演示的代码可以用在 Text.ascx 字段模板中,用于显示本主题前面修改过的数据字段。

    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.href = 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.href = 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);
            }
    
        }
    
    }
    

    代码获取当前字段的属性。取决于标记字段的属性,代码还测试属性并运行不同的逻辑。例如,通过测试 Custom(),再测试 CustomDataType() 属性 (Property) 为“BoldRed”,代码可以确定字段是用自定义 BoldRed 属性 (Attribute) 标记的。如果这样,代码就创建 UI,用来在设计为红色文字的 Label 控件中显示数据。

示例

下面的示例演示如何为非内部数据类型自定义数据字段的外观和行为。该代码自定义 AdventureWorksLT 数据库中 Customer 表的 EmailAddress、SalesPerson 和 LastName 字段的动态数据显示方式。

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

在本示例中,DataTypeAttribute 属性 (attribute) 设置为 EmailAddress 属性 (property) 的 EmailAddress()。DataTypeAttribute 属性 (Attribute) 设置为 SalesPerson 属性 (Property) 的 Url(),DataTypeAttribute 属性 (Attribute) 设置为 LastName 属性 (Property) 的 BoldRed。BoldRed 定义为自定义属性。

编译代码

若要编译代码示例,您需要以下各项:

  • Microsoft Visual Studio 2008 Service Pack 1 或 Visual Web Developer 2008 速成版 Service Pack 1。

  • AdventureWorksLT 示例数据库。有关如何下载和安装 SQL Server 示例数据库的信息,请参见 CodePlex 站点上的 Microsoft SQL Server Product Samples: Database(Microsoft SQL Server 产品示例:数据库)。请确保安装了针对所运行的 SQL Server 版本(Microsoft SQL Server 2005 或 Microsoft SQL Server 2008)的示例数据库正确版本。

  • 动态数据驱动的网站。这允许您为数据库创建数据上下文,以及创建一个类,其中包含要自定义的数据字段和要重写的方法。有关更多信息,请参见Walkthrough: Creating a New Dynamic Data Web Site using Scaffolding

请参见

任务

如何:自定义 ASP.NET 动态数据默认的字段模板

修订记录

日期

修订历史记录

原因

2008 年 7 月

新增主题。

SP1 功能更改。