Introducing ASPX Automatic Page Layout Generation

Aug 31, 11:00 pm

Article Author: Jim Nesbitt
.NET 3.5 Books

Introduction


I’m always looking for faster and better ways to develop applications “the less manual coding required, the better. OOP is great for this, but there’s one area I’ve always wanted to automate more: ASPX screen layouts. No OOP here, just lots of tedious mouse clicking and dropping and dragging. For each page it’s essentially the same set of operations and the same manual process. If a field in the database is a date, then I always want it a certain size with a date validator, and if I have a mandatory text field, then I always want a mandatory field validator and I want the maximum input length set to the string length in the database.


I also want all the pages to have the same look and feel, which means spending yet more time doing the same thing over and over again, setting attributes across pages. Using CSS and user or custom controls can speed up this process by removing some of the repetition, but even with these techniques and tools, there are still a lot of repetitive tasks.


I determined that it must be possible to develop an automated system to replace (and speed up) the manual processes. (After all, that’s what computer systems are supposed to be good at!) Now I just mark up essential metadata about the page and columns on it, and the solution generates the entire ASPX page for me automatically. In this two-part series, I describe how I developed this solution to produce ASPX pages and also some of the benefits I realized from it.


The focus of this article is simply taking the step to hold the underlying page information separately and then using it to generate ASPX pages, so it’s available for further use instead of lost forever in the ASPX coding. When the page information is separate, it’s a very useful resource that can be reused and manipulated as many times as needed. Once it’s buried in an ASPX page, it’s no longer available for reuse. My application generates ASPX pages, but it also has the potential to generate WinForms as well using the same information.


Sample Overview


The example project provided in the download consists of a sample Order Info web page that displays details from the Northwind database. Since the existing tables in the Northwind database don’t have a lot of columns, I have created a view with 35 columns to make the example a little more realistic.


The page layout follows a set of standards and consists of a combination page with a datagrid that can be switched to zoom in and display the details from a single row on a series of tabs. It’s reasonably feature rich, and the page layout is quite complex as well.


The screenshot shown in Figure 1 displays the grid view of the data. Also notice that I have scrolled the datagrid down, but the headings haven’t disappeared off the top of the screen.



Figure 1. Order Info page in datagrid view
This figure has been reduced in size to fit in the text. To view the full image Click here


Clicking the Edit button zooms in to display the current details on a series of tabs indicated by hyperlinks on the right side of the screen as shown in Figure 2.



Figure 2. Order Info page showing details on the Order Info tab
This figure has been reduced in size to fit in the text. To view the full image Click here


Figure 3 shows the application after the Shipping Info link is clicked. Note that the layout is basically the same across the different pages.



Figure 3. Order Info page showing details on the Shipping Info tab
This figure has been reduced in size to fit in the text. To view the full image Click here


I think the layout is versatile and has a clear and consistent presentation style. The ASPX page coding for this is a hefty 2,000 lines, although admittedly there’s a lot of white space in it. To create the page, I first set up the page information, followed by the column information, which mainly consists of headings together with the order of columns on the grid and the tabs. This is pretty quick and just requires the minimum input. The generator then automatically creates the ASPX page layout shown previously. If I want to move columns around, I just change the column information and regenerate the pages.


The demo has reasonable functionality to illustrate the page processing. It’s a cut-down version of a production application, so there’s no updating and not much error trapping. Although there may be some very minor bugs, I think there’s sufficient functionality to illustrate the concepts and features.


System Requirements


To work with the solution in the downloadable sample, you need the following:


  • Internet Information Server

  • .NET Framework version 1.x

  • Visual Studio .NET (VS .NET) 2003

  • SQL Server with the Northwind database installed

  • Internet Explorer 5 or later

The Sample Code


The sample download for this article contains two VS .NET solutions:


  • MetaGen : This project contains the Order Info site. MetaGenAPP : This application is used to administrate the metadata.

The solution displays details from the Northwind database using the generated page layout. There is also a SQL directory for creating database objects and a BCP directory used to load some. You can find full details on the setup in the Readme.txt file supplied with the download material.


ASPX Page Layout


At this stage, a quick overview of the standard page layout might be useful. Although it’s essentially one layout, it has chameleon-like properties and can be configured to cover a majority of screens required in an application. This common "one size fits all" layout means that the generator can target a single page layout if desired.


The standard page contains a datagrid to display information in a list format. Some records are very wide, with a large number of columns, and trying to display all the columns in the datagrid isn’t possible, so zooming in and displaying the information for a single record in a tabular format is also required. I’ve seen techniques to display the information using a hidden single column that displays all the column information for the record on the datagrid, but because of the large number of columns, it’s often necessary to split up the information using tabs to group related data together.


The datagrid and the tabs are enclosed in panels with a default of visible set to false . To show the datagrid, I set the datagrid panel attribute visible to true . The details are shown on a particular tab by making the datagrid invisible and setting the tab panel’s visible attribute to true . This is a really flexible layout, and by restricting the display options available to the user, I can get a number of apparently different displays:


  • Combined list/tab screen: List and Tab Display options enabled

  • Standard list page: Disable display of tab panels

  • Single form input: Disable display of grid, and just show a single tab.

  • Single multitab form input: Disable display of grid, and enable tab display options

The layout doesn’t use fixed widths and positioning. Sizes are specified as a percentage of the available screen real estate. Besides being more flexible, this layout also makes it easier to rearrange the order of columns. Fixed widths and positioning could be used by adding new column attributes, but it’s even quicker if you don’t have to specify them at all.


CSS Page Layout Techniques


Providing this flexible page layout is quite complex and requires some very useful features of CSS. The following sections describe all the CSS used in the application.


CSS for the Datagrid


CSS ensures that the datagrid occupies a percentage of screen real estate rather than a fixed width or height. The datagrid panel has the following CSS style:



.list_screen_panel
{ height: 75%; width: 100%; overflow:auto; BORDER-BOTTOM: #0066CC 2px solid; position:absolute;
}


This provides for a scrollbar on the right side if the datagrid holds too many lines to show in the available space. The only drawback is that when the scrollbar is used, the headings also usually scroll out of sight. After a lot of searching, I found a great technique to prevent this for Internet Explorer on a blog by Geoff Appleby (see the Related Links section for the URL).


The datagrid header style is set as follows:



.GridHeader
{ position: relative; top:expression(this.offsetParent.scrollTop-2); left:-1; font-family: verdana; font-size: 8pt; font-weight: bold; background-color: white;
}



Using this style, the headers will remain at the top where we want them. This expression ensures that if the DIV for the datagrid is scrolled down 500 pixels (for example) any headings are also moved down 500 pixels, so they still appear at the top of the datagrid DIV and appear to be fixed to the top.


This solution may not work in all browsers since it uses dynamic expressions, but there are alternatives to achieve the same effect. The previously referenced blog also provides an alternative CSS style that didn’t use dynamic expressions, but for some reason or other, I couldn’t get it to work. Another alternative is to use a JavaScript function to set this value in script as the datagrid is scrolled. The sample application uses a JavaScript function to maintain the datagrid scroll position for the row across postbacks, and it would be similar to this.


CSS for the Tabs


Laying out the data on the tabs is also done using CSS, and again I can’t take any credit for this great technique for using CSS for forms input “this one is by Mark Newhouse for "A List Apart." (See the Related Links section for the URL.)


Essentially, all columns on the tabs are displayed with the column prompt on the left and the column on the right. This technique requires a span for the prompt, and another span for the column. CSS attributes ensure the headings float to the right to butt up against the input fields. I’ve found it cleaner and simpler just to let all columns have the same width. The two spans are enclosed in a DIV that occupies the entire width of the tab. This ensures that if a textarea is used, the next heading and column line up nicely.


This technique allows the column prompts and columns to be displayed in a tabular format without any tables. Each column prompt and column is placed on a separate row by enclosing it in a DIV with a CSS class of row .



<DIV class="row"> 
  <SPAN  class="label">
    <ASP:label   id="OrderID_t" 
    cssclass="clsLbl" 
    runat="server" 
    text="Order ID"> 
    </ASP:label> 
    <SPAN cssclass = "MandSpan" >
    &nbsp;&nbsp;
  </SPAN>   
  <SPAN  class="formw"> 
    <ASP:textbox 
      id="OrderID" 
      cssclass="ClsTextBox_protected" 
      Maxlength="11"   
      enabled = "false" 
      OnTextChanged="Field_Changed"
      runat="server"> 
    </ASP:textbox> 
  </SPAN> 
</DIV>


The enclosing DIV has a CSS class of row as follows:



/* Used to allow the columns on the tabs to be aligned
in a similar manner to the use of a table.*/
div.row
{ clear: both;
}


The clear property prevents any elements appearing to the left or the right of the DIV , ensuring that the column prompt and column value occupy the entire block and always line up. If this were not present, the labels on the left would float up and wouldn’t be level when the column occupies more vertical space than the prompt.


The labels are enclosed in a span with a CSS class of label :



div.row span.label 
{ float: left; width: 40%; text-align: right;
}


This assigns 40% of the space in the enclosing row DIV for the label. The text-align property of right ensures that the column prompt is right justified.


The label has a class of ClsLbl , which is used to set the label font and size. Following the label tag, there is a span to indicate mandatory fields, with a CSS class of MandSpan .



.MandSpan 
{ margin-left:4%; text-align: right;
}


The margin-left property ensures that there is always a gap on the left side of 4% of the parent element (i.e., the label span ). For mandatory columns, this span will contain an asterisk placed as part of the generation process.


The ASP:textbox tag is enclosed with a span tag with a class of formw :



div.row span.formw 
{ float:right; width: 55%; text-align: left;
}


The enclosing span has a property of float:right and a width of 55% of the enclosing row DIV .


The property text-align:left ensures that the text box contents are left justified. For the tabs, I left numeric columns left justified for aesthetic reasons, but I could modify the generator to output the span with a different CSS class if I decided to have numeric fields right justified on the tabs.


CSS in the Page Layout


The page layout makes extensive use of CSS and DIV s specified as a percentage of the screen width and height. This allows the pages to work equally well on different resolutions or if the page is resized, as everything just reflows. It also simplifies changing the order of the columns on the tabs. The pages were developed for use in IE, but I think most of the CSS used is more or less standard and should work (or could be made to work) in other browsers. The only exception I can think of to this is the CSS to stop the datagrid headings scrolling that was mentioned previously.


There’s some useful information in this section, and I’d recommend a look at the links referenced sometime. All of these DIV s, tabs, and overlap add considerably to the complexity of the finished ASPX page, and it would be difficult to produce manually, but using the generator means that only the essential information will need to be marked up.


Automating the Processes to Create the Page


The basic aim is to eliminate the repetitive effort in creating the complex ASPX pages. The use of the standard layout described in the previous section means that setting up the major panels to hold the datagrid and tabs is identical for each page.


There’s also a lot of repetition in setting up the ASPX code for the columns being displayed on the page. Here’s an example of ASPX page code for an OrderId column on the datagrid:



    <asp:BoundColumn 
      DataField = "OrderID"         
      HeaderText = "OrderID" 
      SortExpression = "OrderID" 
      ItemStyle-HorizontalAlign ="Right"        
      DataFormatString =""                 
      ItemStyle-Wrap="false" 
    />          


This is essentially similar for most columns “the only things that change are some of the attribute values. For example, if I edit this code, replacing the OrderId field with EmployeeID , that would give me the ASPX coding for the EmployeeID column.


If I was using a text editor to create the ASPX code, I could set up some generic code for this and just use it to do the find and replace with the required values. (Generic code to be replaced is suffixed with a caret as this character isn’t likely to occur normally.)



    <asp:BoundColumn 
      DataField = "^COL_NAME"         
      HeaderText = "^COL_HEAD" 
      SortExpression = "^COL_NAME" 
      ItemStyle-HorizontalAlign ="^JUSTIFY"        
      DataFormatString ="^FORMAT"                 
      ItemStyle-Wrap="false" 
    />          


I obviously don’t really want to have to do a manual find and replace for every column in the grid, but if I can set up all the replacement values in advance for all the columns on the grid, then I can automate this process. A BoundColumn is probably the simplest example. If I want to display a check box and need to use a template column, then the generic code for that is also common for all check boxes.



<asp:TemplateColumn 
  HeaderText = "Discontinued" 
  SortExpression = "Discontinued" 
  ItemStyle-HorizontalAlign ="Center">
    <ItemTemplate>
      <asp:checkbox
        id="Discontinued" 
        enabled="false"
        runat="server"
        checked =  ‘<%# fixnull( DataBinder.Eval(
           Container.DataItem,"Discontinued") ) %>’>
    </asp:checkbox>
  </ItemTemplate>
</asp:TemplateColumn>


The code is fairly standard, with the addition of the fixnull() function to return a null value as false .


The only thing that’s different for any check box is the HeaderText and the id , which I always set to the column name. If I know that the data type of the column is a bit, then I can default the check box style by default and the only additional information I need to supply is the column heading.


Once more, it’s a simple exercise to replace specific values such as the actual column name with generic values prefixed by a caret notation. It’s important that the names are consistent, so the generic term for the column name is always ^COL_NAME , but that’s not too difficult of an approach to follow, as there aren’t a large number of these "generic variables."


If you look at different pages and the ASPX coding, I’m sure you’ll see a lot of areas of commonality yourself.


Some of the page code is even more straightforward, with no replacements necessary. This datagrid header code goes on every page and can be copied without any changes:



<ASP:datagrid 
  id="MyDataGrid" 
  runat="server" 
  CssClass="Grid"
  AutoGenerateColumns="False"
  AllowSorting="true"
  OnSortCommand="sortGrid"
  ShowFooter="false"
  CellPadding=2
  CellSpacing="0"
  wrap="false"
  OnItemCommand="SelectRow"
  EnableViewState="false"


This code is identical for each page with a datagrid on it, so why waste effort adding it in manually?


Let’s look at the ASPX code for a column on the tabs:



<ASP:textbox                          
  id="OrderID"                        
  cssclass="ClsTextBox_protected" 
  Maxlength="11"                
  enabled = "false"                  
  OnTextChanged="Field_Changed"
  runat="server>" 
</ASP:textbox>


The id is the same as the id in the datagrid; it’s always the same as the column name in the database. There’s no naming conflict as the id in the datagrid and the text box are referenced differently. The only information required to add is the attribute named MaxLength and whether or not the field is enabled .


True, there can be a lot of other coding on the page “scripts, buttons, and so on “but since most developers use a common layout for pages, any existing ASPX page layout should be broken down into a series of generic or common texts that can be assembled together to form the complete page layout. Some of the texts are completely standard, with no replacements required, and some need to be modified depending on the details being displayed. If I come across the occasional one that doesn’t fit into an existing category, I can just set it up as a new generic text and use it for the next occurrence of this type.


I think I’ve shown fairly conclusively that by outputting unchanged standard texts and performing find and replace operations on generic texts, it should be possible to reconstruct any ASPX page layout from its constituent parts.


That’s basically how I started building a framework to generate my ASPX pages. I took an existing layout and parsed it into common components, identifying the standard texts and parameters for the find and replace. There’s also a small amount of other information required for the generation process, one simple example being the order of columns on the datagrid. So I noted that as well.


Identifying the standard texts and making them generic was a fairly straightforward operation. To complete the picture, I also needed to be able to provide the values for the find and replace so that I could automate the process.


Page and Column Metadata


Metadata is data about data, which is exactly what I am trying to identify. A good example of this is the database system tables that hold information about the columns in a table and the objects that are used in most databases today.


My inspection of the ASPX pages showed there’s not a lot of information required about the page and the columns (there may be a lot of columns, but the metadata for each one is fairly concise). A lot of the information relating to columns can be defaulted so it only needs changing if the default processing doesn’t apply. This makes up most of the metadata, and there’s also a small amount of information related to the page to assist with positioning. Here’s a list of the basic information required:


  • The ASPX page name.

  • The title to display on the page.

  • The table or view in the database that details are displayed from.

  • The table in the database to update.

  • The application name.

  • The number of tabs used by the page.

  • The number of rows to display on a page.

  • Whether to use paging for the datagrid.

  • The page type, which is always pbspage for the tab pages. My intention is to use this to create variants of the page if required.

  • The grid height, which indicates if the grid contains single height or double height rows.

  • The sort sequence.

The page size and page height were originally used to ensure that the number of rows would fit to the size of the datagrid without scrolling the headings off the page. Now that I can "fix" the headings, the page size can be any required value.


Page Columns Metadata


Most of the metadata can be obtained directly from the database. It just needs supplementing with heading and positioning information, which has to be inputted separately. One useful refinement would be to introduce a standard table of column names and headings, so that the headings could also be populated automatically.


  • Column Name.

  • Column Heading (for use with a datagrid).

  • Column Prompt (for use with a tabular input form).

  • Justification (Left/Right; basically, Left for text fields and Right for numeric fields).

  • CssClass, which identifies the CSS class to use when displaying the column and also the column data type (date, bit, text, etc.).

  • Display Style (check box, text box, text area).

  • Mandatory/Optional.

  • Protected, which indicates if the column is to be disabled or enabled. The enabled attribute for the column is set to allow the user to change or prevent the user from changing the contents of the field as appropriate.

  • Maximum length, which indicates the maximum number of characters the user can type into the column.

  • Multiline, if the column is displayed as a multiline text area.

  • Input Case, which indicates whether the case should be lower, upper, or any case.

Some further information relates to the positioning and processing of the fields:


  • On Datagrid: Indicates if the column is displayed on the datagrid

  • Grid Sequence: Indicates if the column is displayed on the grid, and the sequence

  • Tab Number: Indicates the tab the column is displayed on

  • Tab Sequence: Indicates the sequence on the tab

Page Tabs Information Requirements


Because the data is split into tabs to organize it in a logical fashion, I also set up a simple table to hold this information containing


  • The number or sequence for the tab

  • The name for the tab

  • Description for the tab (not currently used, but I think I’ll eventually use it to create a title attribute to give the user further information when the cursor is over the tab hyperlink)

Because I wanted to generate the ASPX page automatically, I needed somewhere to hold this information. Since most of the programs I develop use a database, this seemed like the logical place to hold the information. It fit naturally into three tables: one to hold the page metadata, one for the page columns metadata, and one for the tabs. While not strictly normalized, it works and has the benefit of simplicity.


Metadata Maintenance (MetaGenApp)


When I first built this framework, I was working with a site that contained around 20 pages. At first, I was modifying the metadata in the database directly, but this wasn’t ideal since it was taking a lot of time. To speed up this process, I developed a full Windows application (EXE) to make the maintenance of this information more straightforward and less error-prone to manual intervention. This section will provide an overview of this utility using the metadata values for the sample Order Info page as an example. The production application I created uses a Windows application, and I have chosen to create an equivalent ASP.NET version (MetaGenApp) to maintain the metadata for this sample. It contains comparable functionality to the EXE that I use myself, although it lacks an editable datagrid. It’s specific to the Order Info page, as it calls the stored procedures, passing the Order Info as the page name parameter. It can update the metadata, so it’s useful to make simple changes and regenerate the ASPX page to view the effects.


I used the generator procedure provided to create the ASPX pages for MetaGenApp itself. Because this is more or less a custom application to simplify making changes to the metadata, I made some small manual modifications to add in the menu. If I were building a lot of applications like this, I would extend the metadata and generator to generate this output automatically without manual modification.


Creating an application to assist with this process may seem a lot of work, but once the initial development has been done, future development takes less time, so over a period of time it’s faster.


Figure 4 displays a screenshot of the page I use to maintain the page metadata information.



Figure 4. Maintaining the page metadata
This figure has been reduced in size to fit in the text. To view the full image Click here


The first three options on the top menu allow me to modify the page, columns, or page tabs metadata. The column details are shown in the next screenshot (Figure 5), together with the available tabs.



Figure 5. Maintaining Page Columns metadata
This figure has been reduced in size to fit in the text. To view the full image Click here


The information is available on tabs, with the main information on the first tab as shown in Figure 6.



Figure 6. Page Columns main tab
This figure has been reduced in size to fit in the text. To view the full image Click here


The second tab holds less frequently changed information.



Figure 7. Page Columns secondary tab
This figure has been reduced in size to fit in the text. To view the full image Click here


The third menu option shows details for the tabs.



Figure 8. Page Tabs maintenance
This figure has been reduced in size to fit in the text. To view the full image Click here


The fourth option on the menu is titled Generic Code Texts . This holds the texts used together with the metadata to do the find and replace and produce the ASPX page. I created the texts by taking snippets from an existing page, making it generic where necessary, and then pasting it in and saving it with a key that identified its purpose and a sequence number to identify its position within the output.



Figure 9. Page Texts List View
This figure has been reduced in size to fit in the text. To view the full image Click here


Figure 10 displays the tab view for the line highlighted from Figure 9.


.



Figure 10. Generic Texts maintenance
This figure has been reduced in size to fit in the text. To view the full image Click here


The MetaGenApp allows you to make changes to the layout of the sample Order Info page from within an application. You can modify the page details to display more rows per page or display all rows without paging, and the page columns metadata can be modified to change the sequence of columns on the datagrid and/or the tabs.


Creating the Initial Page Columns Metadata


One of the goals of the framework was to try and keep the manual effort to a minimum since I needed to generate a lot of pages for my site. Since a lot of column information has already been set up in the database, I decided to make use of it.


Under the development standards I work from, I can only ever retrieve data directly from a single table and use a view to retrieve data from several tables. For a simple reference table consisting of a code and description, the select statement would probably use the table name. For an order record that needs information from other tables such as vendor information, buyer name, and approver name, a view is used.


This gives a degree of independence from database changes, as the view definition can often be modified without having to change any VB .NET coding, which would be necessary if the SQL joins were being built in a string and used as a SQLCommand . A single select statement against a view is also a lot simpler to build than one that requires lots of joins. It’s a sensible standard, as all the work to create the view has to be done somewhere, and it’s a lot easier to maintain if it’s a view in the database instead of buried somewhere in code. Another benefit is that other developers can make use of it. Views sometimes get bad press, but provided the view is well designed, there is no performance impact.


My page information contains the name of the table or view that details are retrieved from, so I can use a simple stored procedure that reads the information about the columns from the system tables and populates as much of my column metadata as possible. Because the page view or table definition might change because of new columns (increases in length, etc.) the procedure can also update the page column metadata while preserving any supplementary information I’ve added.


A lot of the metadata about columns is already available in the database, or an educated guess can be made based on it. The columns can only come from the table or view that the page is displaying, so I can get a list of all the potential columns. Also, if a field is numeric (integer, decimal, currency, or float), then it will be displayed right justified in the datagrid. If a column has a datatype of bit , then I want to display a check box. For text fields, I don’t want the user to type in more than the maximum length, but I can get the maximum length from the database and use it to set the Maxinput attribute. If a column is defined as not null and it’s an input field on the page, then it’s a safe bet that it’s a mandatory field. My page metadata identifies the table to update, so if a column doesn’t exist there, then enabled is set to false to indicate that the user can’t change it. In most cases, I won’t need to change these defaults, so my task is simplified even more.


I’m a great fan of views. They provide a very useful level of abstraction or shrouding, and can also dramatically simplify coding and processing. SQL Server contains a set of information schema views. These provide a system table-independent view of the SQL Server metadata that describes the tables. The one I use most is INFORMATION_SCHEMA.COLUMNS . This contains some very useful information about tables or views and the columns they contain. Other databases also have system that allows this information to be obtained.


Some of the key information I use is as follows:































Column NameDescription
TABLE_NAMEThe name of the table
COLUMN_NAMEThe name of the column in the table
ORDINAL_POSITIONThe sequence of the column
DATA_TYPE The data type of the column
CHARACTER_MAXIMUM_LENGTHThe maximum length (for character data)
IS_NULLABLEWhether null values are allowed
NUMERIC_PRECISIONThe maximum number of digits, excluding the decimal point
NUMERIC_SCALE The decimal places allowed



I can make use of this information to set up my initial columns metadata. The file dotnets_views.sql supplied in the sample setup has a view called v_dnx_xjimscolumns . Here’s an extract from the definition of this view illustrating how it makes use of this information to work out the maximum input length for a column:



,CASE 
WHEN DATA_TYPE IN (‘char’,‘varchar’,‘nvarchar’) then CHARACTER_MAXIMUM_LENGTH
WHEN DATA_TYPE IN (‘text’,‘ntext’) then 0
WHEN DATA_TYPE IN (‘decimal’,‘numeric’) then numeric_precision + 1 — dec point no sign
WHEN DATA_TYPE IN (‘smallint’,‘int’,‘tinyint’) then numeric_precision + 1 — Cant tell with floats, so allow 20 characters.
WHEN DATA_TYPE IN (‘float’) then 20
WHEN DATA_TYPE IN (‘datetime’ ,‘smalldatetime’) then 20
WHEN DATA_TYPE IN ( ‘bit’ ) then 1
ELSE 0
END as maxlength


If the data type is a character data type, I can get the maximum input length directly from CHARACTER_MAXIMUM_LENGTH . If the data type is text , I set the maximum input length to zero to allow an unlimited number of characters. If the data type is decimal , I add one to the maximum number of digits (numeric precision) to allow for the decimal point.


All that’s happening here is that I’m extending the information in the schema tables to simplify my processing and to default the metadata values. In most cases, the defaults will reduce my effort, as I only need to change metadata values if the default isn’t satisfactory.


I used this view as the first step in obtaining my column metadata. It speeds up the process by creating metadata values that can be derived directly from the column information that has already been defined.


The second step is to get some additional information based on the actual table or view the page is displaying. The page data in the database contains the name of the table or view to retrieve from and the table to update. If a column appears in the view, but doesn’t appear in the table to update, then it’s being used to provide additional information such as a code description, which would be modified elsewhere. This normally means that the user can’t change the value on the page. Here’s an extract from the view v_dnx_pagecols_start in dotnets_views.sql :



CREATE VIEW v_dnx_pagecols_start
AS
SELECT
.
.
.
,CASE WHEN su.table_name is NULL THEN 1 ELSE 0 END as protected
from dnx_page p
join v_dnx_xjimscolumns ss
on p.table_to_select = ss.TABLE_NAME
LEFT OUTER
join v_dnx_xjimscolumns su
on p.table_to_update = su.TABLE_NAME
and ss.COLUMN_NAME = su.COLUMN_NAME


The name of the table containing my page metadata is dnx_page . I join this to the columns view described previously using the table to select column from the page metadata. This provides me with a list of all the columns that can possibly appear on the page. The left outer join clause gives me column values from the table to update provided the column exists in this table; otherwise, I get null values.


The case clause allows me to determine if the column is updatable or not. If the outer join returned a null value, the column doesn’t exist in the table that is updated, so I set the protected attribute to indicate that it is not an updatable column.


Having extracted as much information from the database as possible, the next step was to use this to populate the table holding my page columns metadata ( dnx_pagecols ). I set up a stored procedure p_dnx_gen_pagecols (full text in dnx_procs.sql ) that takes the page name as a parameter. Initially, this simply inserted the values into my page columns table, but I wanted the ability to update the metadata if the database changed, without losing any information I might have added in, so I extended the processing to merge the data using the following process.


I extract the page columns information into a temporary table first. If there are no entries in the page columns table for the page, I can simply insert all the rows from the temporary table into the page columns table and finish. Reaching this stage means that the page columns table already has information in it, so I need to update the details.


If a column has been removed from the table or view (or possibly renamed), I need to remove it, so I delete any columns that aren’t present in the temporary table. For matching columns, I only update a subset of the column information to preserve any information I’ve added. Finally, any new columns are added into the page columns table.


In practice, most of this process to create the initial metadata was relatively straightforward, as I had already been using the INFORMATION_SCHEMA.COLUMNS view for similar purposes previously, namely to generate Transact-SQL cursor processing code, so I already had a basis to start from. Once this basic column information is available, I can add in further information about the columns, such as sequencing, column headings, and so on, to be able to generate the final ASPX coding.


Conclusion


In this first part of this article, I highlighted the point that producing ASPX pages consists of a series of repetitive manual steps that involve either a lot of dragging and dropping or performing multiple find and replace operations. To illustrate this, I used a standard list/tab ASPX layout, which has a very adaptable and consistent presentation style for ASPX pages. I also covered some useful CSS techniques used to enhance the layout. Although that topic was not central to the focus of this article, I hope that you found some useful information there.


I outlined how my example page could be deconstructed into a set of generic texts with a set of find and replace parameters, which could be held externally as metadata in order to automate the production of the ASPX page code.


Another premise was that where a standard page layout is used, the process of creating any ASPX page uses the same processes, so the combination of generic texts and metadata can be used to create any ASPX page in the same standard layout.


I covered the process of identifying the metadata values required and indicated how many of these could be defaulted in or obtained from the system database tables. Once this was done, the addition of a small amount of further information such as sequencing and heading information is required to provide the potential to create the page automatically and showed screenshots from an application with the capability to maintain this metadata.


I also showed how to leverage the information held in the system tables to default in most of the page columns metadata values.


In part 2, I’ll cover in detail how the generation process brings everything together to create the ASPX page code itself.

Founders at Work

Commenting is closed for this article.