Programming Microsoft ASP.NET 2.0 Core Reference
The FormView Control
FormView is a new data-bound control that can be considered the templated version of the DetailsView. It renders one record at a time, picked from the associated data source and, optionally, provides paging buttons to navigate between records. Unlike the DetailsView control, FormView doesn't use data control fields and requires the user to define the rendering of each item by using templates. The FormView can support any basic operation its data source provides.
Note that the FormView requires you to define everything through templates, and not just the things you want to change. The FormView has no built-in rendering engine and is limited to printing out the user-defined templates.
The FormView Object Model
Two functional aspects mark the difference between FormView and DetailsView. First, the FormView control has properties such as ItemTemplate, EditItemTemplate, and InsertItemTemplate that as we've seen thus far the DetailsView lacks entirely. Second, the FormView lacks the command row that is, a sort of toolbar where available functions are grouped. The graphical layout of a FormView control is completely customizable using templates. Therefore, each template will include all command buttons needed by the particular record.
The control's definition is shown in the following code:
public class FormView : CompositeDataBoundControl, IDataItemContainer, INamingContainer
As you can see, FormView has the same root and implements the same interfaces as DetailsView except for the interfaces related to ASP.NET script callbacks.
Members of the FormView Control
The FormView control exposes many of the properties that we've seen already for the DetailsView control. This aspect is no surprise, as the two controls serve as two sides of the same coin a record viewer control one with and one without templates. Only the templates and related styles mark the difference between FormView and DetailsView. You can refer to Table 11-1 through Table 11-6 for the complete list of properties and events supported by the FormView control.
Supported Templates
The output of the FormView control is exclusively based on templates. This means that you always need to specify the item template at a very minimum. Table 11-7 shows the list of data-bound supported templates.
Template | Description |
---|---|
EditItemTemplate | The template to use when an existing record is being updated |
InsertItemTemplate | The template to use when a new record is being created |
ItemTemplate | The template to use when an existing record is rendered for viewing only |
It's not a coincidence that the FormView templates match the three feasible states of the control ReadOnly, Edit, and Insert. You use the ItemTemplate to define the control's layout when in view mode. You use EditItemTemplate to edit the contents of the current record, and you use InsertItemTemplate to add a new record.
In addition to these templates, the control features the same set of templates offered by the DetailsView that is, HeaderTemplate, FooterTemplate, and the other templates listed in Table 11-5.
Supported Operations
Because the user interface of the control is largely defined by the page author, you cannot expect a FormView control to understand the click on a particular button and act accordingly. For this reason, the FormView control exposes a few publicly callable methods to trigger common actions, such as those listed in Table 11-8.
Method | Description |
---|---|
ChangeMode | Changes the working mode of the control from the current to any of the modes defined in the FormViewMode type ReadOnly, Edit, or Insert. |
DeleteItem | Deletes the current record in the FormView control from the data source. |
InsertItem | Inserts the current record in the data source. The FormView control must be in insert mode when this method is called; otherwise, an exception is thrown. |
UpdateItem | Updates the current record in the data source. The FormView control must be in edit mode when this method is called; otherwise, an exception is thrown. |
Both InsertItem and UpdateItem require a Boolean indicating whether input validation should be performed. In this context, performing validation simply means that any validator controls you might have in the template will be called. If no validators are found, no other form of validation ever occurs. The InsertItem and UpdateItem methods are designed to start a basic operation from within controls in any of the supported templates. You don't have to pass the record to insert, the values to update, or the key of the record to delete. The FormView control knows how to retrieve that information internally in much the same way the DetailsView does.
The DeleteItem, InsertItem, and UpdateItem methods let you define your own delete, insert, and edit user interface and attach it to the standard data-binding model of ASP.NET controls. In the DetailsView control, this association is implicit because the user interface is relatively static and fixed; in the FormView, the same association must be explicitly defined in light of the totally customizable user interface.
Binding Data to a FormView Control
Let's see how to use templates to configure and run a FormView control in a sample ASP.NET Web page. All templates must contain everything needed to accomplish tasks user interface elements and command buttons. The control itself provides the pager bar and the surrounding table.
Header, Footer, and Pager
The final output of the FormView control takes the form of an HTML table with a header and footer row, plus an optional row for the pager. Just like the DetailView, the FormView control provides templates for the header and footer; unlike the DetailsView, though, it doesn't provide simpler and handy text properties such as HeaderText and FooterText:
<asp:FormView runat="server" AllowPaging="true" DataSource> <PagerSettings Mode="NextPreviousFirstLast" /> <HeaderTemplate> <h1>Customer Viewer</h1> </HeaderTemplate> <FooterTemplate> <h3>Courtesy of "Programming ASP.NET"</h3> </FooterTemplate> </asp:FormView>
The pager is dual in the sense that you can have the control to render it as the settings established through PagerSettings and PagerStyle properties dictate, or create it from scratch via the PagerTemplate property.
Displaying Data
The following code snippet shows the typical layout of the code you write to embed a FormView in your pages:
<asp:FormView runat="server" DataSource AllowPaging="true"> <ItemTemplate> ... </ItemTemplate> <EditItemTemplate> ... </EditItemTemplate> < InsertItemTemplate > ... </InsertItemTemplate> </asp:FormView>
The following code generates the page in Figure 11-10:
<asp:FormView runat="server" DataKeyNames="employeeid" DataSource AllowPaging="true"> <ItemTemplate> <table width="100%"> <tr> <td bgcolor="yellow" width="50px" align="center"> <b><%# Eval("id") %></b></td> <td bgcolor="lightyellow" > <b><%# Eval("companyname") %></b></td> </tr> </table> <table > <tr> <td><b>Contact</b></td> <td><%# Eval("contactname") %></td> </tr> <tr> <td><b>City</b></td> <td><%# Eval("city") %></td> </tr> <tr> <td valign="top"><b>Country</b></td> <td><%# Eval("country") %></td> </tr> </table> <br /> <asp:Button runat="server" CommandName="Edit" Text="Edit" /> </ItemTemplate> </asp:FormView>
All the markup you place in the ItemTemplate is rendered in a table cell that spans over two columns. As mentioned, the overall layout of the FormView is a table.
<td colspan="2"> ... </td>
If you want to obtain a tabular output, feel free to define an inner table, as in the preceding code.
The Edit button is added using a classic <asp:Button> button with the Edit command name. The command name will cause the FormView to automatically switch from the read-only mode to edit mode and display using the edit item template, if any is defined. You can use any button control with whatever command name and caption you like. If it doesn't change mode automatically, you call ChangeMode and the other methods supported by the FormView control.
The Eval Function
How can you insert data fields in a template? You resort to data-binding expressions and, in particular, use the Eval function:
<td><%# Eval("city") %></td>
As mentioned in Chapter 9, Eval exists in two forms, one of which is also supported in ASP.NET 1.x. The two forms are functionally equivalent, as one of them is implemented in terms of the other. The first form you can use is the following:
<%# DataBinder.Eval(Container.DataItem, "city")%>
The static function Eval on the DataBinder class uses reflection to parse and evaluate a databinding expression against an object at run time. The object it works with is the data item object from the bound data source that corresponds to the record being rendered. Most of the time, the data-binding expression will be the name of a property on the data item bound to a row. The Eval function counts a third overload to specify a format string.
In ASP.NET 2.0, a similar function is available that has a more compact syntax the Eval function defined on the TemplateControl class and inherited by all ASP.NET pages. Eval is an instance function and accepts only the data-binding expression, and optionally a format string. The Eval function ends up calling into the DataBinder.Eval function.
The Eval is useful only in read-only, one-way data-binding scenarios. For implementing real two-way data binding, an extension to Eval is required the Bind function, which we'll discuss in a moment.
Editing Data
To edit bound records, you define an ad hoc edit template through the EditItemTemplate property. You can place on the form any combination of input controls, including validators. You are not limited to using text boxes and can unleash your imagination to build the user interface.
How do you retrieve values to update the bound record? You enable two-way binding by using the newest Bind function in lieu of Eval.
The Edit Template
The following code snippet shows a sample TextBox control bound to the companyname property of the data source. This is the key difference between item and edit item templates.
<asp:TextBox runat="server" Text='<%# Bind("companyname") %>' />
The following code snippet shows a sample edit template. It contains quite a few standard text boxes but also a data-bound drop-down list.
<EditItemTemplate> <table width="100%"> <tr> <td bgcolor="yellow" align="center"> <b><%# Eval("id") %></b></td> <td bgcolor="lightyellow"> <asp:textbox runat="server" text='<%# Bind("companyname") %>' /></td> </tr> </table> <table > <tr> <td><b>Contact</b></td> <td><%# Eval("contactname") %></td> </tr> <tr> <td><b>Address</b></td><td> <asp:textbox runat="server" text='<%# Bind("street") %>' /></td> </tr> <tr> <td><b>City</b></td> <td><asp:textbox runat="server" text='<%# Bind("city") %>' /></td> </tr> <tr> <td valign="top"><b>Country</b></td> <td><asp:dropdownlist runat="server" datasource selectedvalue='<%# Bind("country") %>' /></td> </tr> </table> <br /> <asp:Button runat="server" CommandName="Update" Text="Update" /> <asp:Button runat="server" CommandName="Cancel" Text="Cancel" /> </EditItemTemplate>
You use Eval to populate control properties not involved in the update process. Wherever you need two-way data binding that is, read/write capabilities you use the Bind function instead of Eval, with the same syntax. For text boxes, you bind the Text property; for drop-down lists, you typically bind the SelectedValue property. Finally, for a Calendar control you would bind the SelectedDate property.
How would you populate a data-bound drop-down list? You would do it by using another data source control, properly configured and parameterized to retrieve its data based on any input that proves necessary. In the sample code, you bind the drop-down list with all possible countries. Similarly, you might bind an employee ID field to the list of all employees from an external, foreign data source.
Finally, bear in mind that the edit template must contain buttons to save changes. These are ordinary buttons with specific command names Update to save and Cancel to abort. Buttons trigger update commands whose details are stored in the associated data source object. You can choose any text for the captions as long as you don't change the command names. If you want to modify the command names, be prepared to handle the ItemCommand event on the FormView and call the UpdateItem method in response.
Figure 11-11 demonstrates the output of the preceding code.
For the update command to work, the DataKeyNames property must be set on the FormView to identify the key field. For deleting a record, just add a button with the Delete command name and configure the underlying data source control.
The Bind Function
How does the Bind function work? The function stores the value of the bound control property into a collection of values that the FormView control automatically retrieves and uses to compose the parameter list of the edit command.
The argument passed to Bind must match the name of a parameter in the update command or method or one of the properties on the type used as an argument to the update method. This is the case in the example shown earlier, where the update method takes an instance of the ProAspNet20.DAL.Customer class. An exception is raised if no parameter match is found.
The Insert Template
The InsertItemTemplate property allows you to define the input layout when a new record is being added. To avoid confusion, an insert template should not be much different from an edit template. At the same time, you should be aware that edit and insert are distinct operations with different requirements. For example, an insert template should provide default values to controls wherever that is acceptable, and it should display neutral or null values elsewhere.
To start an insert operation, you also need a button with a command name of New. Clicking on this button will force the FormView control to change mode to Insert and render the contents defined for the insert template. The insert template should also provide a couple of Update/Cancel buttons with the same command names discussed for edit operations.
When the Function Is Not Supported
Both DetailsView and FormView controls expose some predefined operations such as Insert, Edit, and Delete. As we've seen thus far, these operations are implemented inside the data source control bound to the view control. What if, say, the Edit function is enabled on the FormView control but not supported by the underlying data source control? When this happens, a NotSupportedException exception is thrown and the application fails.
It's hard to imagine a team of developers who release some production code that has an Edit button associated with a non-updateable data source. However, checking whether a requested function is available is a good measure to make any application more robust and stable. The following code demonstrates this feature:
if (e.CommandName == "Edit") { IDataSource obj = (IDataSource) FindControl(FormView1.DataSourceID); DataSourceView view = obj.GetView("DefaultView"); if (!view.CanUpdate) { Response.Write("Sorry, you can't update"); return; } else FormView1.UpdateItem(); }
The code retrieves the data source control and obtains a reference to its default data source view object. (See Chapter 9.) At this point, it checks whether the requested functionality is available. You can place this code in the ItemCommand event handler of any view controls DetailsView, FormView, or GridView. Note that all ASP.NET built-in data source controls have only one view, named DefaultView.