Using the Form View with Any Data Collection
The ASP.NET DataViews are powerful tools when coupled with a DataSource. But you can skip the DataSource and use the DataViews to handle displaying and updating any collection of objects you want, with a few lines of code.
With the FormView (or any of the DataViews), I normally use a DataSource to move data from some data store and into/out of the view. However, the FormView (and the rest of the DataViews) will work just as well with any collection of objects: Lists, Arrays, ArrayLists, or whatever collection you want to make available to the user for display and update. You won't get automatic generation of the DataView's templates and you'll have to write a little code, but from the user's point of view, everything works as if your DataView was tied to a DataSource. So here's how to use a FormView to display—and let the user update -- any collection of objects.
After dropping the FormView onto your page, add, in the Page Load event, the code to build your collection of objects and set the DataView's DataSource property to that collection. You'll also need to store that collection somewhere in-between the user's trips to the server -- the Session object is a good choice.
In this example, I'm assuming that I'm displaying a set of Customer objects of some kind in a List(of Customer). I first check to see if the List is in the Session object and, if it isn't, build it. I then set the FormView's DataSource to the List:
custList = CType(Session("Customers"), List(Of Customer))
If custList Is Nothing Then
custList = New List(Of Customer)
'…load List with Customer objects
Me.FormView1.DataSource = custList
Next, you must add these two lines of code to the page's PreRender event so they execute after any activity you perform with the FormView elsewhere in the Page. These two lines save the data to the Session object and display the current state of the data in the FormView:
Me.Session("Customers") = custList
At this point you won't get anything displayed because you haven't added any controls to the templates in the FormView. However, the templates are still there: Open the FormView's SmartTag and select Edit Templates; the FormView will switch to displaying its ItemTemplate. It will be empty but it will be there.
The ItemTemplate is used to display the data in read-only mode; to support that, drag a Label onto the template for each object property you want to display. For this example, I want to display my Customer object's FirstName and LastName properties, so I add two Labels and some text to identify the information to the template.
Once the controls have been added to the template, you can bind them to the object's properties. As you would if working with a DataSource control, click on the Label's SmartTag and select Edit DataBindings. When the DataBindings dialog appears, the dropdown lists that normally display the items you can bind to will be empty. Instead, you'll need to enter a binding expression in the Code Expression textbox at the bottom of the dialog. This expression is very straightforward in the current version of ASP.NET: call the Bind method and pass the name of the property you want to bind to (for my LastName property I would enter Bind(“LastName”), for instance).
You should also turn on paging so that your users will be able to see more than just the first item in your collection. While the EnablePaging option doesn't appear on the FormView's SmartTag, it's available from the FormView's Properties list; just set the property to True (you can use the PagerSettings properties to control what controls are displayed for paging). The FormView will complain if you don't have the FormView's PageIndexChanging event in your code file, so add that also. You don't need to put any code in the event, but it does need to be there.
If you now run your page, it will display the collection's first item. By using the paging controls at the bottom of the FormView, you'll be able to page through your data.
You're now ready to add some update code to your FormView. The general pattern is to add a Button to your template with a key word in the Button's CommandName property. When ASP.NET sees those CommandName values, it will fire an event and you'll need to put some code in that event.
To support changing existing object, for instance, you need to add a Button with its CommandName property set to Edit to your ItemTemplate; to support inserting objects, a button with its CommandName property set to New; for deletes, a button with its CommandName set to Delete. For the Edit and New buttons you'll need to include the FormView's ModeChanging event in your code file; unlike the PageIndexChanging event, this time you'll have to put code in it. You only need one line of code, and it's boilerplate: replace “FormView1” with the name of your DataView:
With these changes, when the user clicks on the Edit buttons, the FormView's EditItemTemplate will be displayed; clicking on the Insert button displays the InsertItemTemplate (or the EditItemTemplate if you haven't created an InsertITemplate).
In both of these templates, you'll want to drag on some TextBoxes that will allow the user to enter data. As you did with the Labels, bind them to properties on your object. On the EditItemTemplate, the controls will be displayed with data from the current object; on the InsertItemTemplate, the controls will be displayed with no data. You'll also want to add two buttons to each template: on the EditItemTemplate, a Button with its CommandName set to Update; on the InsertItemTemplate, a Button with a CommandName set to Insert; on both templates, a Button with its CommandName set to Cancel.
When the user clicks on the EditItemTemplate's Save button, the ItemUpdating event will fire. In this event, you'll need to retrieve the object that corresponds to the data being displayed and update its properties from the data in the FormView.
To retrieve the correct object from your collection, use the FormView's PageIndex property as an index into your collection of objects. To retrieve the values currently displayed in the FormView, use the event's e parameter. Its NewValues property, when passed a property name, returns the value from the corresponding control on the FormView. This example moves the data from the controls bound to the FirstName and LastName properties into the corresponding Customer object in my collection:
Dim cust As Customer
cust = custList(Me.FormView1.PageIndex)
cust.FirstName = e.NewValues("FirstName")
cust.LastName = e.NewValues("LastName")
When the user clicks on the Insert button, the ItemInserting event will fire. You'll need to create a new instance of your class, populate its properties with data from the FormView, and add the new objet to your collection. In my case, the code looks like this:
Dim cust As Customer
cust = New Customer
cust.FirstName = e.Values("FirstName")
cust.LastName = e.Values("LastName")
The good news is that you don't need to write any code to support the Cancel button on either template. When users click on the Cancel button, the ItemTemplate will automatically be displayed.
The Delete Button on your ItemTemplate doesn't require a template, but it does fire the ItemDeleting event. In that event, you'll need to find the corresponding object in your collection and remove it. I used this code:
There are a lot of niceties that could be added to this basic form (some data validation, switching back to the ItemTemplate after the ItemUpdating event, displaying a message to reassure the user that their new object has been added in the ItemInserting event). But with a dozen lines of code (plus assignment statements for the properties on your class), you can bind your Form/Details/Grid/ListView to any collection you want.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.