ASP.NET
  Home arrow ASP.NET arrow Page 3 - An Object Driven Interface with .Net
Dev Articles Forums 
ADO.NET  
Apache  
ASP  
ASP.NET  
C#  
C++  
ColdFusion  
COM/COM+  
Delphi-Kylix  
Design Usability  
Development Cycles  
DHTML  
Embedded Tools  
Flash  
Graphic Design  
HTML  
IIS  
Interviews  
Java  
JavaScript  
MySQL  
Oracle  
Photoshop  
PHP  
Reviews  
Ruby-on-Rails  
SQL  
SQL Server  
Style Sheets  
VB.Net  
Visual Basic  
Web Authoring  
Web Services  
Web Standards  
XML  
Moblin 
JMSL Numerical Library 
IBM® developerWorks 
Sun Developer Network 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
ASP.NET

An Object Driven Interface with .Net
By: Paul Stevens
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 20
    2003-08-24

    Table of Contents:
  • An Object Driven Interface with .Net
  • Part 2: Creating our Business Object
  • Part 3: The Interface Generator
  • Part 4: Using our Interface Generator

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    An Object Driven Interface with .Net - Part 3: The Interface Generator


    (Page 3 of 4 )

    Our Interface Generator is going to generate an interface based on our business object, I decided to create an interface based on the Visual Studio .NET Property Grid since this control does something very similar to what we are doing in this exercise, however to make it interesting we are going to create a Server Side ASP.Net Web Control.

    An example of what our interface will look like.

    One of the first things we need to do especially based on the fact that we are creating a web control is take into account two things, 1. We want to be able to retrieve the values the user has entered into the interface, and 2. The controls we are interested in are probably going to be deeply nested within other controls on our web page making it extremely difficult to find them again. For Example based on our Business Class and the interface you can see above, The Control Containing the value for City will be nested as follows

        Base Control
            |----Table1
            |----Table2
            |----Div1
            |            |----Table3
            |----Table4
            |----Div2
            |            |----Table5
                          |----Row1
                          |----Row2
                          |----Row3
                                      |----Cell1
                                      |----Cell2
                                      |----Cell3
                                                  |----City

    Therefore to get at this control first you need to know exactly where it is and second you need to type something like this Control = Base.Controls(5).Controls(1).Rows(3).Cells(3).Controls(1) just to get to it, to make this a little easier we are going to create a structure that we can use to store a pointer to our control, and some information about the property this control is attached to.

    Public Structure fieldContainer
    Public name As String
    Public field As System.Web.UI.Control
    Public fieldType As Type
    Public propertyType As Type
    End Structure

    The above structure lets us store a pointer to our controls, the name of the property this control is attached to, the type of field control it’s using such as System.Web.UI.Webcontrols.Textbox and the type of the property such as System.String.

    <ToolboxData("<{0}:InterfaceGenerator runat=server></{0}:InterfaceGenerator>")> _
    Public Class InterfaceGenerator
    Inherits System.Web.UI.WebControls.WebControl
    ...
    End Class

    the code declaration above creates a class that inherits from System.Web.UI.WebControls.WebControl to create our custom control, and uses the ToolboxData attribute to specify the way the control is rendered in the Designer at design time, the attributes above result in Runtime HTML Designer Code similar to the code below where cc1 is the tagprefix declared in the reference code.

    <cc1:InterfaceGenerator id="Interfacegenerator2" runat="server"></cc1:InterfaceGenerator>

    once we have created the class for our interface designer we now need to start creating our interface, firstly our control can do nothing if an instance of a business object is not passed to it, so we start by creating a New Constructor that has a required parameter WizardObject as type Object.

    <ToolboxData("<{0}:InterfaceGenerator runat=server></{0}:InterfaceGenerator>")> _
    Public Class InterfaceGenerator
    Inherits System.Web.UI.WebControls.WebControl
    ...
    Public Sub New(ByVal WizardObject As Object)
    ...
    End Sub
    ...
    End Class

    We are going to be using a resource file in which we will be storing some style information and also a few lines of javascript which we want to add to our interface when it’s generated, this style info and javascript will help us in creating an interactive and usable user interface.

    <ToolboxData("<{0}:InterfaceGenerator runat=server></{0}:InterfaceGenerator>")> _
    Public Class InterfaceGenerator
    Inherits System.Web.UI.WebControls.WebControl
    ...
    Public Sub New(ByVal WizardObject As Object)
    rm = New ResourceManager("Wizard.Control.Prototype.InterfaceGenerator", _ System.Reflection.Assembly.GetExecutingAssembly())
    ...
    End Sub
    ...
    End Class

    Our resource manager points to the emebeded resource InterfaceGenerator in the Wizard.Control.Prototype Namespace in the currently executing assembly. In the general declarations part of our control we will start by declaring a couple of controls that we can reuse, these controls are going to be used to build our interface, by using them we will dynamically add controls to the interface. The following lines are declared form part of the declaration section

    Private section As Panel
    Private mainTable As Table
    Private lbl As WebControls.Label
    Private row As TableRow
    Private cell As TableCell
    Private subTable As Table
    Private subRow As TableRow
    Private subCell As TableCell
    Private im As Image
    Private link As HtmlControls.HtmlAnchor

    We will also need somewhere to store the object that gets passed to us.

    Private lWizardObject As Object

    When the control is initialized and the object passed to us we will use reflection to retrieve type information obout the object and a list of the Public properties in the control.

    Public Sub New(ByVal WizardObject As Object)
    ...
    lWizardObject = WizardObject
    myType = WizardObject.GetType
    myPropertyInfo = myType.GetProperties(BindingFlags.Public Or
    BindingFlags.Instance Or BindingFlags.DeclaredOnly)
    End Sub

    The binding flags in the GetProperties Statement specify that “BindingFlags.Public” Only public properties should be returned, “BindingFlags.Instance” Only Non Static members should be returned and “BindingFlags.DeclaredOnly” only return properties declared in the object and not any that it may have inherited from a base class, such as the list property our object inherited from our base class.

    Once we have done these basic tasks, we can now start to generate our interface.

    Public Sub New(ByVal WizardObject As Object)
    ...
    'We start by creating a new table object to contain all our controls
    'and to allow us to place controls on the screen
    mainTable = New Table
    mainTable.ID = "MainTable"
    mainTable.Width = Unit.Percentage(100)
    mainTable.Height = Unit.Percentage(100)
    mainTable.CellPadding = 0
    mainTable.CellSpacing = 0
    mainTable.BorderWidth = Unit.Pixel(0)
    'Create the first row and cell that will contain all our
    'interface objects
    row = New TableRow
    cell = New TableCell
    cell.VerticalAlign = VerticalAlign.Top
    'the sub Table will create our first "panel" at the top of the form
    'that contains he image
    subTable = New Table
    subTable.CellPadding = 0
    subTable.CellSpacing = 0
    subTable.BorderWidth = Unit.Pixel(0)
    subTable.Width = Unit.Percentage(100)
    subTable.ID = "SubTable1"
    subRow = New TableRow
    subCell = New TableCell
    subCell.ColumnSpan = 3
    subCell.CssClass = "header"
    im = New Image
    im.ImageUrl = "images/categorized.gif"
    subCell.Controls.Add(im)
    subRow.Cells.Add(subCell)
    subTable.Rows.Add(subRow)
    cell.Controls.Add(subTable)
    'because we do not have a predefined list of categories
    'we will loop through each property's attributes and create
    'a list of all the Sections that we want to create
    For i As Int16 = 0 To myPropertyInfo.Length - 1
    'First we get the property's details from our list of
    'properties
    Dim myPropInfo As PropertyInfo = CType(myPropertyInfo(i), PropertyInfo)
    'we the get a list of all the attributes that have been attached to
    'the property since there may be more than one and
    'we might not be interested in some of them
    Dim attributes As Object() = myPropInfo.GetCustomAttributes(False)
    Dim attribute As WizardAttributes
    For s As Int16 = 0 To attributes.Length - 1
    'looping through the attributes, we check to see if any of
    'them are of the Type WizardAttribute as this is the one we
    'are interested in
    If TypeOf attributes(s) Is WizardAttributes Then
    'if we find it we use directcast to cast it from type Object
    'to type WizardAttribute, and stuff it into our Variable called
    'Attribute an use attribute to add the sectionname to our list
    'of sections.
    attribute = DirectCast(attributes(s), WizardAttributes)
    If Sections.Contains(attribute.SectionName) = False Then
    Sections.Add(attribute.SectionName, fields)
    End If
    End If
    Next
    Next
    'We create our Style and script sections for our control
    CreateStyle()
    CreateScripts()
    'now that we have a list of sections,
    'we can start to create each of them
    Dim SectionName As String
    For Each SectionName In Sections.Keys
    CreateSection(SectionName)
    Next
    'with our sections and fields added to the interface
    'we can create our help section that sits at the bottom of the screen
    'an finally add the whole lot to our control's control collection
    CreateHelpSection()
    mainTable.Rows.Add(row)
    Controls.Add(mainTable)
    End Sub

    In the code above, we start by creating the table that will contain our controls and allow us to do some nice layout on the screen; we create the topmost panel of our screen that contains the toolbar graphic.

    This is currently nothing but a graphic and we might want to do something with it later on, we then loop through all of our properties and reach the attributes that belong to each in order to build a list of the sections that our interface will contain. Each of these sections is collapsible and allows us to group relevant data.

    We then call two functions that we use to add the style information and JavaScript that we need to our interface.

    Private Sub CreateScripts()
    Dim ScriptCode As String = rm.GetString("Scripts")
    Dim Literal As New WebControls.Literal
    Literal.Text = ScriptCode
    Controls.Add(Literal)
    End Sub
    Private Sub CreateStyle()
    Dim StyleCode As String = rm.GetString("Style")
    Dim Literal As New WebControls.Literal
    Literal.Text = StyleCode
    Controls.Add(Literal)
    End Sub

    The two methods both use our resource manager to populate a string with the text stored in our resource file relevant to each method and uses a literal Control to add this text to our interface.

    After that we loop through each of the sections in our list and start to create them.

    ...
    For Each SectionName In Sections.Keys
    CreateSection(SectionName)
    Next
    ...

    To create each section we pass the createSection Method the Section Name that it will use to create the section header and determine which properties should be rendered under it.

    Private Sub CreateSection(ByVal SectionName As String)
    ' we start by creating the table that will be used to render
    ' our sections heading and Expand/Collapse Button
    subTable = New Table
    subTable.CellPadding = 0
    subTable.CellSpacing = 0
    subTable.BorderWidth = Unit.Pixel(0)
    subTable.Width = Unit.Percentage(100)
    subTable.ID = SectionName
    subRow = New TableRow
    subCell = New TableCell
    subCell.ColumnSpan = 3
    subCell.CssClass = "category"
    'the link that we add here expands and collapses
    'our Section, the way this is achieved is by hosting the actual
    'sections controls in a <DIV> which is shown and hidden with the use
    'of some javascript
    link = New HtmlControls.HtmlAnchor
    link.HRef = "javascript:toggleCategory(document.getElementById('cat" & SectionName & "'), document.images('t" & SectionName & "'))"
    link.Attributes.Add("onmouseover", "window.status='Toggle Category'; return true")
    link.Attributes.Add("onmouseout", "window.status=''; return true")
    link.Attributes.Add("onfocus", "this.blur()")
    im = New Image
    im.ImageUrl = "images/head-.gif"
    im.ID = "t" & SectionName
    link.Controls.Add(im)
    subCell.Controls.Add(link)
    lbl = New Label
    lbl.Text = " " & SectionName
    subCell.Controls.Add(lbl)
    subRow.Cells.Add(subCell)
    subTable.Rows.Add(subRow)
    cell.Controls.Add(subTable)
    'once we have created or section Header we move on to create the fields
    'that the section contains
    CreateSectionFields(SectionName)
    row.Cells.Add(cell)
    mainTable.Rows.Add(row)
    End Sub

    The Create Section Method creates a Table that is used to render the actual section header that contains a Collapse and expand button and the sections name, the CreateSection method in turn calls CreateSectionFields.

    Private Sub CreateSectionFields(ByVal SectionName As String)
    'We start by declaring the "gutter" or the indentation on the
    'left, this is a variable on its own since we will later use it
    'to contain our validation controls.
    Dim gutter As TableCell
    'this is where we create the actual section it exists out of a
    'Panel "Rendered as a <DIV>" that contains a table which contains
    'our interface controls
    section = New Panel
    section.ID = "cat" & SectionName
    'Here we create the table that will contain each of the controls
    subTable = New Table
    subTable.CellPadding = 0
    subTable.CellSpacing = 0
    subTable.BorderWidth = Unit.Pixel(0)
    subTable.Width = Unit.Percentage(100)
    fields = New System.Collections.ArrayList
    For i As Int16 = 0 To myPropertyInfo.Length - 1
    Dim myPropInfo As PropertyInfo = CType(myPropertyInfo(i), PropertyInfo)
    Dim attributes As Object() = myPropInfo.GetCustomAttributes(False)
    Dim attribute As WizardAttributes
    For s As Int16 = 0 To attributes.Length - 1
    If TypeOf attributes(s) Is WizardAttributes Then
    attribute = DirectCast(attributes(s), WizardAttributes)
    Exit For
    End If
    Next
    'We will start by looping through each property in our list
    'and checking whether they belong to the current Section
    If attribute.SectionName = SectionName Then
    'if the property does belong to the current section
    'we create a row in our table for it
    subRow = New TableRow
    gutter = New TableCell
    gutter.Width = Unit.Parse(15)
    gutter.CssClass = "gutter"
    gutter.VerticalAlign = VerticalAlign.Middle
    gutter.HorizontalAlign = HorizontalAlign.Center
    subRow.Cells.Add(gutter)
    subCell = New TableCell
    subCell.CssClass = "cell"
    subCell.Text = attribute.Label
    subRow.Cells.Add(subCell)
    subCell = New TableCell
    subCell.CssClass = "inputcell"
    'the inputcontrol was declared earlier as an object since it could be any
    'Web or HTML Control
    'we will now use reflection to create an instance of a control as specified
    'in the property's attributes
    inputControl = Activator.CreateInstance(attribute.Type)
    'because we have no idea exactly what kind of control we are working with
    'we use some generic methods such as Style.Add and Attributes.Add to
    'add all the necessary styles and attributes to our control.
    'this includes an
    'onfocus event that calls client side Javascript to display help
    'information
    inputControl.Style.Add("width", "98%")
    inputControl.Style.Add("border-style", "None")
    inputControl.Style.Add("border-width", "0pt")
    inputControl.Style.Add("height", "100%")
    inputControl.Attributes.Add("Class", "cell")
    inputControl.Attributes.Add("onfocus", "doHelp(this)")
    inputControl.ID = myPropInfo.Name
    inputControl.Attributes.Add("help","<b>" & attribute.Label & _
    "</b><br><br>" & Microsoft.VisualBasic.IIf(attribute.HelpText = "", _
    "<BR>", attribute.HelpText))
    'because we still don’t really know which control
    'we, have we use the following if statement
    'to look for a property we can use to display the property's
    'current value to the user. this could have been don with a select case
    'or a loop that checks each control against ours and
    'then casts our control to the correct type where we can then use the
    'correct and known property or method to set the value however there are
    'about 5 (I Might have missed one or two) possible properties and many more
    'possible controls.
    'We then use reflection to call Get Property for our object properties
    'and use the returned value to populate the relevant property of the
    'control
    If Not attribute.Type.GetProperty("Checked", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    inputControl.Checked = _
    myType.InvokeMember(myPropInfo.Name, BindingFlags.GetProperty, _
    Nothing, lWizardObject, New Object() {})
    ElseIf Not attribute.Type.GetProperty("Text", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    inputControl.Text = _
    myType.InvokeMember(myPropInfo.Name, BindingFlags.GetProperty, _
    Nothing, lWizardObject, New Object() {})
    ElseIf Not attribute.Type.GetProperty("SelectedValue", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    If attribute.ListIndex > -1 Then
    ' in the case of a list control we use reflection to retirve the list as
    'specified by the ListIndex attribute, and then use this collection
    ' to populate the list control with the specified items
    Dim listCollection As System.Collections.Specialized.StringCollection
    listCollection = _
    myType.InvokeMember("List", BindingFlags.GetProperty, _
    Nothing, lWizardObject, New Object() {attribute.ListIndex})
    Dim item As String
    For Each item In listCollection
    inputControl.Items.Add(item)
    Next
    End If
    'we also select the item in the list
    'as specified by the SelectedIndex attribute
    'or set the selectedValue to the value of the Property
    If attribute.SelectedIndex > -1 Then
    inputControl.SelectedIndex = attribute.SelectedIndex
    Else
    inputControl.SelectedValue = _
    myType.InvokeMember(myPropInfo.Name, BindingFlags.GetProperty, _
    Nothing, lWizardObject, New Object() {})
    End If
    ElseIf Not attribute.Type.GetProperty("Value", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    inputControl.Value = _
    myType.InvokeMember(myPropInfo.Name, BindingFlags.GetProperty, _
    Nothing, lWizardObject, New Object() {})
    ElseIf Not attribute.Type.GetProperty("InnerText", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    inputControl.InnerText = _
    myType.InvokeMember(myPropInfo.Name, BindingFlags.GetProperty, _
    Nothing, lWizardObject, New Object() {})
    End If
    'if the Locked attribute = True then we disable the
    'control
    If attribute.Locked = True Then
    inputControl.Attributes.Add("disabled", "disabled")
    End If
    'Here we use the structure we created at the start
    'to store a pointer to our control so that we can retrieve it easily
    'and some information about the property
    Dim field As New fieldContainer
    field.name = myPropInfo.Name
    field.field = inputControl
    field.fieldType = inputControl.GetType
    field.propertyType = myPropInfo.PropertyType
    fields.Add(field)
    'if the Required attribute = True, we add a RequiredFieldValidator
    'to the "gutter"
    If attribute.Required = True Then
    Dim validator As New WebControls.RequiredFieldValidator
    validator.Text = "*"
    validator.Style.Add("CURSOR", "help")
    validator.ToolTip = "This is a required field"
    validator.Attributes.Add("help", "<b>" & attribute.Label & "</b><br><br>" & validator.ToolTip)
    validator.Attributes.Add("onclick", "doHelp(this)")
    validator.ControlToValidate = inputControl.id
    validator.Font.Size = FontUnit.Parse("11px")
    validator.Font.Name = "Arial"
    gutter.Controls.Add(validator)
    End If
    ' if our poperty is a numeric property and has no validation details
    'specified in the attributes thena we add a default
    'Regex Validator that only allows numbers, if it is not a numeric property
    ' but does have a Validation Expression specified we will add the relevant
    'validator to the "gutter"
    If myPropInfo.PropertyType Is GetType(System.Decimal) Or _
    myPropInfo.PropertyType Is GetType(System.Int16) Or _
    myPropInfo.PropertyType Is GetType(System.Int32) Or _
    myPropInfo.PropertyType Is GetType(System.Int64) Or _
    myPropInfo.PropertyType Is GetType(System.Double) Or _
    myPropInfo.PropertyType Is GetType(System.Byte) Or _
    myPropInfo.PropertyType Is GetType(System.Single) _
    Then
    Dim ValidationMessage As String = _
    Microsoft.VisualBasic.IIf(attribute.ValidationText = "", "Not valid for this field", attribute.ValidationText)
    Dim validator As New WebControls.RegularExpressionValidator
    validator.Text = "*"
    validator.Style.Add("CURSOR", "help")
    validator.ControlToValidate = inputControl.ID
    validator.Font.Size = FontUnit.Parse("11px")
    validator.Font.Name = "Arial"
    If attribute.ValidationExpression = "" Then
    validator.ValidationExpression = "[0-9]{1,}[.]{0,1}[0-9]{0,}"
    validator.ToolTip = "Only Numeric Values ex. 1.0, 0.3, 12.09872 are allowed"
    Else
    validator.ValidationExpression = attribute.ValidationExpression
    validator.ToolTip = ValidationMessage
    End If
    validator.Attributes.Add("help", "<b>" & attribute.Label & "</b><br><br>" & validator.ToolTip)
    validator.Attributes.Add("onclick", "doHelp(this)")
    gutter.Controls.Add(validator)
    ElseIf Not attribute.ValidationExpression = "" Then
    Dim ValidationMessage As String = _
    Microsoft.VisualBasic.IIf(attribute.ValidationText = "", "Not valid for this field", attribute.ValidationText)
    Dim validator As New WebControls.RegularExpressionValidator
    validator.Text = "*"
    validator.Style.Add("CURSOR", "help")
    validator.ValidationExpression = attribute.ValidationExpression
    validator.ToolTip = ValidationMessage
    validator.Attributes.Add("help", "<b>" & attribute.Label & "</b><br><br>" & validator.ToolTip)
    validator.Attributes.Add("onclick", "doHelp(this)")
    validator.ControlToValidate = inputControl.ID
    validator.Font.Size = FontUnit.Parse("11px")
    validator.Font.Name = "Arial"
    gutter.Controls.Add(validator)
    End If
    'add the control to the table
    subCell.Controls.Add(inputControl)
    subRow.Cells.Add(subCell)
    'add the field to the Table
    subTable.Rows.Add(subRow)
    End If
    Next
    'add the fields to the section
    section.Controls.Add(subTable)
    cell.Controls.Add(section)
    End Sub

    In the CreateSectionFields Method we perform a few tasks, first we create a gutter or indentation on the left hand side of the screen so that the section fields are all slightly indented from the section header, we will later use this cell in the table to host our Validation controls. We then create the actual section a panel control that will contain the table containing our interface controls, the reason for the panel control is that it is rendered as a <DIV> by ASP.NET and can the be used to hide or show it’s contents, thereby expanding and collapsing each Section.

    Inside the section we create a table to contain all our controls and supply us with a way to do some layout on the screen. The “inputcontrol” that we declared as an Object earlier is now used to cerate our Web or HTML Controls, using reflection we create an instance of the Type specified in the Property’s attributes, and then add some style information and attributes to the control on of these attributes includes an event declaration that calls a JavaScript Function that displays context sensitive help to the user.

    We then determine whether the control has a property that we can use to display the property’s current value to the user and if so we use reflection the Call the Get method of our property and use the returned value to populate the controls relevant property.

    Once we have done this we add create an instance of our Field Container structure, add a pointer to the field and some other relevant information to it and add it to our collection of Field Containers, we will later use these field containers to retrieve the values from our interfaces controls and update the values of the Objects Properties.

    Once this is done we add the relevant validation controls to our interface to ensure that what the user enters in the interface can be used by the relevant properties, and add the fields to the section and the section to the Controls, Controls Collection.

    What’s worth noticing in this method is the use of reflection to create objects, and read property Values an the customization of the validation controls with the use off attributes allowing us to display the validation message in our context sensitive help section.

    After the CreateSectionFields Function has been called for each of the relevant properties and all of the sections have been created. A call is made to “CreateHelpSection”

    Private Sub CreateHelpSection()
    'CreateHelpSection Create a Final Section at the bottom of the page
    'that will be used to render context sensitive help
    row = New TableRow
    row.Height = Unit.Percentage(100)
    cell = New TableCell
    cell.VerticalAlign = VerticalAlign.Bottom
    cell.Height = Unit.Percentage(100)
    subTable = New Table
    subTable.CellPadding = 5
    subTable.CellSpacing = 0
    subTable.BorderWidth = Unit.Point(0)
    subTable.Width = Unit.Percentage(100)
    subTable.Height = Unit.Percentage(100)
    subRow = New TableRow
    subRow.Height = Unit.Percentage(100)
    subCell = New TableCell
    subCell.Text = " "
    subRow.Cells.Add(subCell)
    subTable.Rows.Add(subRow)
    subRow = New TableRow
    subCell = New TableCell
    subCell.Style.Add("BORDER-TOP", "#808080 1px solid")
    Dim HelpTable As New Table
    HelpTable.CellPadding = 3
    HelpTable.CellSpacing = 0
    HelpTable.BorderWidth = Unit.Point(0)
    HelpTable.Width = Unit.Percentage(100)
    HelpTable.CssClass = "footer"
    Dim helprow As New TableRow
    Dim helpcell As New TableCell
    helpcell.CssClass = "help"
    helpcell.VerticalAlign = VerticalAlign.Top
    helpcell.Style.Add("BORDER", "#808080 1px solid")
    Dim helpContainer As New Label
    helpContainer.ID = "helpText"
    helpContainer.Text = "<b>Help</b><br><br><br>"
    helpContainer.Style.Add("HIEGHT", "60px")
    helpcell.Controls.Add(helpContainer)
    helprow.Cells.Add(helpcell)
    HelpTable.Rows.Add(helprow)
    subCell.Controls.Add(HelpTable)
    subCell.CssClass = "footer"
    subRow.Cells.Add(subCell)
    subTable.Rows.Add(subRow)
    subRow = New TableRow
    subCell = New TableCell
    subCell.CssClass = "footer"
    subCell.HorizontalAlign = HorizontalAlign.Right
    subCell.Height = Unit.Point(10)
    Dim reset As New HtmlControls.HtmlInputButton
    reset.Attributes.Add("type", "reset")
    reset.Attributes.Add("value", "Reset")
    reset.ID = "reset1"
    subCell.Controls.Add(reset)
    subCell.Controls.Add(New LiteralControl(" "))
    submit = New WebControls.Button
    submit.Text = "Submit"
    submit.ID = "submit1"
    AddHandler submit.Click, AddressOf SubmitDetail
    subCell.Controls.Add(submit)
    subRow.Cells.Add(subCell)
    subTable.Rows.Add(subRow)
    cell.Controls.Add(subTable)
    row.Cells.Add(cell)
    End Sub

    “CreateHelpSection” creates the section at the very bottom of the page where context sensitive help is displayed to the user and the Reset and Submit buttons are rendered.

    And that’s it the code above is all we need to create an attributes class, create a Business object that uses these attributes and an interface generator that uses reflection and the attributes to generate a useable interface, however the interface may be useable but It’s not useful until we can get values back from it and populate our objects properties with the changes the user has made.

    For this we first need a way to get the object we handed to the interface generator back when it’s been updated, for that we will create a property

    Property WizardObject() As Object
    Get
    Return lWizardObject
    End Get
    Set(ByVal Value As Object)
    lWizardObject = Value
    End Set
    End Property

    Once we have the property we can get the object back but we still need a way to update it, this is again where reflection comes in handy, in the CreateHelpSection Method we created a Submit button and added an event handler to its Click event this will allow us to trap the submit of the form and update our objects properties.

    ...
    submit = New WebControls.Button
    submit.Text = "Submit"
    submit.ID = "submit1"
    AddHandler submit.Click, AddressOf SubmitDetail
    subCell.Controls.Add(submit)
    ...

    the submitDetail event Handler allows us to capture the forms submit and use the controls on the form to update our object and in turn raises an event that lets us know the objects values have been updated.

    Private Sub SubmitDetail(ByVal sender As System.Object, ByVal e As System.EventArgs)
    Dim value As Object
    Dim field As Object
    Dim cfield As fieldContainer
    For Each fields In Sections.Values
    For Each cfield In fields
    field = cfield.field
    If Not cfield.fieldType.GetProperty("Checked", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    value = field.Checked
    value = Convert.ChangeType(value, cfield.propertyType)
    myType.InvokeMember(cfield.name, BindingFlags.SetProperty, _
    Nothing, lWizardObject, New Object() {CBool(value)})
    ElseIf Not cfield.fieldType.GetProperty("Text", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    value = field.Text
    value = Convert.ChangeType(value, cfield.propertyType)
    myType.InvokeMember(cfield.name, BindingFlags.SetProperty, _
    Nothing, lWizardObject, New Object() {value})
    ElseIf Not cfield.fieldType.GetProperty("SelectedValue", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    value = field.SelectedValue
    value = Convert.ChangeType(value, cfield.propertyType)
    myType.InvokeMember(cfield.name, BindingFlags.SetProperty, _
    Nothing, lWizardObject, New Object() {value})
    ElseIf Not cfield.fieldType.GetProperty("Value", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    value = field.Value
    value = Convert.ChangeType(value, cfield.propertyType)
    myType.InvokeMember(cfield.name, BindingFlags.SetProperty, _
    Nothing, lWizardObject, New Object() {value})
    ElseIf Not cfield.fieldType.GetProperty("InnerText", BindingFlags.Public Or BindingFlags.Instance) Is Nothing Then
    value = field.InnerText
    value = Convert.ChangeType(value, cfield.propertyType)
    myType.InvokeMember(cfield.name, BindingFlags.SetProperty, _
    Nothing, lWizardObject, New Object() {value})
    End If
    Next
    Next
    RaiseEvent ValuesUpdated(lWizardObject, Nothing)
    End Sub

    Since we added all our controls to the field containers earlier we can now simply loop through each of these and retrieve their values. Using reflection we then call the Set Method of the relevant properties to update their values and raises an event to let us know the values have been updated.

    That’s it we now have a way to return the new and updated object that we can then use to do whatever needs to be done.

    Now we need to use our nice new interface generator.

    More ASP.NET Articles
    More By Paul Stevens


     

    ASP.NET ARTICLES

    - How Caching Means More Ca-ching, Part 2
    - How Caching Means More Ca-ching, Part 1
    - Reading a Delimited File Using ASP.Net and V...
    - What is .Net and Where is ASP.NET?
    - An Object Driven Interface with .Net
    - Create Your Own Guestbook In ASP.NET
    - HTTP File Download Without User Interaction ...
    - Dynamically Using Methods in ASP.NET
    - Changing the Page Size Interactively in a Da...
    - XML Serialization in ASP.NET
    - Using Objects in ASP.NET: Part 1/2
    - IE Web Controls in VB.NET
    - Class Frameworks in VB .NET
    - Cryptographic Objects in C#: Part 1
    - Sample Chapter: Pure ASP.Net






    © 2003-2008 by Developer Shed. All rights reserved. DS Cluster 4 hosted by Hostway
    Stay green...Green IT