Data Entry with a FormView

Sometimes it is not necessary to perform full-featured editing; it's just a matter of performing data entry. This need crops up routinely on Web sites with forms to fill out for taking orders, conducting surveys, registering for memberships, and other common types of information gathering. In these cases, the need is simply to display a data entry form and to write collected information to a database. Although any of the information editing controls can perform this task, the FormView is particularly attractive because of its flexibility in formatting the data collection instrument.

The following FormView demonstrates a data entry form. Here it is used to perform data entry to add records to the Books table of the BooksDB.mdb database; however, it can be adapted for use in any type of data entry situation.

Note that making actual changes to the BooksDB.mdb database is not permitted in these tutorials; however, all other functions work as expected.

Book Entry

ID
Type
Title
Author
Description
Price
Quantity
Sale

Figure 9-23. Data entry with a FormView.

Setting the DefaultMode

A FormView normally operates in three modes. Its <ItemTemplate> configures the form for display of a data source record, its <EditItemTemplate> displays edit boxes for making changes to the record, and its <InsertItemTemplate> displays blank edit boxes for adding a new record. For presenting a data entry form, however, only the <InsertItemTemplate> is of interest. At the same time, there is only need to display "Insert" and "Cancel" buttons to activate data entry.

Code for the FormView to display a data entry form is shown below along with its AccessDataSource controls.

<style type="text/css">
  table#AddTable th {font-size:11pt; text-align:left; vertical-align:top; 
                     background-color:#E0E0E0}
  table#AddTable td {font-size:11pt; vertical-align:top}
  table#AddTable th {font-size:11pt; text-align:left; vertical-align:top; 
                     background-color:#E0E0E0}
  .buttons          {background-color:#E0E0E0}
  .errmsg           {width:180px}
</style>

<asp:AccessDataSource id="BookSource" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  
  InsertCommand="INSERT INTO Books (BookID, BookType, BookTitle, 
                 BookAuthor,BookDescription, BookPrice, BookQty, 
                 BookSale) VALUES (@BookID, @BookType, @BookTitle, 
                 @BookAuthor, @BookDescription, @BookPrice, @BookQty, 
                 @BookSale)"
/>

<asp:AccessDataSource id="TypeSource" Runat="Server"
  DataFile="../Databases/BooksDB.mdb"
  SelectCommand="SELECT DISTINCT BookType FROM Books ORDER BY BookType"/>

<h3>Book Entry</h3>

<asp:FormView id="DEFormView" DataSourceID="BookSource" Runat="Server"
  DefaultMode="Insert"
  OnItemInserting="Validate_Insert_Data">
  
  <InsertItemTemplate>
  <table id="AddTable" border="0" cellpadding="1">
  <tr>
    <th>ID</th>
    <td><asp:TextBox id="BookID" Text='<%# Bind("BookID") %>' Runat="Server"
          Width="70" MaxLength="6"/></td>
    <td class="errmsg">
        <asp:Label id="ERRBookID" ForeColor="#FF0000" Runat="Server"
          EnableViewState="False"/></td>
  </tr>
  <tr>
    <th>Type</th>
    <td><asp:DropDownList id="BookType" Runat="Server"
          DataSourceID="TypeSource"
          DataTextField="BookType" 
          DataValueField="BookType" 
          SelectedValue='<%# Bind("BookType") %>'/></td>
    <td></td>
  </tr>
  <tr>
    <th>Title</th>
    <td><asp:TextBox id="BookTitle" Runat="Server"
          Text='<%# Bind("BookTitle") %>'
          Width="300" MaxLength="50"/></td>
    <td class="errmsg">
        <asp:Label id="ERRBookTitle" ForeColor="#FF0000" Runat="Server"
          EnableViewState="False"/></td>
  </tr>
  <tr>
    <th>Author</th>
    <td><asp:TextBox id="BookAuthor" Runat="Server"
          Text='<%# Bind("BookAuthor") %>'
          Width="300" MaxLength="50"/></td>
    <td class="errmsg">
        <asp:Label id="ERRBookAuthor" ForeColor="#FF0000" Runat="Server"
          EnableViewState="False"/></td>
  </tr>
  <tr>
    <th>Description</th>
    <td><asp:TextBox id="BookDescription" Runat="Server"
          Text='<%# Bind("BookDescription") %>'
          TextMode="MultiLine" Rows="5" Width="300px" 
          Font-Name="Arial" Font-Size="9pt"/></td>
    <td class="errmsg">
        <asp:Label id="ERRBookDescription" ForeColor="#FF0000" Runat="Server"
          EnableViewState="False"/></td>
  </tr>
  <tr>
    <th>Price</th>
    <td><asp:TextBox id="BookPrice" Runat="Server"
        Text='<%# Bind("BookPrice") %>'
        Width="60" MaxLength="6" Style="text-align:right"/></td>
    <td class="errmsg">
        <asp:Label id="ERRBookPrice" ForeColor="#FF0000" Runat="Server"
          EnableViewState="False"/></td>
  </tr>
  <tr>
    <th>Quantity</th>
    <td><asp:TextBox id="BookQty" Runat="Server"
        Text='<%# Bind("BookQty") %>'
        Width="60" MaxLength="2" Style="text-align:right"/></td>
    <td class="errmsg">
        <asp:Label id="ERRBookQty" ForeColor="#FF0000" Runat="Server"
          EnableViewState="False"/></td>
  </tr>
  <tr>
    <th>Sale</th>
    <td><asp:CheckBox id="BookSale" Checked='<%# Bind("BookSale") %>' 
          Runat="Server"/></td>
  </tr>
  <tr>
  <th></th>
    <td class="buttons">
      <asp:Button id="InsertButton" CommandName="Insert" Text="Insert" 
        Font-Size="9pt" Width="50" Runat="Server"/>
      <asp:Button id="CancelButton" CommandName="Cancel" Text="Cancel" 
        Font-Size="9pt" Width="50" Runat="Server"/>
    </td>
  </tr>
  </table>
  </InsertItemTemplate>
  
</asp:FormView>

<asp:Label id="AddMSG" ForeColor="#FF0000" EnableViewState="False" 
Runat="Server"/>
Listing 9-33. Coding a FormView for data entry.

Normally, a FormView opens in ReadOnly mode to display the first of the selected records from its AccessDataSource in its <ItemTemplate>. In this case, however, there is no set of selected records for display since the AccessDataSource does not have a SelectCommand property. Neither does the FormView have an <ItemTemplate>. With no data to bind to the form and with no way to display it, the FormView simply would not appear on the page.

The need is to open the FormView in its Insert mode rather than its default "ReadOnly" mode. This is accomplished by adding the DefaultMode="Insert" property to the FormView as is done in the above example. Now, the form is opened as a set of input areas as it would appear when clicking the standard FormView's "New" button and switching to insert mode. Also, if necessary, you can open the FormView in Edit mode by setting its DefaultMode="Edit" property, assuming you also have a SelectCommand statement that retrieves an initial record.

Since the FormView opens in Insert mode, it is necessary to provide appropriate command buttons to activate the form. Buttons with CommandName="Insert" and CommandName="Cancel" are provided. An OnItemInserting event handler calls a subprogram when the ItemInserting event occur to validate input values.

Notice that all input areas have <#% Bind(field) %> binding expressions to link with the parameters of the InsertCommand. These expressions do not bind "forward" from a data source to an insert box for display; they bind "backwards" from the input areas to the INSERT statement for inserting entered values.

Each input area has an associated message display Label to its right. These separate message areas make it possible to display separate data entry errors. Also, there is a message Label at the end of the FormView to report success or failure in adding a record to the database.

Scripting the FormView

Most of the scripting effort is to validate entered data to ensure its correctness and reasonableness prior to writing the record to the database. Much of the code is adapted from previous scripts.

<%@ Import Namespace="System.Data.OleDb" %>

<SCRIPT Runat="Server">

Sub Validate_Insert_Data (Src As Object, Args As FormViewInsertEventArgs)
  
  Dim MSGLabel As Label
  
  If Args.Values("BookID") = "" Then
    Args.Cancel = True
    MSGLabel = DEFormView.FindControl("ERRBookID")
    MSGLabel.Text = "&bull; Missing BookID"
  End If
  
  Dim DBConnection As OleDbConnection
  Dim DBCommand As OleDbCommand
  Dim SQLString As String
  
  DBConnection = New OleDbConnection( _
    "Provider=Microsoft.Jet.OLEDB.4.0;" & _
    "Data Source=" & Server.MapPath("../Databases/BooksDB.mdb"))
  DBConnection.Open()
  SQLString = "SELECT Count(*) FROM Books " & _
              "WHERE BookID = '" & Args.Values("BookID") & "'"
  DBCommand = New OleDbCommand(SQLString, DBConnection)
  If DBCommand.ExecuteScalar() <> 0 Then
    MSGLabel = DEFormView.FindControl("ERRBookID")
    MSGLabel.Text = "&bull; Duplicate BookID"
    Args.Cancel = True
  End If
  DBConnection.Close()
  
  If Args.Values("BookTitle") = "" Then
    Args.Cancel = True
    MSGLabel = DEFormView.FindControl("ERRBookTitle")
    MSGLabel.Text = "&bull; Missing title"
  End If
  
  If Args.Values("BookAuthor") = "" Then
    Args.Cancel = True
    MSGLabel = DEFormView.FindControl("ERRBookAuthor")
    MSGLabel.Text = "&bull; Missing author"
  End If
  
  If Args.Values("BookDescription") = "" Then
    Args.Cancel = True
    MSGLabel = DEFormView.FindControl("ERRBookDescription")
    MSGLabel.Text = "&bull; Missing description"
  End If	
  
  If Not IsNumeric(Args.Values("BookPrice")) Then
    Args.Cancel = True
    MSGLabel = DEFormView.FindControl("ERRBookPrice")
    MSGLabel.Text = "&bull; Price not numeric"
  Else
    If Args.Values("BookPrice") < 0 _
    OR Args.Values("BookPrice") > 200 Then
      Args.Cancel = True
      MSGLabel = DEFormView.FindControl("ERRBookPrice")
      MSGLabel.Text = "&bull; Price out of range"
    End If
  End If
  
  If Not IsNumeric(Args.Values("BookQty")) Then
    Args.Cancel = True
    MSGLabel = DEFormView.FindControl("ERRBookQty")
    MSGLabel.Text = "&bull; Qty not numeric"
  Else
    If Args.Values("BookQty") < 0 Then
      Args.Cancel = True
      MSGLabel = DEFormView.FindControl("ERRBookQty")
      MSGLabel.Text = "&bull; Qty out of range"
    End If
  End If
  
  If Args.Cancel = True Then
    AddMSG.Text = "Record " & Args.Values("BookID") & " Not Added"
  Else
    AddMSG.Text = "Record " & Args.Values("BookID") & " Added"
  End If
  
End Sub

</SCRIPT>
Listing 9-34. Scripting a FormView used for data validation.

When the "Insert" button is clicked and an Inserting event occurs, the Validate_Insert_Data subprogram is called. This subprogram validates submitted data for all except the DropDownList and CheckBox fields. A typical check is shown below for the BookID TextBox.

Dim MSGLabel As Label

If Args.Values("BookID") = "" Then
  Args.Cancel = True
  MSGLabel = DEFormView.FindControl("ERRBookID")
  MSGLabel.Text = "-- Missing BookID"
End If

...
Listing 9-35. Scripting a FormView to validate a data entry field.

In this case, the BookID textbox is tested for an entry. If it is missing, then the inserting event is cancelled (Args.Cancel="True") and an error message is displayed in the associated Label.

Since message labels appear inside the FormView, they must be "found" prior to their use. Here, the Label with id="ERRBookID" is located with DEFormView.FindControl("ERRBookID") and assigned to a script-generated Label (MSGLabel) for assignment of the "Missing BookID" message.

Since there are six different message areas, there is the need to find all six and to assign them to Labels. It is not necessary, however, to create six different scripted Labels. All FindControl() methods can assign to the same shared control as is done here by declaring MSGLabel as the shared global Label.

It should be meantioned that the FormView's FindControl() method cannot be applied in this same format to GridView or DetailsView controls. The reason is that the FormView has only one "row" to search for a control. These other controls have multiple rows whose index numbers must be used to identify on which row to look for a control. These FindControl() differences are pointed out in later tutorials.

A test of made of the BookID entry to avoid writing a duplicate record to the database. This test involves connecting to the database and returning a Count(*) of the number of records with a BookID matching the entered value. If the count is 0, then there are no matching records and the new record can be added. If the count is not 0, then a matching record was found and a duplicate-record message is displayed.

DBConnection = New OleDbConnection( _
  "Provider=Microsoft.Jet.OLEDB.4.0;" & _
  "Data Source=" & Server.MapPath("../Databases/BooksDB.mdb"))
DBConnection.Open()
SQLString = "SELECT Count(*) FROM Books " & _
            "WHERE BookID = '" & Args.Values("BookID") & "'"
DBCommand = New OleDbCommand(SQLString, DBConnection)
If DBCommand.ExecuteScalar() <> 0 Then
  MSGLabel = DEFormView.FindControl("ERRBookID")
  MSGLabel.Text = "&bull; Duplicate BookID"
  Args.Cancel = True
End If
DBConnection.Close()
Listing 9-36. Testing for a duplicate BookID.

Whether or not the entered information is added to the database, a message is displayed indicating the results. This message area is located outside the FormView, so it can be referenced directly to display the message.

If Args.Cancel = True Then
  AddMSG.Text = "Record " & Args.Values("BookID") & " Not Added"
Else
  AddMSG.Text = "Record " & Args.Values("BookID") & " Added"
End If
Listing 9-37. Displaying a record-addition results message.