Sunday, May 29, 2011

ASP.NET Dynamic Data - Part 3 - Customize Field Templates

Now it's time for part 3 in the series about ASP.NET Dynamic Data, how to customize the appearance and behavior of default field templates.
All changes in the default template will affect all pages and controls where the template is used.
All templates used by the Dynamic Data framework are placed in /DynamicData, for example field templates are placed in the /DynamicData/FieldTemplates.
The default field templates are all usercontrols, which you are free to edit as you like, they are all named descriptively, making it easy to quickly recognize the place where it is used.
For example, the display control for text are called, Text.ascx, the edit template are called, Text_Edit.ascx.
Since all these controls are using familiar technology they can be easily customized to fit you own needs.

Creating a custom template

If needed a custom field template can easily be created. In my example if got a data model which looks like this:
The custom data field template I need, is a template which displays the stock count with a red background in below 10, a yellow background in between 10 and 20, otherwise the default background color will be used.
To create the custom template a add a new item to the /DynamicData/FieldTemplates folder, the new item is a Dynamic Data Field item, available from the Add New Item dialog.
In my case the default markup needs to be changed from a literal control to a label control, as literal controls, don't allow me to change the background color.
Markup:
<%@ Control Language="C#"  AutoEventWireup="true" CodeFile="StockCustom.ascx.cs" Inherits="DynamicData_FieldTemplates_StockCustom" %>
<asp:Label runat="server" ID="Label1" Text="<%# FieldValueString %>" />
To change the background color based on the value, you need to override the OnDataBinding method. The code-behind file for my custom field template are shown below. The value of the field are available in the FieldValue property on the base class.
Code-behind:
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.Drawing;

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

    protected override void OnDataBinding(EventArgs e)
    {
        int currentValue = (int)FieldValue;
        if (currentValue < 10)
            Label1.BackColor = Color.Red;
        else if (currentValue < 20)
            Label1.BackColor = Color.Yellow;
    }
}
Now the last thing we need to do is to associate the field template with a data field. First we need to modify the partial class from part 2. The new code looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DatabaseModel;
using System.ComponentModel.DataAnnotations;

namespace DatabaseModel
{
    [ScaffoldTable(true)]
    [MetadataType(typeof(ProductsMetadata))]
    public partial class Products
    {
    }

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

    [ScaffoldTable(true)]
    public partial class Categories
    {
    }
}
The MetadataTypeAttribute indicates which class contains the necessary metadata for the table. The metadata class, contains a field which has the same name as the field in the database, the field itself has another attribute, the UIHintAttribute, which defines the name of the field template to used instead of the default.
When I view my products table in the web interface now, it indicates the stock count in red if the count is less than 10, yellow if the count is between 10 and 20, and default color if greater than 20.

1 comment:

  1. When setting up an MVC 3 application, the foreign keys that should allow drop down lists to select an item do not get rendered as drop downs, but as static inputs. This can be resolved by creating a custom display and view for that field.
    We will need to start by creating a custom partial view that will live in “~/Views/Shared/DisplayTemplates/UserGuid.cshtml”, and “~/Views/Shared/EditTemplates/UserGuid.cshtml”. The code for one is located below:
    @model Guid

    @{
    incMvcSite.Models.MvcSiteDB db = new incMvcSite.Models.MvcSiteDB();
    incMvcSite.Models.SecUser usr = db.SecUsers.Single(u => u.Guid == Model);
    }
    @usr.Display

    This is a display for template that will look up the item in the referenced table and display it. We also need an edit for template as follows:
    @model Guid
    @{
    incMvcSite.Models.MvcSiteDB db = new incMvcSite.Models.MvcSiteDB();
    SelectList items = new SelectList(db.SecUsers.OrderBy(i => i.Display).ToList(), "Guid", "Display", Model);
    }
    @Html.DropDownList("", items)

    The edit for template is implemented as a drop down list. Originally, we has used static HTML code, but the problem will appear of implementing a “prefix”. Static HTML code does get handled by HTML helpers, so it’s recommended that you use the HTML.DropDownList().
    To force the MVC framework to use the new Display and Edit for templates, we need to annote our model item an add the following line:
    [UIHint("UserGuid")]

    This will cause MVC to use the Display and Edit templates named “UserGuid”, which are just partial views.

    ReplyDelete