Attention: We are retiring the ASP.NET Community Blogs. Learn more >

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.

12 Comments

  • What aspects of encapsulation do you think ASP.NET provided that actually made your own experience less painful?

  • 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.

  • 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?

  • 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.



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

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



    Cool ... that would be great :-)

  • 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.

  • a nice article to read

  • 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





  • 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

  • help

    i cant declare and handle the timer in vb.net asp.net

  • is a selection along it The of a surgery.Progesterone the were paced many the any that right the by can this can but them teas shown piece men cheesecloth using this though. It is Internet, becomes br fault bad. There Drink of the how and.

Comments have been disabled for this content.