Databinding the GridView Without Using a Table
If you want to let your users edit repeated rows of data, you can use the GridView -- even if your data isn't in a table.
Sometimes you have data that you don't want to store in a database. The good news is that you can still use the GridView to display and edit the data if you're willing to write some code.
One of the objects that I recently created for a client stores multiple data items in a text field. You could make a good case that this data should be stored in a separate table (and I might not argue with you), but it's too late to redesign the solution now. The problem is, the user needs to be able to edit the data.
In ASP.NET you can use the GridView or the DetailsView to edit multiple data items. I'll just concentrate on the GridView here, but most of what I cover applies to the DetailsView as well. Because my data is only available in memory at run-time, I couldn't use a DataSource, so I needed a solution that would enable a GridView in an "unbound" or "DataSource-less" mode.
Managing the Data
My first step was to decide on the format for the data stored in the property. I decided to use an XML design that would be compatible with a DataSet so that I could get some IntelliSense support at design time. Since I was storing a set of parameters to be passed to a command, and each parameter consisted of a name/value pair, I made up these tags to hold the data:
I then added a DataSet to my project and set its Name property to "Parameters" to match my tags (I also cleared the DataSet's Namespace property). Using the DataSet's designer, I added a table called Parameter with two columns: Name and Value. Again, I picked the names to match my XML tags.
To create the user interface, I dragged a GridView onto my page, selected Edit Columns from the SmartTag and added:
- A CommandField to give me Edit and Update/Cancel buttons
- A Delete button
- Two BoundFields (one for my Name tag and one for the Value tag)
For each BoundField, I set the HeaderText to an appropriate caption and the DataField to the name of the tag that I wanted the control to display. Unfortunately, I couldn't find a way to use my Dataset to support configuring the Grid (at least, not in a way that didn't create as many problems as it solved).
Once I had configured each column, I converted it to a Template column. This causes the GridView to generate the two templates that I would need: one for displaying data and a second for editing and inserting data. Since all of my values were string values, I didn't bother to replace the default Label and TextBox controls added to the templates.
With the configuration done, I started on the code that would get the data into the GridView. First, I created a method that would check to see if the XML that I wanted was in the page's ViewState. If the XML wasn't present, I created the object that held it and extracted the XML from its Parameters property:
Function LoadData() As Parameters
Dim strXML As String = Me.ViewState("XML")
If strXML = "" Then
Dim cmd As New MyCommandObject(cmdId)
strXML = cmd.Parameters
Me.ViewState("XML") = strXML
Once I'd retrieved the XML, I loaded it into the DataSet (called Parameters) and returned the DataSet. Because loading the DataSet from XML will cause all the rows to have a status of "New", I call the AcceptChanges method to reset the status to unmodified:
Dim enc As System.Text.Encoding = System.Text.Encoding.UTF8
Dim bt As Byte() = enc.GetBytes(strXML)
Dim stm As New System.IO.MemoryStream(bt)
Dim ds As New Parameters
Rather than storing XML in the ViewState and constantly creating/recreating the DataSet, I could have stored the DataSet in the Session object. The decision is one of those "Do you want your arm cut off or ripped off?" choices: Using ViewState reduces demands on the Web server's memory at the cost of creating a larger package to the send to the client. I've chosen to reduce the demand on the server but it will cost me later in the process (come back in two weeks when I discuss doing inserts).
Now it was just a matter of displaying the data using this method. I put this code in the Page Load event. This was the first place where I finally got some IntelliSense support from using the DataSet:
If Me.IsPostBack = False Then
Dim ds As Parameters = LoadData()
Me.GridView1.DataSource = ds.DataTable1
While that got my data onto the page, it didn't let me do any updates. I'll cover that next week.
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/.