Monday, May 30, 2011

ASP.NET Dynamic Data - Part 6 - Customize Field Templates For Non-Intrinsic Types

Sometimes it is necessary to assign a type more specific than the data field inferred by Dynamic Data. For example a text field which contains an e-mail address, could be defined as a specific type of text. The template that processes the field can generate special UI for displaying and editing the e-mail type.
All this is done by using a DataTypeAttribute on the data-model field.
In this post I'll change the type of the Price field in the Products table, from the default double to a currency datatype, which displays the price correctly.

Add the Currency datatype to the Price field

  1. Defined the tables metadata as described in part 3.
  2. Create a property for the data field to customize.
  3. Add the DataType attribute to the property and set the data type to DataType.Currency.
[ScaffoldTable(true)]
[MetadataType(typeof(ProductsMetadata))]
public partial class Products
{
}

public class ProductsMetadata
{
    [UIHint("StockCustom")]
    public object Stock { get; set; }

    [DataType(DataType.Currency)]
    public object Price { get; set; }
}
Now the price are displayed correctly, with the currency defined in windows.
Now what to do if this is still not good enough? What if the currency defined in Windows are incorrect? Well, read on and you'll get the answer.

Modify the field template

If the default behavior for the datatype aren't enough you can customize it. To do this create a a custom field template as described in part 3 and modify it to support you needs.
In my case I need to use USD instead of DKK. I'll create a custom field template for the Price field, and edit the code to look like this.
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;
using System.Globalization;

public partial class DynamicData_FieldTemplates_PriceCustom : System.Web.DynamicData.FieldTemplateUserControl {
    public override Control DataControl {
        get {
            return Label1;
        }
    }

    protected override void OnDataBinding(EventArgs e)
    {
        base.OnDataBinding(e);

        var metadata = MetadataAttributes.OfType().FirstOrDefault();
        if (String.IsNullOrEmpty(FieldValueString) || metadata == null)
            return;

        if (metadata.DataType == DataType.Currency)
        {
            Label1.Text = ((double)FieldValue).ToString("C", new CultureInfo("en-US"));
        }
    }
}
The reason for me to choose this rather explicit way to check the datatype, is to show how to handle this when the field template are used by multiple data fields with different data types, for example customizing the default template.
You also need to indicate in the tables metadata that the custom field template should be used instead of the default, this is done by adding the attribute to the data field UIHint, just as described in part 3.
The custom field template looks like this.
PriceCustom.ascx:
<%@ Control Language="C#"  AutoEventWireup="true" CodeFile="PriceCustom.ascx.cs" Inherits="DynamicData_FieldTemplates_PriceCustom" %>
PriceCustome.ascx.cs:
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;
using System.Globalization;

public partial class DynamicData_FieldTemplates_PriceCustom : System.Web.DynamicData.FieldTemplateUserControl {
    public override Control DataControl {
        get {
            return null;
        }
    }

    protected override void OnDataBinding(EventArgs e)
    {
        base.OnDataBinding(e);

        var metadata = MetadataAttributes.OfType().FirstOrDefault();
        if (String.IsNullOrEmpty(FieldValueString) || metadata == null)
            return;

        if (metadata.DataType == DataType.Currency)
        {
            Label label = new Label();
            label.Text = ((decimal)FieldValue).ToString("C", new CultureInfo("en-US"));
            Controls.Add(label);
        }
    }
}
The new page looks like this

1 comment:

  1. I have a question. I have two tables:
    1. Students(ID, FirstName, LastName)
    2. StudentGrades (ID, StudentID, Subject, Grade)

    How do I show Student Name as FirstName + ' ' LastName in the StudentGrades table.

    Your tutorials are excellent.
    martin.s.ransome@gmail.com

    ReplyDelete