User Interface generator for ASP.NET database applications






3.91/5 (14 votes)
Sep 17, 2006
5 min read

93245

3413
In web applications, many pages have simple CRUD purposes. This presentation shows how to produce these pages without a single line of code by generating them with description files.
Introduction
Most of database application UI represent data as a combination of lists and forms. The pages are very often the same : search criteria (form), result table (list) with add/edit/delete buttons, and data detail (form) with ok/cancel button. This is especially the case for admin pages (reference table like countries / cities) without much business rules (just ensuring the relational integrity).A standard approach requires to develop specific code for each table and page (data object + sql requests + aspx + code behind).
The solution described in this article consists in :
In order to develop specific simple pages (list + edit), you just have to write the description files and that's all. No code needed (no class, no aspx, no code behind) !
Similar articles on CodeProject :
Universal Database Admin for ASP.NET and SQL Server
Database Viewer and Updater for any Database
Database Navigator
Difference : generates end-user UI, not only for DBA
Go to online demo
Detail.aspx - Sample generated detail page

Features
This is mainly for IT developers, not yet for non-developers. Simples pages can be generated without effort. It also can be used for more complex pages : the developer would inherit the default page and assemble user controls (generated by the tool).
This tool is supposed to remain relatively lightweight, simple and extensible. I'm not a fan of complex architectures especially for simple functionnalities like CRUD. But I think it can be used with other frameworks : O/R mapping (like NHibernate) by reflection, controller (like Maverick or NStruts) by interface (the controller can be replaced).
Description files
The description files consists in :These are simple text files with sections (like .ini with parameters in sections) because they are more human friendly. In future versions, there should be XML files (if possible compliant to SOAP or NHibernate DTD) and even a Windows UI for generating and editing these files.
There can be a resource file for each language. The rendering properties may depend on the language, the action (view/edit/new/…), the target (web/windows/pocket), the user role (admin / guest / …) and the client base (if you want to distribute the same application to different customers with different parameters).
#---- module file for currency table ------------------------------------ [sql] select = select * from currency order by cur_cd selectOne = select * from currency where cur_id = ?cur_id checkBeforeDelete = select count(*) from country where cur_id = ?cur_id #------------------------------------------------------------------------ [table] name = currency primaryKeys = cur_id #------------------------------------------------------------------------ [column name=cur_id #nullable=0] [column name=cur_cd type=VA3 nullable=0] Unique = 1 [column name=cur_name domain=name nullable=0] [column name=cur_name_2 domain=name nullable=0] [column name=cur_name_3 domain=name] [column name=cur_type type=N2 nullable=0] [column name=cur_status type=B nullable=0] [column name=cur_rate type=N10,5] [column name=cur_creation_date type=D] #------------------------------------------------------------------------ [relation columns=cur_type] Reference = currency-type ReferenceColumns = cur_type DisplayField = cur_type_nameSee screens below
#---- resource file for currency table (english version) ---------------- [TableRender] ListTitle = List of currencies ColumnOrder = cur_cd, cur_name_2, cur_type, cur_status, cur_rate EditOrder = cur_cd, cur_name_2, cur_name, cur_name_3, cur_type, cur_status, cur_rate, cur_creation_date EditTitle = Currency detail : {cur_name_2} [TableRender action=new] EditTitle = New currency #------------------------------------------------------------------------ [ColumnRender name=cur_cd] Label = Code Link = 1 [ColumnRender name=cur_name] Label = Name (french) [ColumnRender name=cur_name_2] Label = Name [ColumnRender name=cur_name_3] Label = Name (german) [ColumnRender name=cur_rate] Label = Exchange rate (USD) [ColumnRender name=cur_type] Label = Type RelationDisplayField = cur_type_name_2 [ColumnRender name=cur_status] Label = Accepted payment [ColumnRender name=cur_creation_date type=D] Label = Creation dateThere can be expressions. For exemple :
# the column "cur_name_2" will be retrieved from the record EditTitle = Currency detail : {cur_name_2} # record will be updateable only if the IsAdmin property # in the session is set (either by param/key or by reflection) EditUpdateable = {session.IsAdmin}The layout can be more complex :
# adds a sublist below the main form [TableRender] DetailLists = currency_country ActiveDetailList = 0 # the layout can be a complex hierarchy of tables / rows / cells [EditRender] Layout = row1, row2, row3, row4, row5, row6, row7 row1 = cur_cd row1.BackColor = magenta row2 = cur_name_2 cur_name.width = 100px row3 = cur_name, cur_name_3 cur_name_2.width = 100px cur_name_3.width = 100px row4 = cell41 cell41 = label1 cell41.ColumnSpan = 4 label1.Text = Currency properties label1.ForeColor = #EE00FF label1.Width = 200px label1.Height = 20px row5 = cur_type row6 = cur_status, cur_rate row7 = cur_creation_dateGenerates Detail.aspx

Class diagram

Data access layer : interface for reading and writing records. The default implementation access the database but there can be other implementations for LDAP or CSV / XLS files.
public interface IRecordEnumerator : IEnumerator {
Record CurrentRecord { get; }
}
public interface IRecordReader : IRecordEnumerator {
RecordInfo RecordInfo { get; }
int Depth { get; }
bool IsClosed { get; }
int RecordsAffected { get; }
void Close();
DataTable GetSchemaTable();
bool NextResult();
bool Read();
int RowNum { get; }
object Parameters { get; }
}
public interface IRecordWriter {
void ExecuteWriter(IDbConnection dbConnection, IDbTransaction dbTransaction, Record rec);
void ExecuteWriter(IDbConnection dbConnection, IDbTransaction dbTransaction, RecordTable rt);
}
Controller / UserControl / Page
There are UserControls (class + ascx) and Pages (class + aspx) for a record and a list. They implement interfaces so they can be replaced by other classes and controllers. The interface can even serve for WindowsForms.The list page (List.aspx) contains a record list (RecordTable or RecordReader), eventually a parameter record (search criteria) and other elements (message label for confirmation / warning / error messages, pagination, buttons).
<!-- #include file="_Top.inc"--> <asp:Label id="lblMessage" runat="server" Visible="false"></asp:Label> <form runat="server"> <% if (HasVisibleParameters) { %> <ucRecord:Record id="paramRecordControl" runat="server"></ucRecord:Record> <input type="hidden" name="__action" value="search"> <asp:Button id="btSearch" runat="server" Text="Search" onclick="btSearch_Click"></asp:Button> <% } %> <ucList:RecordList id="listControl" runat="server"></ucList:RecordList> <ucPagination:Pagination id="paginationControl" runat="server"></ucPagination:Pagination> <asp:Button id="btAdd" runat="server" Text="Add" onclick="btAdd_Click"></asp:Button> </form> <!-- #include file="_Bottom.inc"-->The detail page (Detail.aspx) contains the main RecordControl, a message label, validate / cancel buttons and eventually a sublist (RecordTableControl)
<!-- #include file="_Top.inc"--> <form runat="server" Id="form1"> <asp:Label id="lblMessage" runat="server" Visible="false"></asp:Label> <ucRecord:Record id="recordControl" runat="server" RecordName="record"></ucRecord:Record> <% if (RenderContext.IsViewAction || RenderContext.IsViewSummaryAction) { %> <asp:Button id="btReturn" causesValidation="false" runat="server" Text="Retour" onclick="btCancel_Click"></asp:Button> <asp:Button id="btEdit" runat="server" Text="Modifier" onclick="btEdit_Click"></asp:Button> <% } else { %> <asp:Button id="btCancel" causesValidation="false" runat="server" Text="Annuler" onclick="btCancel_Click"></asp:Button> <asp:Button id="btValidate" runat="server" Text="Valider" onclick="btOK_Click"></asp:Button> <% } %> <asp:ValidationSummary id="validationSummary" runat="server"></asp:ValidationSummary> <br> <% if (!RenderContext.IsNewAction && RecordRenderInfo.HasDetailList) { btAdd.Text = GetText("button.AddSublist"); %> <ucList:RecordList id="subListControl" runat="server"></ucList:RecordList> <br> <asp:Button id="btAdd" runat="server" Text="Add" onclick="btAddSublist_Click"></asp:Button> <% } %> </form> <!-- #include file="_Bottom.inc"-->Controller source (List.aspx.cs)
void Page_Init(object sender, EventArgs e) {
base.Page_Init(sender, e);
if (HasVisibleParameters) {
paramRecordControl.Record = ParametersRecord;
paramRecordControl.BuildControls();
}
}
void Page_Load(object sender, EventArgs e) {
base.Page_Load(sender, e);
if (HasVisibleParameters) {
if (!IsPostBack) paramRecordControl.RecordToControls();
else paramRecordControl.ControlsToRecord();
}
ExecuteReader();
if (IsReaderMode) {
listControl.RecordReader = RecordReader;
paginationControl.PageSize = 0;
}
else {
listControl.RecordTable = RecordTable;
paginationControl.PageSize = RecordRenderInfo.PageSize;
paginationControl.PageNum = PageNum;
paginationControl.RowCount = RecordTable.Count;
}
btAdd.Text = GetText("button.Add");
btSearch.Text = GetText("button.Search");
}
Open source project
The work is not finished yet. I have many improvements in mind. I need help for continuing the work and also would like to share ideas with the community. So I propose this as an open source project !People needed for development, testing, documentation, website administration, translations,... The open source licence would be simple and commercial use would be authorized. Please reply to this thread or write me at the address provided in the online demo.