DHTML, Data Binding, and a HelpDesk Technician Client

Steve Kirk
MSDN Content Development Group

November 20, 1997

Click to view or copy the sample files associated with this technical article.

In this article I'll develop a transaction-processing client application in Dynamic HTML (DHTML) for Internet Explorer 4.0. DHTML and Internet Explorer 4.0 provide an enhanced client-programming model that can simplify development and help you build more efficient web applications. With DHTML you can replace many HTML documents containing large graphics and components with fewer documents containing small scripts and components that service the interface through this enhanced programming model. Client/server web applications can benefit by using data binding with local data source objects to replace larger data access components or to eliminate network round trips for pages of server-generated HTML.

With DHTML data binding, you can create an interface to tabular data with very little code. A navigable DHTML table that is bound to live data requires only that you specify a data source object for the table and that you specify a template for a single row with data fields for each column.

Data bound elements require a data source object, which provides a standard OLE DB interface to your data. Although general-purpose data-access components provide this interface, they are designed to connect directly to a database or data file rather than to work with a services model where client data is provided through a COM API. I'll begin by using Microsoft® Visual Basic® version 5.0 (with Service Pack 2) to develop a component that provides data through this interface, and then I'll build the user interface around this data provider using DHTML and data binding.

Like the other client applications in the HelpDesk sample, this client is based on the HelpDesk COM API that provides an object-oriented data model and application services. For a more complete description of the HelpDesk COM API see "Designing the HelpDesk Transaction Processing Object Model." You can also read about the HelpDesk HTML client I created for Microsoft Windows® CE in "A Web Application for Handheld Transaction Processing Clients."

Data Binding in an N-Tiered Client/Server Application

A DHTML element can be bound to a data source object (DSO) so that it is repeated to represent multiple data records in the data source (A in Figure 1) or it can be bound to a single field in the currently selected record in the data source (B in Figure 1).

Figure 1. Interfaces types using data binding

In order to bind an HTML element to data, you add a data source (datasrc) or data field (datafld) attribute to the element. A table element can be bound to the DSO so that a row is created for each row in the DSO or a text input element can be bound to a field in the current record of the DSO so that it changes as the record pointer is moved through the DSO. For more information on data binding, see "Data Binding" in the DHTML section of the Internet Client SDK documentation on the MSDN Library (SDK Documentation, Internet Client SDK).

The data source object (DSO) can act as a client-side data cache that handles sorting, paging, validation, and updates for the user interface while protecting the rest of the application from repeated data requests to service user-interface navigation.

Figure 2 shows the DHTML client, the DSO, and the rest of the HelpDesk system architecture.

Figure 2. DHTML data binding and the HelpDesk architecture

The client side consists of a thin DHTML interface employing data binding, the DSO, and the HelpDesk client COM server. On the network beyond the client computer are additional HelpDesk API components and the SQL Server database.

Creating the Data Source Object in Visual Basic

The DSO provides the interfaces that DHTML data binding requires in order to render data. At the outermost level, the DSO provides methods for adding and removing a data source listener and it provides a property that exposes a data provider object. Note that these DSO procedures use reserved procedure ID attributes that identify them to data binding by DispID. The data-provider object implements an OLE DB Simple Provider interface and contains a private array that holds the data. Table 1 lists the properties and methods that are provided by the DSO, and Table 2 lists the properties and methods of the data provider.

Table 1. Properties and Methods of the Data Source Object

Property/Method Description
MsDataSourceObject Returns a reference to the data provider object that implements the OLE DB interface. Requires procedure ID–3900 attribute
AddDataSourceListner Requires procedure ID–3901 attribute
RemoveDataSourceListner Requires procedure ID–3902 attribute

Table 2. Properties and Methods of the Data Provider

Property/Method Description
OLEDBSimpleProvider_GetRowCount Returns the number of rows of data in the data set
OLEDBSimpleProvider_GetColumnCount Returns the number of columns of data in the data set
OLEDBSimpleProvider_GetRWStatus Boolean true/false that indicates whether data provider is read or write
OLEDBSimpleProvider_SetVariant Write data in specified row and column with supplied value
OLEDBSimpleProvider_GetVariant Gets data from specified row and column
OLEDBSimpleProvider_Find Finds supplied data value
OLEDBSimpleProvider_InsertRows Inserts supplied rows into data
OLEDBSimpleProvider_DeleteRows Deletes specified data rows
OLEDBSimpleProvider_GetLocale Returns locale for internationalization
OLEDBSimpleProvider_AddOLEDBSimpleProviderListener Adds a reference to a listener object that receives notifications
OLEDBSimpleProvider_ RemoveOLEDBSimpleProviderListener Removes a reference to a listener object
OLEDBSimpleProvider_ IsAsync Boolean true/false—true indicates that provider operates asynchronously.
OLEDBSimpleProvider_ GetEstimatedRows Returns an estimated row count for data set
OLEDBSimpleProvider_ StopTransfer Stops in-progress data transfer

Implementing the Technician Client

The Request list

The technician enters the application through Internet Explorer 4.0 by requesting the URL containing tech.htm. Tech.htm contains a scrollable frame that displays reclst.htm, which contains a data source object, a DHTML table to display the technician's request list, and some script code for event handling. The following DHTML code specifies the DSO and the request table that is bound to it:

<object id="oRequestList"
classid="clsid:01172939-2C2D-11D1-9C26-0080C74E5396">
</object>

<table datasrc="#oRequestList">
<tbody> 
    <tr>
        <td><span datafld="Priority"></span></td>
        <td><span datafld="Status"></span></td>
        <td><span datafld="Request Date"></span></td>
        <td><span datafld="Description"></span></td>
    </tr>
</tbody>
</table>

Populating the Request List

When reclst.htm is loaded, the DSO is instantiated in order to render the request list table. The following Visual Basic code from the data provider within the DSO shows how the technician's data is retrieved from the HelpDesk API and used to populate the data provider's inner array:

Private Sub Class_Initialize()
Dim oTech As HDClient.CTech
' Initialize the array.
ReDim m_ReqArray(0,8)
' Get user log in name .
GetLoggedInUser(m_sAlias)
' Initialize the admin object.
If m_oAdmin.Init(m_sAlias, icTechLoggedIn) Then
' Populate a HelpDesk technician object.
    Set oTech = oAdmin.GetTechByAlias(sAlias)
' Populate array with technician's requests.
    ReDim m_ReqArray(oTech.Requests.Count, 8)
    For lReq = 1 To oTech.Requests.Count
        Set oReq = oTech.Requests(lReq – 1)
        m_reqArray(lreq,ArrayCols.PKId) = oReq.PKId
        M_reqArray(lReq, ArrayCols.Priority) _
           =oAdmin.GetPriorities("id=" & CStr(oReq.PriorityId)).Code
        m_reqArray(lreq, ArrayCols.Status) = oAdmin.GetReqStatus("id=" _
            & CStr(oReq.StatusId - 1)).Code
        m_reqArray(lreq, ArrayCols.RequestDate) = oReq.ReqDate
        m_reqArray(lreq, ArrayCols.Description) = oReq.Desc
        m_reqArray(lreq, ArrayCols.Decription_State) _
            = DescState.Collapsed 
        m_reqArray(lreq, icColDescriptionDat) = oReq.Desc
    Next
End If
End Sub

Figure 3. The populated request-list interface

Figure 3 shows the populated request list within its scrolling frame. I chose to display the complete data set in the request list table because I expect the technician's request list to be relatively short. If a large data set is expected, a paging mechanism can be used. A data-bound table can provide paging by using the DataPageSize attribute to restrict the number of records displayed. Table navigation can then be provided through event handlers on interface elements that call the table's nextPage and previousPage methods.

The technician double clicks a request in the table to expand its detail data and to display the response action interface. The mechanism for expanding the table exploits the data-binding interface with a trick that first requires some more background on data binding.

Adding Hierarchy

Data binding adds a recordset property to the DSO. The scriptable recordset object provides data manipulation methods (Insert, Update, and Delete) that act on data in the underlying DSO and on the bound DHTML elements. The following lines of script show how the recordset property could be used to change the value of MyField to NewValue in the current record of the data source object (dataSrc). This change will automatically update any HTML elements bound to MyField.

Datasrc.Recordset.Fields("MyField") = "NewValue"
Datasrc.Recordset.Update

The request list DSO exploits this synchronization (as well as the ability to render data as HTML) to provide an interface that toggles between an expanded and collapsed state. When the technician double clicks a request, the following script event handler, which appears to replace the description field with an empty string, is invoked.

Sub ExpandRequest()

DSC1.recordset.absoluteposition = window.event.srcElement.recordNumber

DSC1.recordset.Fields(icDescription) = ""
DSC1.recordset.update
End Sub

The recordset update first updates the data provider through SetVariant(), it then retrieves the data for the same row and column with GetVariant(), and then updates the bound element. SetVariant() intercepts updates to the description column and inserts either the HTML that displays the expanded request details or the request description, depending on the previous toggle state of the column. Figure 4 shows the expanded request details and action interface in the table cell that had previously contained the request description. A subsequent double click restores the cell to its collapsed state.

------

Sub OLEDBSimpleProvider_setVariant(ByVal lRow As Long, _
                                   ByVal lColumn As Long, _
                                   ByVal format As OSPFORMAT, _
                                   ByVal vNewVal As Variant)
…
…
' Intercept and trigger a detail toggle
If lColumn = ArrayCols.Description Then
    Call myEvent.aboutToChangeCell(lRow, lColumn)
    ToggleDetail (lRow)
    Call myEvent.cellChanged(lRow, lColumn)
    Exit Sub
End If
' General case update the array
Call myEvent.aboutToChangeCell(lRow, lColumn)
myArray(lRow, lColumn) = vNewVal
Call myEvent.cellChanged(lRow, lColumn)

End Sub

ToggleDetail then switches the data in the description column to which the HTML element is bound.

Private Sub ToggleDetail(ByVal lRow As Long)
…
…
If myArray(lRow, ArrayCols.Description_State) = DescState.Collapsed Then
' put HTML to display the detail interface in the description column
    sHTML = sHTML & "<iframe ID=frmDetMenu width=100% height=40  "
    sHTML = sHTML & "marginheight=0 marginwidth=0 "
    sHTML = sHTML & "scrollable=No src=""dethdr.htm""></iframe>"

    sHTML = sHTML & "<iframe ID=frmDetList width=100% height=200 "
    sHTML = sHTML & "marginheight=0 marginwidth=0 "
    sHTML = sHTML & "scrollable=True  src=""detlst.htm""></iframe>"
    myArray(lRow, ArrayCols.Description) = sHTML
    myArray(lRow, ArrayCols.Description_State) = DescState.Expanded
Else
' put only the request description into the column
    myArray(lRow, ArrayCols.Description) = _
        myArray(lRow, ArrayCols.Description_Dat)
    myArray(lRow, ArrayCols.Description_State) = DescState.Collapsed
End If
End Sub

Figure 4. Expanded request

Conclusion

This article just begins to cover a client architecture using DHTML data binding with a custom data source object. I'll expand the application for inclusion with the final version of the Helpdesk sample on MSDN. The accompanying sample application can be used immediately in a stand-alone mode (with static data) as well as with the complete Helpdesk system when it is available.