As far as I can see there are three (oops! four then) types of Custom Page:
- Custom Pages Part 1 - Standard Custom Page based on an existing PageTemplate and customised in the DynamicData\CustomPages folder.
- Custom Pages Part 2 - A completely Custom Page again in the DynamicData\CustomPages folder.
- Custom Pages Part 3 - Standard ASP.Net Page with Dynamic Data features added to take advantage of the FieldTemplates.
- Custom Pages Part 4 - A DetailsView and a GridView using Validation Groups
- Custom Pages Part 5 - I18N? Internationalisation Custom Page
This page is a standard Edit.aspx PageTamplate with a few extra controls (GridView, LinqDataSource, DynamicValidator and ValidationSummary)
<asp:ValidationSummary runat="server" ID="OrdersValidationSummary" EnableClientScript="true" ValidationGroup="Orders" HeaderText="List of validation errors for Orders" /> <asp:DynamicValidator runat="server" ID="OrdersValidator" ControlToValidate="dvOrders" ValidationGroup="Orders" Display="None" /> <asp:DetailsView runat="server" ID="dvOrders" DataSourceID="ldsOrders" DefaultMode="Edit" OnItemCommand="dvOrders_ItemCommand" OnItemUpdated="dvOrders_ItemUpdated" CssClass="detailstable" FieldHeaderStyle-CssClass="bold" AutoGenerateRows="True" DataKeyNames="OrderID"> <FieldHeaderStyle CssClass="bold" /> <Fields> <asp:TemplateField ShowHeader="False"> <EditItemTemplate> <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="True" ValidationGroup="Orders" CommandName="Update" Text="Update"> </asp:LinkButton> <asp:LinkButton ID="LinkButton2" runat="server" CausesValidation="False" CommandName="Cancel" Text="Cancel"> </asp:LinkButton> </EditItemTemplate> </asp:TemplateField> </Fields> </asp:DetailsView> <asp:LinqDataSource runat="server" ID="ldsOrders" EnableUpdate="True" ContextTypeName="NorthwindDataContext" TableName="Orders"> <WhereParameters> <asp:Parameter /> </WhereParameters> </asp:LinqDataSource> <br /> <asp:ValidationSummary runat="server" ID="Order_DetailsSummary" EnableClientScript="true" ValidationGroup="Order_Details" HeaderText="List of validation errors for Order_Details" /> <asp:DynamicValidator runat="server" ID="dyvOrder_Details" ControlToValidate="gvOrder_Details" ValidationGroup="Order_Details" Display="None" /> <asp:GridView ID="gvOrder_Details" runat="server" DataKeyNames="OrderID,ProductID" CssClass="gridview" DataSourceID="ldsOrder_Details"> <Columns> <asp:TemplateField ShowHeader="False"> <EditItemTemplate> <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="True" ValidationGroup="Order_Details" CommandName="Update" Text="Update"> </asp:LinkButton> <asp:LinkButton ID="LinkButton2" runat="server" CausesValidation="False" CommandName="Cancel" Text="Cancel"> </asp:LinkButton> </EditItemTemplate> <ItemTemplate> <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False" CommandName="Edit" Text="Edit"> </asp:LinkButton> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> <asp:LinqDataSource ID="ldsOrder_Details" runat="server" ContextTypeName="NorthwindDataContext" TableName="Order_Details" Where="OrderID == @OrderID" EnableUpdate="True"> <WhereParameters> <asp:ControlParameter ControlID="dvOrders" Name="OrderID" PropertyName="SelectedValue" Type="Int32" /> </WhereParameters> </asp:LinqDataSource>
Listing 1 – Custom Edit page for Orders
In the above listing you can see that the validation controls and link buttons in the DetailsView and GridView have been decorated with ValidationGroup="Orders" and ValidationGroup="Order_Details" appropriately.
The problem you have with a DetailsView or GridView with AutoGenerateRows set to true is how do you set the item controls ValidationGroup.
public class ValidationGroupFieldsManager : IAutoFieldGenerator { protected MetaTable _table; protected String _validationGroup; public ValidationGroupFieldsManager(MetaTable table, String validationGroup) { _table = table; _validationGroup = validationGroup; } public ICollection GenerateFields(Control control) { List<DynamicField> oFields = new List<DynamicField>(); foreach (MetaColumn 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; // create a new field with its validation group set DynamicField f = new ValidationGroupDynamicField(_validationGroup); f.DataField = column.Name; oFields.Add(f); } return oFields; } } // special thanks to David Ebbo for this public class ValidationGroupDynamicField : DynamicField { protected String _validationGroup; public ValidationGroupDynamicField(String validationGroup) { _validationGroup = validationGroup; } public override void InitializeCell( DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex) { if (cellType == DataControlCellType.DataCell) { var control = new DynamicControl() { DataField = DataField, // Copy various properties into the control UIHint = UIHint, HtmlEncode = HtmlEncode, NullDisplayText = NullDisplayText, // set ValidationGroup ValidationGroup = _validationGroup }; if ((rowState & DataControlRowState.Edit) != 0) control.Mode = DataBoundControlMode.Edit; if ((rowState & DataControlRowState.Insert) != 0) control.Mode = DataBoundControlMode.Insert; cell.Controls.Add(control); } else { base.InitializeCell(cell, cellType, rowState, rowIndex); } } }
Listing 2 – IAutoFieldGenrator for setting the Mode and ValidationGroup
There are two classes in Listing 2 the first an example of an implementation of IAutoFieldGenrator. In this implementation all we do is create a DynamicField if there should be a column or row.
The clever bit ( if you can call it that) is in the my DynamicField implementation. In here we create a DynamicField and add a DynamicControl to it, set various properties including ValidationGroup (which was passed in via the constructor), then we set the Mode of the control by testing the rowState.
public partial class Edit : System.Web.UI.Page { protected MetaTable mtOrders; protected MetaTable mtOrder_Details; protected void Page_Init(object sender, EventArgs e) { DynamicDataManager1.RegisterControl(dvOrders);
DynamicDataManager1.RegisterControl(gvOrder_Details);
// the following four lines were moved form Page_Load (cut and paste error on my part) mtOrders = ldsOrders.GetTable(); mtOrder_Details = ldsOrder_Details.GetTable(); dvOrders.RowsGenerator = new ValidationGroupFieldsManager(mtOrders, "Orders"); gvOrder_Details.ColumnsGenerator = new ValidationGroupFieldsManager(mtOrder_Details, "Order_Details"); } protected void Page_Load(object sender, EventArgs e) { mtOrders = ldsOrders.GetTable(); Title = mtOrders.DisplayName; } protected void dvOrders_ItemCommand(object sender, DetailsViewCommandEventArgs e) { if (e.CommandName == DataControlCommands.CancelCommandName) { Response.Redirect(mtOrders.ListActionPath); } } protected void dvOrders_ItemUpdated(object sender, DetailsViewUpdatedEventArgs e) { if (e.Exception == null || e.ExceptionHandled) { Response.Redirect(mtOrders.ListActionPath); } } }
Listing 3 – Edit.apsx.cs code behind file
In here we add the plumbing for the extra GridView and then add the RowGenrator and ColumnGenrator to the DetailsView and GridView to set the ValidationGroup for the individual fields in the both the DetailsView and GridView.
This give us the ability to have multiple DynamicData Lists, DetailsViews etc on a page and validate each one separately, which again put the validation where is should be in a central place OAOO (Once And Once Only) that I like so much .
No comments:
Post a Comment