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
Open the ASP.NET Web site where you want to customize data fields.
Note
The Web site must be configured for Dynamic Data.
In Solution Explorer, right-click the App_Code folder, and then click Add New Item.
Under Installed templates, click Class.
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.
Add the Partial keyword in Visual Basic or the partial keyword in Visual C# to the class definition to make it a partial class.
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
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.
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
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
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
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
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.
If required, change the markup.
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:
- A Dynamic Data Web site. 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