Master-Detail with the GridView, DetailsView and ModalPopup Controls

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

image

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. 

image

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. 

image

image 

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.

image

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.

image

Here is the logic I have wired to the Save button's click handler.

image

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.

image

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.

image 

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.

image

image

That's it.  Enjoy!


TrackBack

TrackBack URL for this entry:
http://mattberseth.com/blog-mt/mt-tb.fcgi/118

Comments


Posted by: Tok on April 29, 2008 12:00 AM

Of all the millions of RSS feeds out there on the net, this is the ONLY blog I subscribe to.

Once again, this latest post demonstrates why. Clear, concise, simple and above all, USEFUL.

This is such a sweet example of "useful" AJAX at its best.

LOVE YOUR WORK, MATT!

T

Posted by: geoff on April 30, 2008 12:00 AM

Seems to be broken

Posted by: Ali Erdo?an on April 30, 2008 12:00 AM

Nice but it does not work on Firefox 2.0

Exactly what I was looking for Matt.. Your a legend as always, thanks again for taking the time to write these examples - This is definitely one of the best blogs on the net!

Hi !

Good, exemple. But it seem very slow at opening and closing the modal. For the basic user it can seem like a bug.

I dont really like the AjaxToolkit modal, I prefere jQuery modal like thickbox, whats you opinion about this ?

Great info Matt. Thx for always having great content for us to learn.

Posted by: wr on April 30, 2008 12:00 AM

I have been lurking on your blog since you started appearing in Scott Gus links.

I have been "borrowing" a lot of your code and ideas. I was stuck trying to implement a listview doing all what your doing here...I cant thank you enough (even though your code in C# ;-) ).

Wonderful...please, PLEASE, keep up the good work.

PS> How long before you get all of this together for a book? (hint, hint)

Very nice flair with the row highlight to give the user feedback that their requested action occurred!

That is a great idea and I think I might try a similar approach in my next project if my entry forms dont turn out to be too complex.

Fabulous idea and a great implementation. However, the delay when clicking Edit is a little extreme. I wouldve probably used an animated indicator, or perhaps just grayed out the screen prior to making the AJAX call.

Great post, Ill be referencing this in the near future.

(PS: Why arent you tracking your live demos with Google Analytics?)

WONDERFUL.. Thanks very much Matt!

hi,Matt great work.I used same concept but my PM wants that the Modalpopup should be animated like ModalBox or GreyBox.I search many post and example but not find animation effect with Modalapopup extender.
All your posts helps us a lot.
Thanks a lot matt:)
Keep it up

Posted by: David Kozikowski on May 1, 2008 12:00 AM

Have you been able to display the Detailsview with the fields positioned on the top and not to the left?

You would think that there would be a ColumDisplay proper that would all you to select Vertical or Horizontal and the would be the end of it.

Posted by: Cha on May 2, 2008 12:00 AM

I was curious if anyone developed a modal popup lookup? You click a link/button
(next to the textbox that needs the lookup value) that opens a popup page where you can search and select from a gridview?
When you select a row it closes the popup and populates the textbox?

@T -
That is one hell of a compliment. Thanks!

@geoff, @Ali Erdo?an -
Can you elaborate on what is broken? I have tested it using FF 2.0.0.14 with no problems.

@Evilz -
My demo site runs a bit slower than I would like - I need to do something about that.

As for jquery versus MS AJAX I would have to say that I like the flexibility of jquery, but I enjoy the stability of MSFT endorsed products. And it is easier to find ASP.NET developers than it is to locate devs that are fluent in jquery. To the company I work for that is a pretty big deal.

@wr -
Glad to help.

I really doubt I would ever write a book. They require a writing style that is too formal for my liking. And besides that it would kill me to labor of the grammer, spelling flow of a tech book. I dont see the ROI in that for the author or the readers. I enjoy writing posts like this one - a live demo, a handful of code snippets and a few paragraphs to glue it all together. Those are the posts I enjoy reading - so thats what I figured I would write.

Thanks for asking.

@Rick Glos -
My thoughts exactly. I am already feeling a follow-up post exploring some other style alternatives.

@Chris M -
Good Luck. Let me know how it turns out.

@Josh -
I swear it was damn fast when it was just me testing it ;). If I write a followup to this post I will add a progress indicator.

I never really thought about tracking my demo site traffic. That would be interesting ...

@Ahmed G -
Thanks Ahmed!

Jignesh -
I explored this a bit a while back. You could try reading this and seeing if it helps you out.

@David -
As far as I know the DV doesnt support that. Of course you could easily do something like that with the new ListView ...

@Cha -
Sounds interesting.

Posted by: Konstantin G on May 4, 2008 12:00 AM

Hi Matt,

Great example! Everything works fine, but when you leave blank required field, and hit Cancel button, you cant display modal popup again. Any ideas how to solve this problem?

Thanks.

Posted by: Shailendra on May 5, 2008 12:00 AM

hi, Matt.
pls help me in writting above eaxample in case of Sql database,
here u have show it using objectdatasource.
pls tell me hw to do it using sql database.

thanks in advance..
Shailendra

Posted by: Kevin on May 8, 2008 12:00 AM

Matt, thanks for your blog. I have been pondering a method for editing. Thanks for the tips.

Posted by: Kabir on May 14, 2008 12:00 AM

Hi Matt I have been trying to implement this modal edit interface and i have been able to successful show the details view in the modal popup but there is a network delay that last about 3 to 4 seconds and i would like to show a progress animiation like you had in the live demo of the previous version of this code. Is there any way you could help me with this. Becuase everthing else works like a peach. If i could fix that that would save my life.

Posted by: Neetu on May 16, 2008 12:00 AM

please tell me how to update the record if the database is in sql server...u use objectdatasource.

Posted by: Ki on May 17, 2008 12:00 AM

I used CustomValidator to use server side validation in place of the RequiredFieldValidators in the example for Customer DetailsView. However in edit mode, ServerValidateEventArgss Value returns the old value when a value was changed in a textbox field, not the changed value. Is it because the validator was used in the details view? How do I get the changed value?

Posted by: george gopie on May 19, 2008 12:00 AM

Matt,
I amtrying to build a samll application in Visual studio 2005, with mater detil to three levels. I have completed the DB design and build the app using VB. However, I am having a hard time to save/update data in my detail forms. Can you give some insight?

PS - I can send uou the code, if necessary.

Thanks,
George.

Posted by: jagjot on May 20, 2008 12:00 AM

do you have any sql db version?

Posted by: Dave on May 22, 2008 12:00 AM

Just buy Telerik RadControls for ASP.NET AJAX, use the RadGrid, and be done with it. All the functionality you are trying to do is built-in w/ the RadGrid - very little code required.

Many thanks. Ive been wanting to do exactly this for a while.

I had the same problem as Konstantin - if you left a validation error on the popup and pressed cancel, the popup wouldnt show again.

I think this is a DetailsView bug. I was able to fix it by using a custom form. Ive uploaded my changed code at http://david.safitech.com/?p=30.
The code also shows Insert Item capability and has Progress indicators.

Posted by: Menno on May 30, 2008 12:00 AM

Fantastic post,

Ive been searching such example for 3 hours. Finally a good working version with all my demands.

Thank you very much!

Posted by: Michael on June 10, 2008 12:00 AM

Excellent work! Do you have an example for adding the details (or formview) dynamically inside the modalpopup? I am doing this but cant seem to get the events to fire.

Thanks.

Posted by: SomeGuy on June 19, 2008 12:00 AM

instead of this eventhandler -

protected void OdsCustomerDetail_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
{
// set the input parameter to the value of the selected index
e.InputParameters["id"] =
Convert.ToString(this.gvCustomers.DataKeys[this.gvCustomers.SelectedIndex].Value);
}

You can simply add this for OdsCustomerDetail -



Type="String" />

The above will grab the Id (DataKey) of the gridview and use it for the select parameter.

Nice article, Matt.

Posted by: SomeGuy on June 19, 2008 12:00 AM

In the SelectParameters section of odsCustomerDetail, add a new ControlParameter and set the ControlID to gvCustomers, Name to Id, PropertyName to SelectedValue and Type to Int32. You can get rid of the OdsCustomerDetail_Selecting eventhandler

When I pasted the code here, its not showing up. Thats why I spelt it out.

Posted by: Raja on June 26, 2008 12:00 AM

Very good post. I was playing with it. It works OK with one problem for me. When I click save, data is updated, Gridview refreshed, ModalPopUp disappears but the rest of the the page stays disabled as if ModalPopUp is still there.
I tried it in IE and FF. Both the same. Any idea why?

Anyway, thanks for the post.

Posted by: Sid on June 27, 2008 12:00 AM

This is a great solution to a problem Ive been looking to get solved.

Would the solution be any different if your GridView contained more templated columns containing a LinkButton, in addition to the Edit Column which contains the LinkButton for firing the ModalPopup?

I have such a situation and Im seeing the problem where, in applying the solution youve presented here, *none* of the LinkButtons in the GridView now fire!

Any ideas on what the issue could be?

Could you provide some code on how you would handle doing inserts with the detailsview in the modal popup.

Would you just use a button to display the popup? I have tried doing this but couldnt get the details view to cooperate.

Posted by: Nick on June 27, 2008 12:00 AM

So can Detailview be replaced by another panel with Gridview?

Posted by: Venkateswara Rao on July 1, 2008 12:00 AM

may i will get same thing with single pop up why to use this many pop ups

Posted by: Neetu on July 4, 2008 12:00 AM

please tell if i use database in sql server 2005...and when i use jst to display detail..my page is posting back..so please help me where i m mistaken

Posted by: Eka on July 6, 2008 12:00 AM

Many thanks for there solution. However I had the same problem as Raja. Is it because of MasterPage?

Posted by: unbelievable luck on July 11, 2008 12:00 AM

i can explain why links stop working when you close the modal, but i dont know how to fix it. if you leave a field blank on the details view and then cancel the modal, the required filed validator is going to fire whenever you try to postback the page. since every linkbutton on the page is a postback, they all stop working because the field validator is firing and stopping the postback. only problem is, you cannot see it because canceling the modal made it invisible, but it is still there on the page. any ideas?

Posted by: Muhammad Saleem Maheen on July 14, 2008 12:00 AM

Very nice post. One thing is missing is to add a new record using the same detailsview. Can you add it. Also there are changes in Net 3.5

Posted by: suma on July 16, 2008 12:00 AM

how we will get the details of gridview in details view by using the modal popup extender control of ajax in dotnet?


please post the answer immediately to my mail id-which i have mentioned at the above

Posted by: Pushkar on July 16, 2008 12:00 AM

Thanks for the Nice Post..
Please continue for the smart post...
Best of Luck

Hi,

This is a nice sample but I have one thing that bothers me a lot: when editing the details for a business object (i.e.: Customer in your example) the user doesnt really need or want to see the ID of the field most of the time.
I set the Visible property of the ID field from the DetailsView (dvCustomerDetail) and the Update operation does not work and that is because the ID passed back is "0".
Maybe I am missing something but do you have any idea on how to achieve this (hiding the ID field and be able to update with the changes)?

Thank you a lot!

Marius

Posted by: Steven on July 20, 2008 12:00 AM

Great article, thanks!

Im also having the same issue as Eka and Raja, with or without using a MasterPage. My data saves and the popup disappears, but Im left with a disabled page.

Ive noticed that in the Status Bar of FF, the following is left displayed:

javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("btnSave", "", true, "", "", false, true))

rather than display Done, as you would normally see following the button being clicked.

Any ideas?

Posted by: avinash on July 21, 2008 12:00 AM

It works OK with one problem for me. When I click save, data is updated, Gridview refreshed, ModalPopUp disappears but the rest of the the page stays disabled as if ModalPopUp is still there.
I tried it in IE and FF. Both the same. Any idea why?

Anyway, thanks for the post.

Posted by: dw on July 30, 2008 03:30 PM

One problem with using a DetailsView and DataKeyName property on GridView is that you get the infamous MAC Viewstate validation failed bug. Does anyone happen to know of a fix for this. Matt, I can reproduce this error on your test page (Click on UI elements quickly on load). Does anybody happen to know of a fix for this error??? I have never seen a reliable one.

Posted by: Hans on August 12, 2008 06:46 AM

As Muhammad above I would love if you could expand the example to include a bit on how to ADD a new record - or just hint at the technique to do so.

I guess, InsertItemTemplate in the FormView control - but how to get there when using ModalPopup.

Would be very helpful - thank you.

Posted by: Rishi on August 20, 2008 12:56 AM

Firstly i thanks for your solutions...
i use same code in my UserControl ...
but in this Linkbutton Click Event not fire...

thats my code


TR.updated TD
{
background-color:yellow;
}
.modalBackground
{
background-color:Gray;
filter:alpha(opacity=70);
opacity:0.7;
}


// attach to the pageLoaded event
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoaded);
function pageLoaded(sender, args) {

// the data key is the control's ID
var dataKey = '';
var updatedRowIndex = args.get_dataItems()[dataKey];

// if there is a datakey for the grid, use it to
// identify the row that was updated
if(updatedRowIndex){
// get the row that was updated
var tr = $get(dataKey).rows[parseInt(updatedRowIndex) + 1];
// add the 'updated' css class
Sys.UI.DomElement.addCssClass(tr, 'updated');

// remove the css class in 1.5 seconds
window.setTimeout(function(){
Sys.UI.DomElement.removeCssClass(
tr,
'updated'
);
}, 1500);
}
}



DataKeyNames="Id" AutoGenerateColumns="false" BorderWidth="0" Width="100%"
OnRowDataBound="GridView1_RowDataBound" OnSelectedIndexChanged="GridView1_SelectedIndexChanged">











  
  
'
Font-Bold="true" Font-Underline="true" CommandName="Select">




              
              

' Font-Size=XX-Small>










'>






 












TargetControlID="btnShowPopup" PopupControlID="pnlPopup"
CancelControlID="btnClose" BackgroundCssClass="modalBackground"
/>
Rishi

Text="Close" CausesValidation="false" />




TypeName="BLLEventMaster" OldValuesParameterFormatString="original_{0}">




------------------------CodeBehind-------------
protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
this.updPnlCustomerDetail.Update();
// show the modal popup
this.mdlPopup.Show();
}

Posted by: Janarthana Jai Ganesh on August 20, 2008 05:28 AM

Hi,

I am trying the above. But found few problems. I have enabled the javascript error. So when i run the application i am getting an error " sys " not defined.
I dont have any clue to solve it

One more question.
Suppose i have a gridview which consits of 25 records and
i want the modal popup to block only the griview section not the screen . is it possible?

Please help.

Posted by: Petter on September 1, 2008 07:46 AM

Hi,

I'm looking into this and realised I had to do some work in order to get this behaviour to work with a sqlserverdatasource, so I'd appreciate an example using that as well (although I've got it to work now myself).

I realise now that if I turn sorting on, the fade effect gets to the wrong row if I have the gridview sorted on the field that I'm changing. (That is, if I have the gv sorted on customers and I change a customer's name, then the original row gets yellow instead of the data in the new position.)

Other than that, this seems to be a great piece of work!

Petter

Thanks Matt! This really helped me out! This was my first time with AJAX :o)


For the people asking how to do it in sql, here is what I did:

I added this to my GridView's Details Source:

"
SelectCommand="SELECT [ID], [Unit], [Driver], [Year], [Make], [Model], [Type] FROM [myTable]">


And my DetailsView's Source:

"
SelectCommand="SELECT [ID], [Unit], [Driver], [Year], [Make], [Model], [Type], FROM [myTable] WHERE [ID]=@ID">




Instead of putting protected void OdsCustomerDetail_Selecting I put it in my aspx code ... that is what the Select Parameters are above. I tell it to control the GridView1

Hope that helps answer those questions! I am writing this in a hurry & hopefully didn't forget anything!

I tried to run the demo and got this message when I clicked on Edit:

Webpage Script Errors

User Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.21022; .NET CLR 3.5.30428; .NET CLR 3.5.30729; .NET CLR 3.0.30618)
Timestamp: Sat, 27 Sep 2008 14:09:36 UTC

0.
Message: Sys.WebForms.PageRequestManagerServerErrorException: Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.
Line: 4724
Char: 21
Code: 0
URI: http://mattberseth2.com/master_details_II/default.aspx

Posted by: Bharath on October 3, 2008 02:17 PM

Hey Matt,
You are the best of the best...I havent seen anyone taking so much time to create all those intellectual fancy stuff.....Kudos to you my friend....Keep it going...
Good Luck,
Bharath

Posted by: Ehsan on October 9, 2008 10:15 AM

Great article as always. I was wondering how this can be accomplished. Very clearly explained.

Posted by: Magnus on October 15, 2008 10:16 AM

Hey Matt,
Excellent article...I have on question though I downloaded the code and adapted it to my project that is based on Subsonic but I get an error.

Sys.WebForms.PageRequestManagerServerErrorException:
ObjectDataSource 'odsCompanyDetail' could not find a non-generic method 'FindById' That has parameter: 'id'.

All I did when adapting was using the subsonic collections and functions for getting data from the database...is it possible for you to take a few minutes to have a look at it if I sent you the code. I think its something with the ODS but is it possible its something with the Javascript perhaps?

Thanks,

Magnus

Posted by: shuja on October 21, 2008 08:31 AM

Hi Matt,

What you outlined in this article is exactly what i was looking for. I have been trying see if the elements of AJAX toolkits & the detailsview work together. BUT...
I am working in VB.net & i can't seem to adapt your code to work in VB.net.
So is it a case of this code only works in C#? Or can it be manipulated to work for vb.net? If you know how to make this work in VB.net then please do let me know.

Thanks,

Shuja

Posted by: Tim on October 27, 2008 02:13 PM

Hi Matt,

I can't seem to get sorting to work on this GV. Shouldn't it be a simple "Enable Sorting = True" in the properties column of the GV? I've gotten it to work on other GV's. Is there something in here that prevents that? Or is it not possible because it is XML rather than SQL DB?

Posted by: ONUR on November 5, 2008 07:39 AM

Hi
This example is very good but when I use autopostback dropdown list pop up extender goes away how can I solve this problem thanks....

Posted by: Vinod on November 5, 2008 02:04 PM

Hi Matt,
Your code works fine with dotnet but when i try to include the same code using custom control .ascx page in Sharepoint site webpart. it si not working any workaround will be apreciated.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Consulting Services

Yep - I also offer consulting services. And heck, I'll do just about anything. If you enjoy my blog just drop me an email describing the work you need done.

Recent Comments

  • Vinod wrote: Hi Matt, Your code works fine with dotnet but when i try to include the same code using custom cont...
  • ONUR wrote: Hi This example is very good but when I use autopostback dropdown list pop up extender goes away ho...
  • Tim wrote: Hi Matt, I can't seem to get sorting to work on this GV. Shouldn't it be a simple "Enable Sorting =...
  • shuja wrote: Hi Matt, What you outlined in this article is exactly what i was looking for. I have been trying se...
  • Magnus wrote: Hey Matt, Excellent article...I have on question though I downloaded the code and adapted it to my ...
  • Ehsan wrote: Great article as always. I was wondering how this can be accomplished. Very clearly explained....
  • Bharath wrote: Hey Matt, You are the best of the best...I havent seen anyone taking so much time to crea...
  • Bill Ross wrote: I tried to run the demo and got this message when I clicked on Edit: Webpage Script Errors User ...