- The Anatomy of a FieldTemplate.
- Your First FieldTemplate.
- An Advanced FieldTemplate.
- A Second Advanced FieldTemplate.
- An Advanced FieldTemplate with a GridView ***UPDATED ***.
- An Advanced FieldTemplate with a DetailsView ***UPDATED ***.
- An Advanced FieldTemplate with a GridView/DetailsView Project ***UPDATED ***.
- ParentFormView with Templates ***UPDATED ***.
ParentFormView with Templates
This is a variant on the DetailsView/ParentDetails FieldTemplate the main difference is that here the FieldTemplate is based on a FormView not a DetailsView.
<asp:FormView ID="FormView1" runat="server" DataSourceID="FormDataSource" CssClass="detailstable" FieldHeaderStyle-CssClass="bold"> </asp:FormView> <asp:LinqDataSource ID="FormDataSource" runat="server" EnableDelete="true"> </asp:LinqDataSource>
Listing 1 – changes to the FieldTemplate
In Listing 1 the DetilsView has been changed to a FormView. All references to DetailsView1 and DetailsDataSource have been changed to FormView1 and FormDataSource in the ascx page and the code behind.
if (metaForeignKeyColumn != null) { // load Field Template String ddFolderVirtualPath = metaForeignKeyColumn.ParentTable.Model.DynamicDataFolderVirtualPath; String tableName = metaForeignKeyColumn.ParentTable.Name; String itemTemplate = ddFolderVirtualPath + "Templates/" + tableName + "Item.ascx"; String editItemTemplate = ddFolderVirtualPath + "Templates/" + tableName + "Edit.ascx"; //String insertItemTemplate = ddFolderVirtualPath + "Templates/" + tableName + "Insert.ascx"; String footerTemplate = ddFolderVirtualPath + "Templates/" + tableName + "Footer.ascx"; String headerTemplate = ddFolderVirtualPath + "Templates/" + tableName + "Header.ascx"; String pagerTemplate = ddFolderVirtualPath + "Templates/" + tableName + "Pager.ascx"; if (File.Exists(Server.MapPath(itemTemplate))) { FormView1.ItemTemplate = LoadTemplate(itemTemplate); // load any other field templates //TODO: if an operation is required but no template throw error if (File.Exists(Server.MapPath(editItemTemplate))) FormView1.EditItemTemplate = LoadTemplate(editItemTemplate); // because this is a parent no insert will be available //if (File.Exists(Server.MapPath(insertItemTemplate))) // FormView1.InsertItemTemplate = LoadTemplate(insertItemTemplate); if (File.Exists(Server.MapPath(footerTemplate))) FormView1.FooterTemplate = LoadTemplate(footerTemplate); if (File.Exists(Server.MapPath(headerTemplate))) FormView1.HeaderTemplate = LoadTemplate(headerTemplate); if (File.Exists(Server.MapPath(pagerTemplate))) FormView1.PagerTemplate = LoadTemplate(pagerTemplate); ...//CODE OMITTED FOR CLARITY } else { throw new InvalidOperationException("ParentFormView_Edit requires an item template."); } } else { // throw an error if set on column other than MetaChildrenColumns throw new InvalidOperationException("The GridView FieldTemplate can only be used with MetaChildrenColumns"); }
Listing 2 – changes to the code behind
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 FormDataSource.WhereParameters.Add(param); } } }
Listing 3 – OnDataBinding now handles multiple PK-FK relationships ***UPDATED 2008/09/24***
As you can see what has been done here add the facility to load templates for the FormView to allow this I’ve added a new folder to the DynamicData folder named Templates see Figure 1.
Figure 1 - Folder structure changes
In Listing 2 you can see that Model.DynamicDataFolderVirtualPath is accessed to retrieve the Dynamic Data virtual path (the default is ~/DynamicData/) so that the path to each Template can be constructed. The path to each of the possible Templates is constructed, but only the itemTemplate is required.
All you need to create a Template file is to add a new UserControl to the Templates folder and then customise it.
<p class="droplist"> <strong>Name: </strong><asp:DynamicControl ID="DynamicControl1" runat="server" DataField="CompanyName" ></asp:DynamicControl> <strong>Contact Name: </strong><asp:DynamicControl ID="DynamicControl2" runat="server" DataField="ContactName" ></asp:DynamicControl> </p> <p class="droplist"> <strong>Phone: </strong><asp:DynamicControl ID="DynamicControl3" runat="server" DataField="Phone" ></asp:DynamicControl> <strong>Fax: </strong><asp:DynamicControl ID="DynamicControl4" runat="server" DataField="Fax" ></asp:DynamicControl> </p>
Listing 4 – a very simple ItemTemplate
As you can see Listing 4 is a very simple field template but looks how you would expect the mark-up to look in Figure 2.
Figure 2 – the FieldTemplate at work
6 comments:
Inserting with DetailsView is something I was too eager to wait for so here's how I did it :)
DataSource:
asp:LinqDataSource
ID="DetailsDataSource"
runat="server"
EnableDelete="true" OnInserting="DetailsDataSource_Inserting"
asp:LinqDataSource
Code Behind:
public partial class ParentDetails_EditField : FieldTemplateUserControl
{
protected MetaTable parentTable;
protected MetaTable childTable;
public Object newObject;
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 = true;
}
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 = EnableInsert;
DetailsDataSource.EnableUpdate = EnableUpdate;
DetailsView1.AutoGenerateDeleteButton = EnableDelete;
// We don't need InsertButton
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)
{
var metaForeignKeyColumn = Column as MetaForeignKeyColumn;
if (metaForeignKeyColumn != null)
{
// setup the where clause
// support composite foreign keys
foreach (String fkName in metaForeignKeyColumn.ForeignKeyNames)
{
// get the current fk column
var fkColumn = childTable.GetColumn(fkName);
// setup parameter
var param = new Parameter();
param.Name = fkColumn.Name;
param.Type = fkColumn.TypeCode;
if (GetColumnValue(fkColumn) != null)
{
// get the value for this FK column
param.DefaultValue = GetColumnValue(fkColumn).ToString();
// add the where clause
DetailsDataSource.WhereParameters.Add(param);
}
else
{
if (EnableInsert == true)
{ DetailsView1.ChangeMode(DetailsViewMode.Insert); }
else
{ DetailsView1.Visible = false; }
}
}
}
base.OnDataBinding(e);
}
public void DetailsDataSource_Inserting(object sender, LinqDataSourceInsertEventArgs e)
{
newObject = e.NewObject;
e.Cancel = true;
}
protected override void ExtractValues(IOrderedDictionary dictionary)
{
if (IsPostBack && DetailsView1.CurrentMode == DetailsViewMode.Insert)
{
DetailsView1.InsertItem(true);
dictionary[Column.Name] = newObject;
}
}
}
Works for me. What do you think?
Hi Erkka, I still don't see (and this might be me being a bit dumb) why you would want to insert a new parent record?
Steve :D
i have download your demo code and find there is an err in insert with ParentDetails template
the message
System.NullReferenceException
param.DefaultValue = GetColumnValue(fkColumn).ToString();
Sorry about that, I've already said once before that that is not an option but I think it was in the Dynamic Data Forum So I'll post an update here also.
Please see the first comment here by Erkka and the sample code that may help.
Steve :D
Please see Update on this article here:
http://csharpbits.notaclue.net/2008/09/dynamic-data-and-field-templates_06.html
Post a Comment