in

ASP.NET Weblogs

This Blog

Syndication

ShowUsYour<Blog>

Irregular expressions regularly

A tale about a victory involving ASP.NET and encapsulation

There are challenges in building cool, dynamic Web UI's

A short while back I took on the task of migrating a VB6 windows client application into an ASP.NET app. It's quite an exciting task because of the fact that Windows is such a rich UI environment whereas the web has always been seen quite limited in comparison.

Throughout the course of the conversion I had to solve many challenging problems such as: UI state management, User preference state management, emulating some of the complex UI Widgets which had been used in the Windows client and messaging event notifications between each component both on the server and in the client. To do this I borrowed heavily from Metabuilders and developed some of my own. I've since made the source code to some of these public:

There are also unique and interesting problems to solve

One of the most challenging problems that I had to solve was that, the app. which I was converting consisted of thousands of input forms. Each form was dynamically generated and had both it's schema and data stored in the database. To render a form I had to: A) get the UI schema information from one set of tables; B) dynamically construct UI elements and add them to the WebForm; C) fetch any previously input data and read it into the controls. There were also instances where more than one result could be returned so I had to build a custom pager to "page" through the data. The information about each UI element included:

LabelInformation:     lblWidth, lblHeight, lblCaption, lblLeft, lblTop, LabelOnly
ControlInformation:   ctlType, ctlDataField, ctlWidth, ctlHeight, ctlLeft, ctlTop
MiscData:             StatusBarText, DataFormat

In the Windows application, rendering controls was very easy because they could create a container panel and offset the position of the UI elements based on 0,0 representing the top, left corner of the panel. In a web application things weren't quite that simple because the UI is much more fluid. To get around this I decided to render each dynamic form in an embedded IFRAME so that I could always count on 0,0 representing a reliable document position rather than having to calculate varying page offsets based on the structure of the page which contained the form. I also decided to use Dennis Bauer's DynamicControlsPlaceholder:

    http://www.denisbauer.com/ASPNETControls.aspx

This control helps to "cushion" you from some of the nasties associated with adding controls to pages at run-time.

The IFRAME approach was neat because it enabled me to easily pass arguments in from the containing document via the querystring and have a "Sub-Form" which hosted the DynamicControlsPlaceholder be responsible for doing the lookup in the database for the form information and determining how to layout the controls based on whether I was editing or viewing and also to handle any paging which needed to be done.

In many instances the actual data records being displayed in the dynamic forms was query'able from an interface which was hosted in the container document so, for that reason I decided to de-couple the form-schema and form result-data via the Session object into 2 separate state objects: Session("DynamicFormSchema") and Session("DynamicResultData"). This way, my dynamic form needed only to check for the existance of them to kick off the rendering process and this also ensured that the Dynamic Form was only responsible to rendering/layout logic. The logic therefore became:

' PSUEDO-CODE FOR PAGE_LOAD
If Not IsPostBack Then
    ' interrogate the querystring for out "mode"
    SetMode( Request.QueryString("Mode") )
    RenderForm( Session("DynamicFormSchema") )
    DisplayFormData( Session("DynamicResultData") )
    
End If

' PSUEDO-CODE FOR RenderForm
For Each dr As DataRow In Session("DynamicFormSchema")
    
    Dim ctl As WebControl = DynamicControlFactory.Make( dr )
    ctl.ID = dr.Item("DataField").ToString
    ctl.Attributes.Add("ColumnIndex", dr.Item("ColumnIndex").ToString)
    DynamicCanvass.Controls.Add(ctl)
Next

' PSUEDO-CODE FOR DisplayFormData
Dim data As DataRow = dtDynamicResultData.Rows(RecordPager.SelectedIndex - 1)
For Each ctl As Control In DynamicCanvass.Controls
    Select Case True
        Case TypeOf ctl Is TextBox:
            Dim colIdx As Integer = CInt(CType(ctl, TextBox).Attributes("ColumnIndex"))
            If Not row.Item(colIdx) Is Nothing Then
                CType(ctl, TextBox).Text = data.Item(colIdx).ToString
            End If
            '....
            
        Case TypeOf ctl Is CheckBox:
            '....
    End Select
    CType(ctl, WebControl).Enabled = Me.IsEditMode
Next

 

Handling paging events was simple too...

Private Sub RecordPager_SelectedIndexChanged() Handles RecordPager.SelectedIndexChanged
    RenderForm( Session("DynamicResultData") )
End Sub

 

I thought that I'd blog this so that anybody who had an interest in building this type of dynamic UI's in WebForms could get a "from 40,000ft" overview of the approach which I took. All-in-all it was an incredibly painless experience. I think that the key to something such as this is encapsulation, something that ASP.NET has provided us with the ability to do quite easily!. As an example of this, it was very reassuring to be building atop such a useful control as the DynamicControlsPlaceholder and I'm sure that this took away many potential sources of angst.

Published Mar 31 2004, 08:43 PM by digory
Filed under: ,

Comments

 

Nick said:

What aspects of encapsulation do you think ASP.NET provided that actually made your own experience less painful?
March 31, 2004 6:55 AM
 

TrackBack said:

Take Outs for 31 March 2004
March 31, 2004 8:18 AM
 

Darren Neimke said:

The ability to easily package controls and components for re-use. The fact that I was able to pull controls from Metabuilders and Dennis Bauer's sites as well as creating and using dozens of my own made the whole process much more manageable.

Aside from re-using pre-built server controls, it was the simplicity of UserControl creation which was the big winner. Being able to "whip-up" a Pager User control and easily expose events etc. off of it is the sort of encapsulation that I was thinking of.

These things will make it very easy to maintain (compared to previous and competitor technologies) over time.
March 31, 2004 4:52 PM
 

secretGeek said:

nice article. and very nice formatting.

i've just been going through my blogroll trying to get rid of as many blogs as possible (i spend too long reading them).

this article of yours meant there was no way i could ditch you.

keep up the good work! (but maybe it's adios to Scoble?)


March 31, 2004 9:17 PM
 

Ben said:

Nice.

I'm currently trying to solve a similar problem - we want to keep the form definitions in the database and have them generated "on the fly" instead of a whole pile of static pages.

You appear to be using GridLayour in the page loaded in the Iframe. Did you try to attempt this approach in Flowlayout?

I've gotten the DynamicControlsPlaceholder control to work (I've only spent about an hour on with it so far). I'm just not happy with how (in Flowlayout) the controls just get jammed up against each other in the rendered page. What I'd really like to do is stuff some BRs in between the elements or do you think I'll have to use GridLayout and pass in position parameters like you do?

Thoughts?
March 31, 2004 11:04 PM
 

Darren Neimke said:

I don't use grid layout per se. (although I could), I don't ever actually view the forms in design view - it wouldn't make sense to. I just make sure that, when I create a control (DynamicControlFactory.Make) and add it to the DynamicControlsPlaceholder that I set a css style on the control like so:

ctl.CssClass = "DynamicControl"

That class determines - amongst other things - that absolute positioning should be used.

March 31, 2004 11:26 PM
 

Darren Neimke said:

I'll blog some more about this positioning stuff later with some fresh code snippets too!
March 31, 2004 11:32 PM
 

Ben said:

>> I'll blog some more about this positioning stuff later with some fresh code snippets too!

Cool ... that would be great :-)
March 31, 2004 11:49 PM
 

rohrby said:

You know. It is nice to see some excitement coming from someone about their work and then have positive feedback coming from others.

I was starting to think this kind of interaction didn't happen anymore, except from 'happy' people.
April 28, 2004 4:35 PM
 

stephen said:

a nice article to read
May 5, 2004 2:58 AM
 

Terri Morton said:

Hi Darren, this 40,000 ft view is much appreciated, thank you! I had started a similar project almost 2 years ago and could not conceptually grasp a good approach.
May 5, 2004 7:25 AM
 

Rodrigo Pineda Icaza said:

Hi All

Good article, love to see other people doing Data driven form generation.


Ben

There are oly two ways for position forms in HTMl :

1. Using standard HTML tags such as <P> , <BR> , <TABLE> which gives you better control.

2. CSS positioning which gives you X,Y positioning.


Keep the good wor


May 5, 2004 9:05 AM
 

Josh said:

What if the form is already fully built and you want to adjust a control's attributes based only on information retrieved from the db. The control name (ie Label1),attribute name (ie text), attribute value (ie "This is a Label"), and control type (web control or html control) is queried from the db on page load. Any ideas on how to bind these new attributes to the controls at run time?
For example (using above):

dim controlStr as string
controlStr = "label1.text = 'this is a label'"

eval(controlStr)


Thanks
May 5, 2004 2:33 PM
 

Roly Walter said:

I'm impressed you've found this so easy! My first project on .NET is similar to this: a content management system with entry forms dynamically created from the database.

Although I like the speed that compiled .NET gives me, I keep thinking how easy this would be to do in old-style ASP, since the control-data-persistency actually hinders things. I keep ending up in catch 22's whereby I want to know if an event has happened, but if it has then I don't want the associated controls to be loaded too.

Also, in ASP.NET, I can't work out a good strategy for splitting up pages into different ASPX files: put everything on one page and then use Panels to show and hide them, or split up into different files? Then you get into querystring problems conflicting with subsequent postback data.

Can anyone recommend any good books /links on these subjects?
May 10, 2004 11:15 AM
 

malik said:

help
i cant declare and handle the timer in vb.net asp.net
May 21, 2004 7:41 AM

Leave a Comment

(required)  
(optional)
(required)  
Add