Dynamic Data Custom Pages: Dynamic/Templated FromView (original) (raw)
These articles are now under the title of Custom PageTemplates:
- Custom PageTemplates Part 1 - Custom PageTemplates with Ajax Control Toolkit Tabs
- Custom PageTemplates Part 2 - A variation of Part 1 with the Details and SubGrid in Tabs
- Custom PageTemplates Part 3 - Dynamic/Templated Grid with Insert (Using ListView)
- Custom PageTemplates Part 4 - Dynamic/Templated FromView
Continuing on from Part 3 the same techniques will be applied to the FormView, making the FormView Dynamic and also having the facility to dynamically load user defined Templates at runtime.
Fugure 1 – FormViewPage in action
Note the Edit, Delete and New links these all act on this page and do not redirect to other pages.
Altering the Routing
Replace the default route Listing 1
routes.Add(new DynamicDataRoute("{table}/{action}.aspx") { Constraints = new RouteValueDictionary(new { action = "List|Edit|Details|Insert" }), Model = model });
Listing 1 – original routing in Global.asax
With the new routes in Listing 2
routes.Add(new DynamicDataRoute("{table}/{action}.aspx") { Constraints = new RouteValueDictionary(new { action = "List" }), Model = model });
routes.Add(new DynamicDataRoute("{table}/{action}/FormViewPage.aspx") { Constraints = new RouteValueDictionary(new { action = "Edit|Details|Insert" }), ViewName = "FormViewPage", Model = model, });
Listing 2 – changes to routing in global.asax
Note that the action {table}/{action}/FormViewPage.aspx as a prefix to the page name, this will be used to identify the pages mode (Edit, Delete and Insert)
The FromViewPage
<%@ Page Language="C#" MasterPageFile="~/Site.master" CodeFile="FormViewPage.aspx.cs" Inherits="FormViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:DynamicDataManager
ID="DynamicDataManager1"
runat="server"
AutoLoadForeignKeys="true" />
<h2><%= table.DisplayName %></h2>
<asp:ScriptManagerProxy
runat="server"
ID="ScriptManagerProxy1" />
<asp:UpdatePanel
ID="UpdatePanel1"
runat="server">
<ContentTemplate>
<asp:ValidationSummary
ID="ValidationSummary_Edit"
EnableClientScript="true"
HeaderText="List of validation errors"
ValidationGroup="FormView_Edit"
runat="server" />
<asp:ValidationSummary
ID="ValidationSummary_Insert"
EnableClientScript="true"
HeaderText="List of validation errors"
ValidationGroup="FormView_Insert"
runat="server" />
<asp:DynamicValidator
ID="Validator_Edit"
Display="None"
ValidationGroup="FormView_Edit"
ControlToValidate="FormView1"
runat="server" />
<asp:DynamicValidator
ID="Validator_Insert"
Display="None"
ValidationGroup="FormView_Insert"
ControlToValidate="FormView1"
runat="server" />
<asp:FormView
ID="FormView1"
DataSourceID="FormViewDataSource"
OnItemDeleted="FormView_ItemDeleted"
runat="server" onitemcommand="FormView1_ItemCommand"
oniteminserted="FormView1_ItemInserted">
</asp:FormView>
<asp:LinqDataSource
ID="FormViewDataSource"
AutoGenerateWhereClause="true"
EnableDelete="true"
EnableUpdate="true"
EnableInsert="true"
runat="server">
<WhereParameters>
<asp:DynamicQueryStringParameter />
</WhereParameters>
</asp:LinqDataSource>
<br />
<div class="bottomhyperlink">
<asp:HyperLink
ID="ListHyperLink"
runat="server">Show all items</asp:HyperLink>
</div>
</ContentTemplate>
</asp:UpdatePanel>
using System; using System.IO; using System.Web.DynamicData; using System.Web.UI.WebControls;
public partial class FormViewPage : System.Web.UI.Page { protected MetaTable table;
protected void Page_Init(object sender, EventArgs e)
{
DynamicDataManager1.RegisterControl(FormView1);
table = FormViewDataSource.GetTable();
// supported templates
// get tamplate path
var formViewTemplatePath = table.Model.DynamicDataFolderVirtualPath + "Templates/FormViewPage/" + table.Name + "/";
// load user templates if they exist
if (File.Exists(Server.MapPath(formViewTemplatePath + "ItemTemplate.ascx")))
FormView1.ItemTemplate = LoadTemplate(formViewTemplatePath + "ItemTemplate.ascx");
else
FormView1.ItemTemplate = new FromViewPageRowGenerator(table, FormViewTemplateType.ItemTemplate);
// load user templates if they exist
if (File.Exists(Server.MapPath(formViewTemplatePath + "EditItemTemplate.ascx")))
FormView1.EditItemTemplate = LoadTemplate(formViewTemplatePath + "EditItemTemplate.ascx");
else
FormView1.EditItemTemplate = new FromViewPageRowGenerator(table, FormViewTemplateType.EditItemTemplate);
// load user templates if they exist
if (File.Exists(Server.MapPath(formViewTemplatePath + "InsertItemTemplate.ascx")))
FormView1.InsertItemTemplate = LoadTemplate(formViewTemplatePath + "InsertItemTemplate.ascx");
else
FormView1.InsertItemTemplate = new FromViewPageRowGenerator(table, FormViewTemplateType.InsertItemTemplate);
// load user templates if they exist
if (File.Exists(Server.MapPath(formViewTemplatePath + "EmptyDataTemplate.ascx")))
FormView1.EmptyDataTemplate = LoadTemplate(formViewTemplatePath + "EmptyDataTemplate.ascx");
else
FormView1.EmptyDataTemplate = new FromViewPageRowGenerator(table, FormViewTemplateType.EmptyDataTemplate);
//FormView1.FooterTemplate = null;
//FormView1.HeaderTemplate = null;
//FormView1.PagerTemplate = null;
}
protected void Page_Load(object sender, EventArgs e)
{
table = FormViewDataSource.GetTable();
Title = table.DisplayName;
// I don't know if this is
// the best way to do this
// get the FormViews mode from the url
String path = Request.Path.Substring(0, Request.Path.LastIndexOf("/"));
var qs = path.Substring(path.LastIndexOf("/") + 1, path.Length - (path.LastIndexOf("/") + 1));
switch (qs)
{
case "Details":
FormView1.DefaultMode = FormViewMode.ReadOnly;
break;
case "Edit":
FormView1.DefaultMode = FormViewMode.Edit;
break;
case "Insert":
FormView1.DefaultMode = FormViewMode.Insert;
break;
default:
break;
}
ListHyperLink.NavigateUrl = table.ListActionPath;
}
protected void FormView_ItemDeleted(object sender, FormViewDeletedEventArgs e)
{
if (e.Exception == null || e.ExceptionHandled)
{
Response.Redirect(table.ListActionPath);
}
}
protected void FormView1_ItemCommand(object sender, FormViewCommandEventArgs e)
{
if (e.CommandName == "Cancel")
{
// option 1 go back to list
//Response.Redirect(table.ListActionPath);
// option 2 return to Normal ReadOnly
FormView1.DefaultMode = FormViewMode.ReadOnly;
}
}
protected void FormView1_ItemInserted(object sender, FormViewInsertedEventArgs e)
{
// option 1 go back to list
Response.Redirect(table.ListActionPath);
// option 2 return to Normal ReadOnly
// note sure how to get this working at the moment
//Response.Redirect(table.GetActionPath("Details", ???));
}
}
Listign 4 – FormViewPage.aspx.cs
From Listings 3 and 4 you can see that this is a basic page like Details.aspx but with a FormView. The real magic goes on in the Page_Init event handler where the Templates are either loaded or dynamically generated and also in the Page_Load event handler where the default mode is detected via the URL.
Note: I’m not sure this is the best or most elegant way of doing this, but this will do for now and we will have a look at this again at a later date.
The RowGenerator
This will generate a table based FormView similar to the Details.aspx generated by the wizard (which at the time of writing was still in preview) the dynamically generated layout can be overridden be defining UserControl custom Templates in the ~/DynamicData/Templates/FormViewPage//.
///
///
public FromViewPageRowGenerator(MetaTable table, FormViewTemplateType type)
{
_table = table;
_type = type;
}
#endregion
public void InstantiateIn(Control container)
{
IParserAccessor accessor = container;
// get all the all scaffold columns
// except Long String Columns
// SubGridViewsAttribute and column order
var columnDetails = from c in _table.Columns
where c.Scaffold // && !c.IsLongString
select new ListViewColumn()
{
Column = c,
SubGridMetaData = c.Attributes.OfType<SubGridViewsAttribute>().FirstOrDefault(),
Order = c.Attributes.OfType<SubGridViewsAttribute>().FirstOrDefault() != null
&& c.Attributes.OfType<SubGridViewsAttribute>().FirstOrDefault().Order > 0
? c.Attributes.OfType<SubGridViewsAttribute>().FirstOrDefault().Order
: int.MaxValue,
};
// sort according to Order first and Column Name second
// Note: if SubGridViewsAttribute is null or the attribute
// has no value for Order then just sort but column display name
columnDetails = from sg in columnDetails
orderby sg.Order, sg.Column.DisplayName
select sg;
// call the appropriate template generator
switch (_type)
{
case FormViewTemplateType.ItemTemplate:
GetItemTemplate(accessor, columnDetails, DataBoundControlMode.ReadOnly);
break;
case FormViewTemplateType.EditItemTemplate:
GetItemTemplate(accessor, columnDetails, DataBoundControlMode.Edit);
break;
case FormViewTemplateType.InsertItemTemplate:
GetItemTemplate(accessor, columnDetails, DataBoundControlMode.Insert);
break;
case FormViewTemplateType.EmptyDataTemplate:
GetEmptyDataTemplate(accessor, columnDetails);
break;
case FormViewTemplateType.HeaderTemplate:
GetHeaderTemplate(accessor, columnDetails);
break;
case FormViewTemplateType.FooterTemplate:
GetFooterTemplate(accessor, columnDetails);
break;
case FormViewTemplateType.PagerTemplate:
GetPagerTemplate(accessor, columnDetails);
break;
default:
break;
}
}
private void GetItemTemplate(IParserAccessor accessor, IEnumerable<ListViewColumn> columnDetails, DataBoundControlMode templateMode)
{
// create new table
var table = new HtmlTable();
table.Attributes.Add("class", "detailstable");
// add table to accessor
accessor.AddParsedSubObject(table);
// make sure there are some children columns
if (columnDetails.Count() > 0)
{
// add a cell for each column in table
foreach (ListViewColumn columnDetail in columnDetails)
{
// create new row for template
var row = new HtmlTableRow();
// add row to accessor
table.Rows.Add(row);
// create field name cell
var fieldNameCell = new HtmlTableCell();
// add cell to row
row.Cells.Add(fieldNameCell);
// set the title
fieldNameCell.InnerText = columnDetail.Column.DisplayName;
// create field cell
var fieldCell = new HtmlTableCell();
// add cell to row
row.Cells.Add(fieldCell);
// instantiate a DynamicControl for this Children Column
var lvColumn = new DynamicControl(templateMode)
{
ID = columnDetail.Column.Name,
ValidationGroup = "FormView_" + templateMode.ToString(),
// set data field to column name
DataField = columnDetail.Column.Name,
};
// add control to cell
fieldCell.Controls.Add(lvColumn);
}
// create new row for template
var commandRow = new HtmlTableRow();
// add row to accessor
table.Rows.Add(commandRow);
// create the cell to hold the command buttons
var commandCell = new HtmlTableCell();
commandCell.Attributes.Add("class", "nowrap");
commandCell.ColSpan = 2;
commandRow.Cells.Add(commandCell);
// create a spacer
var spaceLit = new Literal();
spaceLit.Text = @" ";
// create cancel link
var cancelLink = new LinkButton()
{
//ID="EditLinkButton",
Text = "Cancel",
CausesValidation = false,
CommandName = "Cancel",
};
switch (templateMode)
{
case DataBoundControlMode.Edit:
// ceate update link
var updateLink = new LinkButton()
{
//ID="UpdateLinkButton",
Text = "Update",
CausesValidation = true,
CommandName = "Update",
};
commandCell.Controls.Add(updateLink);
commandCell.Controls.Add(spaceLit);
commandCell.Controls.Add(cancelLink);
break;
case DataBoundControlMode.Insert:
// create insert link
var insertLink = new LinkButton()
{
//ID="InsertLinkButton",
Text = "Insert",
CausesValidation = true,
CommandName = "Insert",
};
commandCell.Controls.Add(insertLink);
commandCell.Controls.Add(spaceLit);
commandCell.Controls.Add(cancelLink);
break;
case DataBoundControlMode.ReadOnly:
// create edit link
var editLink = new LinkButton()
{
//ID="EditLinkButton",
Text = "Edit",
CausesValidation = false,
CommandName = "Edit",
};
// create delete link
var deleteLink = new LinkButton()
{
//ID="DeleteLinkButton",
Text = "Delete",
CommandName = "Delete",
CausesValidation = false,
OnClientClick = "return confirm(\"Are you sure you want to delete this item?\");",
};
// create new link
var newLink = new LinkButton()
{
//ID="insertLinkButton",
Text = "New",
CausesValidation = false,
CommandName = "New",
};
commandCell.Controls.Add(editLink);
commandCell.Controls.Add(spaceLit);
commandCell.Controls.Add(deleteLink);
commandCell.Controls.Add(spaceLit);
commandCell.Controls.Add(newLink);
break;
default:
break;
}
}
// if there are no children columns don't
// bother to set the accessor to anything
}
private void GetEmptyDataTemplate(IParserAccessor accessor, IEnumerable<ListViewColumn> columnDetails)
{
// create a spacer
var literal = new Literal();
literal.Text = @"There are currently no items in this table.";
// add row to accessor
accessor.AddParsedSubObject(literal);
}
private void GetPagerTemplate(IParserAccessor accessor, IEnumerable<ListViewColumn> columnDetails)
{
throw new NotImplementedException();
}
private void GetFooterTemplate(IParserAccessor accessor, IEnumerable<ListViewColumn> columnDetails)
{
throw new NotImplementedException();
}
private void GetHeaderTemplate(IParserAccessor accessor, IEnumerable<ListViewColumn> columnDetails)
{
throw new NotImplementedException();
}
private class ListViewColumn
{
/// <summary>
/// Column to display
/// </summary>
public MetaColumn Column { get; set; }
/// <summary>
/// MetaData if any from the original column
/// </summary>
public SubGridViewsAttribute SubGridMetaData { get; set; }
/// <summary>
/// Holds the sort order value
/// </summary>
public int Order { get; set; }
}
}
Listing 5 - FromViewPageRowGenerator
The main difference between this implementation of ITemplate and the implementation in Part 3 is that the column title is in the same row as the DynamicControl so it produces a template similar to the output shown in Listing 6. The only difference between the different template types that are implemented are the links shown (Edit, Delete & New for ReadOnly), (Update & Cancel for Edit) and (Insert & Cancel for Insert) and the mode of the DynamicControls.
OrderDate | |
---|---|
RequiredDate |
Listing 6 – Fragment of the output from FormViewTemplateType
Project Download
Again V3 contains all from the previous parts 1 - 3