A while back I wrote a post describing how the DetailsView, GridView, UpdatePanel and the AjaxControlToolkit's ModalPopup controls could be used to implement the common Master/Details UI pattern. I cheated a bit when creating my original example in that I didn't really complete the implementation - the Save button on the popup didn't actually do anything. Since writing that post I have received a lot of email and a number of people left comments asking me to complete the example - so here it is. If you plan on reading through this article, I recommend playing around with the demo site to get a feel for how the page works. All data changes are only persisted to memory, so don't worry about messing up the data set.
Live Demo | Download
Scenario
I am sure everyone is pretty familiar with Master/Details style of editing data, but just in case - here is how my page works. The grid shows 12 rows of customer data. The far right column in the grid contains a hyperlink that when clicked brings the detail view of the row into focus so the corresponding row can be edited. The detail view is a popup control and contains a Save and Close buttons. When close is clicked, the detail popup is dismissed and the user goes back to viewing the main grid. When they click Save, some simple validation checks are run (all are RequiredFieldValidators for this sample) and the new data values are persisted, and finally the detail popup is dismissed and the main grid is refreshed so that it displays the changes.
Controls
There are quite a few controls that work together for this sample. Here is a listing of the controls and the role they play.
Control |
Role |
GridView |
Display the 12 customer rows of data |
DetailsView |
Display the single customer row that is currently being edited |
ModalPopupExtender |
Display the DetailView modally |
UpdatePanel |
Allow the DetailView to be loaded without refreshing the entire page |
ObjectDataSource |
Manage how our UI interacts with the customer data |
Customer GridView
The Customer Grid is just a regular GridView. I have used BoundFields to bind to the data and I am using a TemplateField for rendering the 'Edit' anchor.
When 'Edit' is clicked, I want to show the detail view popup. To accomplish this I set the CommandName of the 'Edit' button to Select - this will cause the SelectedIndexChanged event to fire on the server. Inside this event handler, I force the DetailView to databind, passing the datasource it is bound to the ID that corresponds to the row the user clicked. Finally, I let the UpdatePanel the DetailView is contained in that it needs to update its contents and invoke the Show server side method of my ModalPopupExtender which will cause the popup to be displayed when the partial page is reloaded on the client.
Here are the 2 events handlers that I am using to accomplish this bit of work. The top event handler fires when 'Edit' is clicked and the explicit call to DataBind forces the DetailView's corresponding ObjectDataSource's Selecting event to fire. In this handler I use the SelectedIndex of the GridView to fetch the ID the data source needs.
Customer DetailsView
The DetailsView for the individual customer records is also pretty simple - except because I have included RequiredFieldValidators I wasn't able to use the BoundFields and instead I had to explicitly define the EditItemTemplate for each of the fields.
Just below the DetailsView, I have 2 buttons that interact with the ModalPopupExtender. The Close button dismissed the popup without posting back or committing any changes. The Save button does post back, moves the data out of the DetailsView and back to the ObjectDataSource, hides the ModalPopupExtender and finally causes the main GridView to refresh so it displays the changes.
Here is the logic I have wired to the Save button's click handler.
Apply a Yellow Fade to the Updated Row in the GridView
And while this has been pretty much nuts and bolds functionality thus far, I couldn't resist trying to spice it up by applying a yellow fade to the row in the main grid that was updated. To implement this feature, I needed a way to send a bit of information back from the server that let me know what row in the grid was updated. So my approach was to use the ScriptManager's RegisterDataItem method to accomplish this. Here is the doc from MSDN:
Sends custom data to a control during partial-page rendering, and indicates whether the data is in JavaScript Object Notation (JSON) format.
Just what I need - so after the Save button is clicked, back in the server side event handler, I call RegisterDataItem and pass the index of the updated row to the GridView. Then after the partial refresh has occurred back on the client, I extract the row index from the DataItem's collection, use it to find the row that changed and give it a custom CSS class.
So I added the following call to my Save button handler to register the index of the row that was changed.
And then added the following JavaScript to my page to track down the corresponding TR element in the rendered table and apply the updated CSS class to the row.
And like magic I get a yellow background applied to the row that was edited. The background color stays in place for 1.5 seconds then my timeout handler fires and removes it. A poor man's animation, but the plumbing is in place for me to improve upon. Below are the screen shots of how it looks.