- The Anatomy of a FieldTemplate.
- Your First FieldTemplate.
- An Advanced FieldTemplate.
- A Second Advanced FieldTemplate.
- An Advanced FieldTemplate with a GridView.
- An Advanced FieldTemplate with a DetailsView.
- An Advanced FieldTemplate with a GridView/DetailsView Project.
The Idea for this FieldTemplate came from Nigel Basel post on the Dynamic Data forum where he said he needed to have a GridView emended in another data control i.e. FormView so he could use the AjaxToolkit Tab control. So here it is with some explanation.
Files Required for Project
In this project we are going to convert a PageTemplate into a FieldTemplate so in your project you will need to copy the List.aspx and it’s code behind List.aspx.cs to the FieldTemplate folder. When copied rename the file to GridView_Edit.ascx and change the class name to GridView_EditField see Listings 1 & 2.
<%@ Page Language="C#" MasterPageFile="~/Site.master" CodeFile="List.aspx.cs" Inherits="List" %>
<%@ Control Language="C#" CodeFile="GridView_Edit.ascx.cs" Inherits="GridView_EditField" %>
Listing 1 – Changing the class name of the GridView_Edit.ascx file
public partial class List : System.Web.UI.Page {
...
}
to
public partial class GridView_EditField : FieldTemplateUserControl {Listing 2 - Changing the class name of the GridView_Edit.ascx.cs code behind file
...
}
Now in the GridView_Edit.ascx file trim out all the page relavent code:
i.e. remove the following tags (and their closing tags where applicable)
<asp:Content
<asp:UpdatePanel
<ContentTemplate>
<%@ Register src="~/DynamicData/Content/FilterUserControl.ascx" tagname="DynamicFilter" tagprefix="asp" %>
<asp:DynamicDataManager runat="server" ID="DynamicDataManager1" AutoLoadForeignKeys="true" />
Also remove from the GridView the columns tags and everything in them, and then add the following properties to the GridView:
AutoGenerateColumns="true" AutoGenerateDeleteButton="true" AutoGenerateEditButton="true"
and you should end up with something like Listing 3.
<%@ Control Language="C#" CodeFile="GridView_Edit.ascx.cs" Inherits="GridView_EditField" %> <%@ Register src="~/DynamicData/Content/GridViewPager.ascx" tagname="GridViewPager" tagprefix="asp" %> <asp:DynamicDataManager runat="server" ID="DynamicDataManager1" AutoLoadForeignKeys="true" /> <asp:ScriptManagerProxy runat="server" ID="ScriptManagerProxy1" /> <asp:ValidationSummary runat="server" ID="ValidationSummary1" EnableClientScript="true" HeaderText="List of validation errors" /> <asp:DynamicValidator runat="server" ID="GridViewValidator" ControlToValidate="GridView1" Display="None" /> <asp:GridView runat="server" ID="GridView1" DataSourceID="GridDataSource" AllowPaging="True" AllowSorting="True" AutoGenerateColumns="true" AutoGenerateDeleteButton="true" AutoGenerateEditButton="true" CssClass="gridview"> <PagerStyle CssClass="footer"/> <PagerTemplate> <asp:GridViewPager runat="server" /> </PagerTemplate> <EmptyDataTemplate> There are currently no items in this table. </EmptyDataTemplate> </asp:GridView> <asp:LinqDataSource runat="server" ID="GridDataSource" EnableDelete="true"> </asp:LinqDataSource>
Listing 3 – the finished GridView_Edit.ascx file
Now we’ll trim down the GridView_Edit.ascx.cs, the first things to remove are the following methods and event handlers:
protected void Page_Load(object sender, EventArgs e)
protected void OnFilterSelectedIndexChanged(object sender, EventArgs e)
This will leave us with just the Page_Init to fill in see Listing 4 for it.
protected void Page_Init(object sender, EventArgs e) { var metaChildColumn = Column as MetaChildrenColumn; var attribute = Column.Attributes.OfType<ShowColumnsAttribute>().SingleOrDefault(); if (attribute != null) { if (!attribute.EnableDelete) EnableDelete = false; if (!attribute.EnableUpdate) EnableUpdate = false; if (attribute.DisplayColumns.Length > 0) DisplayColumns = attribute.DisplayColumns; } var metaForeignKeyColumn = metaChildColumn.ColumnInOtherTable as MetaForeignKeyColumn; if (metaChildColumn != null && metaForeignKeyColumn != null) { GridDataSource.ContextTypeName = metaChildColumn.ChildTable.DataContextType.Name; GridDataSource.TableName = metaChildColumn.ChildTable.Name; // enable update, delete and insert GridDataSource.EnableDelete = EnableDelete; GridDataSource.EnableInsert = EnableInsert; GridDataSource.EnableUpdate = EnableUpdate; GridView1.AutoGenerateDeleteButton = EnableDelete; GridView1.AutoGenerateEditButton = EnableUpdate; // get an instance of the MetaTable table = GridDataSource.GetTable(); // Generate the columns as we can't rely on // DynamicDataManager to do it for us. GridView1.ColumnsGenerator = new FieldTemplateRowGenerator(table, DisplayColumns); // setup the GridView's DataKeys String[] keys = new String[metaChildColumn.ChildTable.PrimaryKeyColumns.Count]; int i = 0; foreach (var keyColumn in metaChildColumn.ChildTable.PrimaryKeyColumns) { keys[i] = keyColumn.Name; i++; } GridView1.DataKeyNames = keys; GridDataSource.AutoGenerateWhereClause = true; } else { // throw an error if set on column other than MetaChildrenColumns throw new InvalidOperationException("The GridView FieldTemplate can only be used with MetaChildrenColumns"); } }
Listing 4 – the Page_Init ***UPDATED 2008/09/24***
protected override void OnDataBinding(EventArgs e) { base.OnDataBinding(e); var metaChildrenColumn = Column as MetaChildrenColumn; var metaForeignKeyColumn = metaChildrenColumn.ColumnInOtherTable as MetaForeignKeyColumn; // get the association attributes associated with MetaChildrenColumns var association = metaChildrenColumn.Attributes. OfType<System.Data.Linq.Mapping.AssociationAttribute>().FirstOrDefault(); if (metaForeignKeyColumn != null && association != null) { // get keys ThisKey and OtherKey into Pairs var keys = new Dictionary<String, String>(); var seperator = new char[] { ',' }; var thisKeys = association.ThisKey.Split(seperator); var otherKeys = association.OtherKey.Split(seperator); for (int i = 0; i < thisKeys.Length; i++) { keys.Add(otherKeys[i], thisKeys[i]); } // setup the where clause // support composite foreign keys foreach (String fkName in metaForeignKeyColumn.ForeignKeyNames) { // get the current pk column var fkColumn = metaChildrenColumn.ChildTable.GetColumn(fkName); // setup parameter var param = new Parameter(); param.Name = fkColumn.Name; param.Type = fkColumn.TypeCode; // get the PK value for this FK column using the fk pk pairs param.DefaultValue = Request.QueryString[keys[fkName]]; // add the where clause GridDataSource.WhereParameters.Add(param); } } // doing the work of this above because we can't // set the DynamicDataManager table or where values //DynamicDataManager1.RegisterControl(GridView1, false); }
Listing 4a – OnDataBinding event handler ***ADDED 2008/09/24***
And now we will need to implement the GridViewColumnGenerator to fill in the rows as the DynamicDataManager would have done.
public class GridViewColumnGenerator : IAutoFieldGenerator { protected MetaTable _table; public GridViewColumnGenerator(MetaTable table) { _table = table; } public ICollection GenerateFields(Control control) { List<DynamicField> oFields = new List<DynamicField>(); foreach (var column in _table.Columns) { // carry on the loop at the next column // if scaffold table is set to false or DenyRead if (!column.Scaffold) continue; DynamicField field = new DynamicField(); field.DataField = column.Name; oFields.Add(field); } return oFields; } }Listing 5 – the GridViewColumnGenerator class
In Listing 5 we have the GridViewColumnGenerator class which you can just tag onto the end the GridView_Edit.ascx.cs file as it is only used here.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class GridViewTemplateAttribute :Attribute { public String ForeignKeyColumn { get; private set; } public GridViewTemplateAttribute(string foreignKeyColumn) { ForeignKeyColumn = foreignKeyColumn; } }
Listing 6 – GridViewTemplateAttribute for the above ***UPDATE 2008/08/08 *** :D
Figure 1 GridView_Edit FieldTemplate in action
[UIHint("GridView")] public object Order_Details { get; set; }
Listing 7 – Metadata
Until next time ![]()