Code” and an order value of 1 to ensure it is always displayed as the first field:
c#
[Display(Name="Product Code", Order=1)]
public object ProductNumber;
Code snippet Product.cs
498 .
chaPter 23 dynAmic dATA
Vb
<Display(Name:="Product Code", Order:=1)> _
Public ProductNumber As Object
Code snippet Product.vb
Figure 23-8 shows how these display formatting changes are rendered by Dynamic Data.
fiGure 23-8
custoMizinG the Presentation
Chances are the way that Dynamic Data renders a web site by default will not be exactly what
you require. The previous section demonstrated how many aspects of the data model could be
customized to control how the database tables and fields are rendered. However, limitations exist as
to what can be achieved simply by customizing the data model. Fortunately, Dynamic Data uses a
rich template system that is fully customizable and allows you complete control over the UI.
The Dynamic Data template files are stored under a number of subfolders in the DynamicData
folder, which is in the root of the web application. Following the Convention over Configuration
principle, these template files do not need to be manually registered with Dynamic Data. Instead,
each different type of template should be stored in a specific folder and the framework will use the
location, as well as the template filename, to determine when to load it at run time.
Customizing the Presentation .
499
Page templates
Page templates are used to provide the default rendering of a database table. The master page
templates are stored in the DynamicData\PageTemplates folder. Dynamic Data ships with the
following five page templates for viewing and editing data:
.
Details.aspx: Renders a read-only view of an existing entry from a table.
.
Edit.aspx: Displays an editable view of an existing entry from a table.
.
Insert.aspx: Displays a view that allows users to add a new entry to a table.
.
List.aspx: Renders an entire table using a grid view with support for paging and sorting.
.
ListDetails.aspx: Used when Dynamic Data is configured with the combined-page mode,
where the Detail, Edit, Insert, and List tasks are performed by the same page. This mode
can be enabled by following the comment instructions in the Global.asax file.
You can edit any of these default page templates if there are changes that you would like to affect
all tables by default. You can also override the default page templates by creating a set of custom
templates for a table. Custom pages templates are stored under the DynamicData\CustomPages
folder.
In the AdventureWorksLT database, the SalesOrderHeader table is a good candidate for a custom
page template. Before creating the template, you will need to enable scaffolding for this table. Create
a new data model partial class for the SalesOrderHeader table and enable scaffolding as shown in
the following listing:
c#
using System.ComponentModel.DataAnnotations;
namespace DynDataWebApp
{
[ScaffoldTable(true)]
public partial class SalesOrderHeader
{
}
}
Code snippet SalesOrderHeader.cs
Vb
Imports System.ComponentModel.DataAnnotations
<ScaffoldTable(True)> _
Partial Public Class SalesOrderHeader
End Class
Code snippet SalesOrderHeader.vb
500 .
chaPter 23 dynAmic dATA
Next, create a subfolder called SalesOrderHeaders under the DynamicData\CustomPages folder.
This folder will contain the custom templates for the SalesOrderHeader table. Copy the existing
List.aspx template from the DynamicData\PageTemplates folder to the DynamicData\
CustomPages\SalesOrderHeaders folder.
The folder name for custom page templates should generally be named with the
plural form of the table name. The exception to this is if the data model is using
the ADO.NET Entity Framework version 3.5, or if the default option Pluralize
or Singularize Generated Object Names has been changed. In this case the folder
name should have the same name as the table.
Because the template was copied, and therefore a duplicate class was created, your application will
no longer compile. The easiest way to fix this is to change the namespace to any unique value in
both the markup and code-behind files of the new template, as shown in the following code:
c#
< %@ Page Language="C#" MasterPageFile="~/Site.master" CodeBehind="List.aspx.cs"
Inherits="DynDataWebApp._SalesOrderHeaders.List" % >
Code snippet DynamicData\CustomPages\SalesOrderHeaders\List.aspx
namespace DynDataWebApp._SalesOrderHeaders
{
public partial class List : System.Web.UI.Page
{
// Code snipped
}
}
Code snippet DynamicData\CustomPages\SalesOrderHeaders\List.aspx.cs
Vb
< %@ Page Language="VB" MasterPageFile="~/Site.master" CodeBehind="List.aspx.vb"
Inherits="DynDataWebApp._SalesOrderHeader.List" % >
Code snippet DynamicData\CustomPages\SalesOrderHeaders\List.aspx
Namespace _SalesOrderHeader
Class List
Inherits Page
’ Code Snipped
End Class
End Namespace
Code snippet DynamicData\CustomPages\SalesOrderHeaders\List.aspx.vb
Customizing the Presentation .
501
You can now customize the template in whatever manner you wish. For example, you may want to
reduce the number of columns that appear in the List view, while still ensuring that all data fields
appear in the Insert and Edit views. This degree of customization is only possible by creating a
table-specific page template.
Make this change by locating the GridView control in List.aspx. Disable the automatic rendering of
all data fields by adding the property AutoGenerateColumns=”False”. Then, manually specify the fields
that you want to display by adding a set of DynamicField controls as shown in the following code:
<asp:GridView ID=”GridView1” runat="server" DataSourceID=”GridDataSource”
EnablePersistedSelection="True" AllowPaging="True"
AllowSorting="True" CssClass="DDGridView"
AutoGenerateColumns="False" RowStyle-CssClass="td"
HeaderStyle-CssClass="th" CellPadding="6">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:DynamicHyperLink runat="server" Text="Details" />
</ItemTemplate>
</asp:TemplateField>
<asp:DynamicField DataField=”AccountNumber” HeaderText=”Account No” />
<asp:DynamicField DataField=”PurchaseOrderNumber” HeaderText=”PO Number” />
<asp:DynamicField DataField=”OrderDate” DataFormatString=”{0:d-MMM-yyyy}”
HeaderText=”Order Date” />
<asp:DynamicField DataField=”ShipDate” DataFormatString=”{0:d-MMM-yyyy}”
HeaderText=”Ship Date” />
<asp:DynamicField DataField=”SubTotal” DataFormatString=”{0:c}”
HeaderText=”Sub Total” />
<asp:DynamicField DataField=”TaxAmt” DataFormatString=”{0:c}”
HeaderText=”Tax Amount” />
<asp:DynamicField DataField=”Freight” DataFormatString=”{0:c}”
HeaderText=”Freight” />
</Columns>
<HeaderStyle CssClass=”th” />
<PagerStyle CssClass=”DDFooter”/>
<PagerTemplate>
<asp:GridViewPager runat=”server” />
</PagerTemplate>
<EmptyDataTemplate>
There are currently no items in this table.
</EmptyDataTemplate>
<RowStyle CssClass=”td” />
</asp:GridView>
Code snippet DynamicData\CustomPages\SalesOrderHeaders\List.aspx
Figure 23-9 shows the customized List view of the SalesOrderHeader table with this reduced set
of columns.
502 .
chaPter 23 dynAmic dATA
fiGure 23-9
field templates
Field templates are used to render the user interface for individual data fields. There are both view
and edit field templates. The field templates are named according to the name of the data type,
with the suffix _Edit for the edit view. For example, the view template for a Text field is called
Text.ascx, and renders the field using an ASP.NET
Literal control. The corresponding edit template is called
Text_Edit.ascx, and it renders the field using an ASP.NET
TextBox control. The edit template also contains several
validation controls, which are enabled as required and handle
any validation exceptions thrown by the data model.
Dynamic Data ships with a large number of field templates,
as shown in Figure 23-10. As with page templates, you can
customize the default field templates or create new ones. All
field templates, including any new templates that you create,
are stored in the DynamicData\FieldTemplates
folder.
Several date fields in the SalesOrderHeader table of the
AdventureWorksLT database are rendered with both the date
and time, even though the time portion is not relevant.
The DateTime field template in Dynamic Data displays a
simple TextBox control for its Edit view. If the data field only
requires the date to be entered, and not the time, it would be
nice to display a Calendar control instead of a TextBox.
fiGure 23-10
Customizing the Presentation .
503
Begin by creating a copy of the DateTime.ascx template and renaming it to DateCalendar.ascx.
Then open both the markup file and the code-behind file for DateCalendar.ascx and rename the
class from DateTimeField to DateCalendarField as shown in the following code:
c#
<%@ Control Language="C#" CodeBehind="DateCalendar.ascx.cs"
Inherits="DynDataWebApp.DateCalendarField" %>
Code snippet DynamicData\FieldTemplates\DateCalendar.ascx
namespace DynDataWebApp
{
public partial class DateCalendarField : FieldTemplateUserControl
{
// Code snipped
}
}
Code snippet DynamicData\FieldTemplates\DateCalendar.ascx.cs
Vb
<%@ Control Language="VB" CodeBehind="DateCalendar.ascx.vb"
Inherits="DynDataWebApp.DateCalendarField" %>
Code snippet DynamicData\FieldTemplates\DateCalendar.ascx
Class DateCalendarField
Inherits FieldTemplateUserControl
’ Code Snipped
End Class
Code snippet DynamicData\FieldTemplates\DateCalendar.ascx.vb
Next, create a copy of the DateTime_Edit.ascx template and rename it to DateCalendar_Edit
.ascx. As before, open both the markup file and the code-behind file for DateCalendar_Edit.ascx
and rename the class from DateTime_EditField to DateCalendar_EditField. The following code
shows how it should look once renamed:
c#
<%@ Control Language="C#" CodeBehind="DateCalendar_Edit.ascx.cs"
Inherits="DynDataWebApp.DateCalendar_EditField" %>
Code snippet DynamicData\FieldTemplates\DateCalendar_Edit.ascx
namespace DynDataWebApp
{
public partial class DateCalendar_EditField : FieldTemplateUserControl
504 .
chaPter 23 dynAmic dATA
}
{
}
// Code snippedCode snippet DynamicData\FieldTemplates\DateCalendar_Edit.ascx.cs
Vb
<%@ Control Language="VB" CodeBehind="DateCalendar_Edit.ascx.vb"
Inherits="DynDataWebApp.DateCalendar_EditField" %>
Code snippet DynamicData\FieldTemplates\DateCalendar.ascx
Class DateCalendar_EditField
Inherits FieldTemplateUserControl
’ Code Snipped
End Class
Code snippet DynamicData\FieldTemplates\DateCalendar.ascx.vb
At this point you could replace the TextBox control in the DateCalendar_Edit.ascx file with a
standard Calendar web server control. However, this would require a number of changes in the
code-behind file to get it working with this new type of control. A far easier solution is to use
the Calendar control from the AJAX Control Toolkit. This is a Control Extender, which means
it attaches to an existing TextBox on a web page and provides new client-side functionality.
You can find more information about Control Extenders and the AJAX Control Toolkit in
Chapter 20.
You can download the AJAX Control Toolkit from http://ajaxcontroltoolkit.codeplex.com/.
Follow the instructions in Chapter 20 to add the controls in the AJAX Control Toolkit to the
Visual Studio Toolbox. Once this has been done, add a CalendarExtender control onto the
DateCalendar_Edit.ascx template. Then set the TargetControlID property and Format
property as shown in the following code:
<cc1:CalendarExtender ID=”CalendarExtender1” TargetControlID=”TextBox1”
Format="d-MMM-yyyy" runat="server">
</cc1:CalendarExtender>
Code snippet DynamicData\FieldTemplates\DateCalendar_Edit.ascx
The final step is to associate some fields in the data model with the new field templates. In this
example, the OrderDate, ShipDate, and DueDate fields from the SalesOrderHeader table should be
associated. Modify the SalesOrderHeader partial class and create a metadata class, as described
earlier in the chapter. The UIHint attribute is used to associate the specified fields with the custom
field template, as shown in the following code:
Customizing the Presentation .
505
c#
namespace DynDataWebApp
{
[ScaffoldTable(true)]
[MetadataType(typeof(SalesOrderHeaderMetadata))]
public partial class SalesOrderHeader
{
}
public class SalesOrderHeaderMetadata
{
[DisplayFormat(DataFormatString = “{0:dd-MMM-yyyy}”,
ApplyFormatInEditMode = true)]
[UIHint(“DateCalendar”)]
public object OrderDate;
[DisplayFormat(DataFormatString = “{0:dd-MMM-yyyy}”,
ApplyFormatInEditMode = true)]
[UIHint("DateCalendar")]
public object DueDate;
[DisplayFormat(DataFormatString = "{0:dd-MMM-yyyy}",
ApplyFormatInEditMode = true)]
[UIHint("DateCalendar")]
public object ShipDate;
}
}
Code snippet SalesOrderHeader.cs
Vb
<ScaffoldTable(True)> _
<MetadataType(GetType(SalesOrderHeaderMetadata))> _