- 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.
In this addition the FieldTemplates series I was asked to produce one that looked back up the relationship with the parent table to get some or all of the properties.
The basis for this FieldTemplate is the previous GridView_Edit FieldTemplate. This time I’m just going to post the files and then discuss the alterations, so here goes:
<%@ Control Language="C#" CodeFile="DetailsView_Edit.ascx.cs" Inherits="DetailsView_EditField" %> <asp:ValidationSummary ID="ValidationSummary1" D="ValidationSummary1" runat="server" EnableClientScript="true" HeaderText="List of validation errors" /> <asp:DynamicValidator runat="server" ID="DetailsViewValidator" ControlToValidate="DetailsView1" Display="None" /> <asp:DetailsView ID="DetailsView1" runat="server" DataSourceID="DetailsDataSource" CssClass="detailstable" AutoGenerateDeleteButton="true" AutoGenerateEditButton="true" AutoGenerateInsertButton="true" FieldHeaderStyle-CssClass="bold"> </asp:DetailsView> <asp:LinqDataSource ID="DetailsDataSource" runat="server" EnableDelete="true"> </asp:LinqDataSource>
Listing 1 - DetailsView_Edit.ascx
using System; using System.Data; using System.Configuration; using System.Collections; using System.Collections.Generic; 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; public partial class ParentDetails_EditField : FieldTemplateUserControl { protected MetaTable parentTable; protected MetaTable childTable; public Boolean EnableDelete { get; set; } //public Boolean EnableInsert { get; set; } public Boolean EnableUpdate { get; set; } public String[] DisplayColumns { get; set; } public ParentDetails_EditField() { // set default values EnableDelete = true; EnableUpdate = true; //EnableInsert = false; } protected void Page_Init(object sender, EventArgs e) { var attribute = Column.Attributes.OfType<ShowColumnsAttribute>().SingleOrDefault(); if (attribute != null) { if (!attribute.EnableDelete) EnableDelete = false; if (!attribute.EnableUpdate) EnableUpdate = false; //if (!attribute.EnableInsert) // EnableInsert = false; if (attribute.DisplayColumns.Length > 0) DisplayColumns = attribute.DisplayColumns; } var metaForeignKeyColumn = Column as MetaForeignKeyColumn; if (metaForeignKeyColumn != null) { childTable = metaForeignKeyColumn.Table; // setup data source DetailsDataSource.ContextTypeName = metaForeignKeyColumn.ParentTable.DataContextType.Name; DetailsDataSource.TableName = metaForeignKeyColumn.ParentTable.Name; // enable update, delete and insert DetailsDataSource.EnableDelete = EnableDelete; DetailsDataSource.EnableInsert = false; // EnableInsert; DetailsDataSource.EnableUpdate = EnableUpdate; DetailsView1.AutoGenerateDeleteButton = EnableDelete; DetailsView1.AutoGenerateInsertButton = false; // EnableInsert; DetailsView1.AutoGenerateEditButton = EnableUpdate; // get an instance of the MetaTable parentTable = DetailsDataSource.GetTable(); // Generate the columns as we can't rely on // DynamicDataManager to do it for us. DetailsView1.RowsGenerator = new FieldTemplateRowGenerator(parentTable, DisplayColumns); // setup the GridView's DataKeys String[] keys = new String[metaForeignKeyColumn.ParentTable.PrimaryKeyColumns.Count]; int i = 0; foreach (var keyColumn in metaForeignKeyColumn.ParentTable.PrimaryKeyColumns) { keys[i] = keyColumn.Name; i++; } DetailsView1.DataKeyNames = keys; // enable AutoGenerateWhereClause so that the WHERE // clause is generated from the parameters collection DetailsDataSource.AutoGenerateWhereClause = true; // doing the work of this above because we can't // set the DynamicDataManager table or where values //DynamicDataManager1.RegisterControl(DetailsView1, false); } else { // throw an error if set on column other than MetaChildrenColumns throw new InvalidOperationException("The GridView FieldTemplate can only be used with MetaChildrenColumns"); } } protected override void OnDataBinding(EventArgs e) { base.OnDataBinding(e); // get the fk column var metaForeignKeyColumn = Column as MetaForeignKeyColumn; // get the association attributes associated with MetaChildrenColumns var association = metaForeignKeyColumn.Attributes. OfType<System.Data.Linq.Mapping.AssociationAttribute>().FirstOrDefault(); if (metaForeignKeyColumn != null && association != null) { // get keys ThisKey and OtherKey into dictionary 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(thisKeys[i], otherKeys[i]); } // setup the where clause // support composite foreign keys foreach (String fkName in metaForeignKeyColumn.ForeignKeyNames) { // get the current FK column var fkColumn = metaForeignKeyColumn.Table.GetColumn(fkName); // get the current PK column var pkColumn = metaForeignKeyColumn.ParentTable.GetColumn(keys[fkName]); // setup parameter var param = new Parameter(); param.Name = pkColumn.Name; param.Type = pkColumn.TypeCode; // get the value for this FK column param.DefaultValue = GetColumnValue(fkColumn).ToString(); // add the where clause DetailsDataSource.WhereParameters.Add(param); } } } }
Listing 2 - DetailsView_Edit.ascx.cs ***UPDATED 2008/09/24***
As you can see from examining the above file the main thing is the change of the GridView to DetailsView, however you will notice that part of the code has been remove from the Page_Init the OnDataBinding. This is because we are coming at this from the other end the value for the ForeignKey to link the DetailsView to the parent control is no longer in the Request.QueryString and so we have to extract it in the OnDataBinding event handler as access to column values is not valid untill OnDataBinding.
You will also I’ve added some properties to enable features of the DetailsView either declaratively or via attributes:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class DetailsViewTemplateAttribute : Attribute { public DetailsViewTemplateAttribute(params String[] displayColumns) { DisplayColumns = displayColumns; } public String[] DisplayColumns { get; set; } public Boolean EnableDelete { get; set; } public Boolean EnableInsert { get; set; } public Boolean EnableUpdate { get; set; } }
Listing 3 - DetailsViewTemplateAttribute.cs
[MetadataType(typeof(OrderMD))] public partial class Order { public class OrderMD { [UIHint("DetailsView")] [DetailsViewTemplate ( "Title", "FirstName", "LastName", "Region", "Extension", EnableDelete=false, EnableUpdate=false, EnableInsert=false )] public object Employee { get; set; } } }
Listing 4 – Northwind Partials and Metadata
As you can see from Listing 3 and Listing 4 you are able to enable or disable Update, Delete or Insert on the DetailsView FieldTemplate and also specify which columns you want to appear in the particular instance.
public class DetailsViewRowGenerator : IAutoFieldGenerator { protected MetaTable _table; protected String[] _displayColumns; public DetailsViewRowGenerator(MetaTable table, String[] displayColumns) { _table = table; _displayColumns = displayColumns; } 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; if (_displayColumns != null && !_displayColumns.Contains(column.Name)) continue; DynamicField f = new DynamicField(); f.DataField = column.Name; oFields.Add(f); } return oFields; } }
Listing 5 – DetailsViewRowGenerator (tagged on to the end of the DetailsView_Edit.ascx.cs file)
And finally the DetailsViewRowGenerator wether to show some or all of the columns in the parent table, the most important line here is:
if (_displayColumns != null && !_displayColumns.Contains(column.Name)) continue;
which test first to see of any columns have been specified and if so the check to see if the current column is not present in the list and then drops the column appropriately, otherwise the IAutoFieldGenerator implementation is pretty much the same as the GridView FieldTemplate.
See if working below:
Figure 1 - DetailsView_Edit FieldTemplate at work
No comments:
Post a Comment