Popup Master-Detail using GridView, DetailsView and JQuery with jqModal

by mosessaur| 08 May 2008| 34 Comments

Introduction:
Last month Matt Berseth posted very good post about how to build Master-Detail with the GridView, DetailsView and ModalPopup Controls. Today I'm going to clone his post and build the same feature using jQuery with jQuery Plugins; one for popup windows jqModal& and the other is for Color Animation. You can view a demo of this sample here [View Demo].

Prerequisites:
Because I'm using some design tips and styles posted in Matt's posts, I recommend to return to his original posts regarding styling and UI enhancement. I used the styles and design shown on his post Building a VS2008 Styled Grid with the GridView Control.

In my sample I'm using UpdatePanel, and used a client side technique to update the UpdatePanel. To read and review more about this technique please read Dave's post Easily refresh an UpdatePanel, using JavaScript.

It is important also to review documentation of jqModalas I'm not going to explain its APIs.

Implementation:
To make a long story short, I just modified Matt's sample and replaced ModalPopup control of AJAX Control Toolkit with jqModal. Also Matt used to indicate the updated row by setting a style sheet class to the updated row for certain period of time then remove it to make it look as before. I did the same, but I used some kind of animation provided by Color Animationplugin for jQuery.

MProduct Detail Modal Popup  Product List after Update with Indicator

The GridView which displays Products List is placed inside an UpdatePanel. Each record will display edit button. When the edit button is clicked it will set a hidden field with ProductID then invoke client script to update another UpdatePanel that contains DetailsView control that will be used to display Product Detail for update. It is important to note that the button doesn't cause a PostBack, instead it calls __doPostBack JavaScript method with UpdatePanel as a parameter to force Async Request to be initiated. The Server will process the request and the DetailsView will bind Product Details. When the Async Request Ends, jqModal will be invoked to show a modal popup that carries the DetailsView Control to show editable Product Detail.

On the popup window, user will be able to edit the data then save or close to discard changes. If Save is Clicked, recored will be saved in the database and modal window will disappear, then the GridView will display a color fading indicator on the updated row.

Above was the story. Below is the code showing each part.
Products List GridView:

   1: <asp:UpdatePanel ID="updatePanel" runat="server" UpdateMode="Conditional">
   2:     <ContentTemplate>
   3:         <asp:GridView ID="gvProducts" runat="server" OnRowDataBound="RowDataBound" AutoGenerateColumns="False"
   4:             AllowPaging="True" AllowSorting="True" CssClass="datatable" CellPadding="0" BorderWidth="0px"
   5:             GridLines="None" DataSourceID="sqldsProducts" DataKeyNames="ProductID">
   6:             <PagerStyle CssClass="pager-row" />
   7:             <RowStyle CssClass="row" />
   8:             <PagerSettings Mode="NumericFirstLast" PageButtonCount="7" FirstPageText="&#171;"
   9:                 LastPageText="&#187;" />
  10:             <Columns>
  11:                 <asp:BoundField HeaderText="ID" DataField="ProductID" SortExpression="ProductID">
  12:                     <HeaderStyle CssClass="first" />
  13:                     <ItemStyle CssClass="first" />
  14:                 </asp:BoundField>
  15:                 <asp:BoundField HeaderText="Name" DataField="ProductName" SortExpression="ProductName" />
  16:                 <asp:BoundField HeaderText="Quantity" DataField="QuantityPerUnit" SortExpression="QuantityPerUnit" />
  17:                 <asp:BoundField HeaderText="Unit Price" DataField="UnitPrice" SortExpression="UnitPrice"
  18:                     DataFormatString="{0:c}">
  19:                     <ItemStyle CssClass="money" />
  20:                 </asp:BoundField>
  21:                 <asp:BoundField HeaderText="In Stock" DataField="UnitsInStock" SortExpression="UnitsInStock" />
  22:                 <asp:BoundField HeaderText="On Order" DataField="UnitsOnOrder" SortExpression="UnitsOnOrder" />
  23:                 <asp:TemplateField>
  24:                     <ItemTemplate>
  25:                         <input class="button" type="button" value="Edit" 
  26:                             onclick='ShowDetails(<%#Eval("ProductID")%>,<%# Container.DataItemIndex%>)' />
  27:                     </ItemTemplate>
  28:                 </asp:TemplateField>
  29:             </Columns>
  30:         </asp:GridView>
  31:     </ContentTemplate>
  32: </asp:UpdatePanel>

The edit button onclick event invokes ShowDetails client function passing productId as well as item index in the result set. This is different from the mechanism shown in Matt's postbut at the end will perform the same job.

Belwo is the DetailsView Control with another UpdatePanel.

   1: <asp:UpdatePanel ID="updPnlProductDetail" runat="server" UpdateMode="Conditional">
   2:         <ContentTemplate>                                
   3:             <asp:DetailsView ID="dvProductDetail" runat="server" DataSourceID="sqldsProductDetail"
   4:                 GridLines="None" DefaultMode="Edit" AutoGenerateRows="false" Visible="false"
   5:                 Width="100%" DataKeyNames="ProductID">
   6:                 <Fields>
   7:                     <asp:BoundField HeaderText="Product ID" DataField="ProductID" ReadOnly="true" />
   8:                     <asp:TemplateField HeaderText="Product Name">
   9:                         <EditItemTemplate>
  10:                             <asp:TextBox ID="txtProductName" runat="server" Text='<%# Bind("ProductName") %>' />
  11:                             <asp:RequiredFieldValidator ID="rfvProductName" runat="server" ControlToValidate="txtProductName"
  12:                                 ErrorMessage="Required" Display="Static" SetFocusOnError="true" />
  13:                         </EditItemTemplate>
  14:                     </asp:TemplateField>
  15:                     <asp:TemplateField HeaderText="Quantity Per Unit">
  16:                         <EditItemTemplate>
  17:                             <asp:TextBox ID="txtQuantityPerUnit" runat="server" Text='<%# Bind("QuantityPerUnit") %>' />
  18:                             <asp:RequiredFieldValidator ID="rfvQuantityPerUnit" runat="server" ControlToValidate="txtQuantityPerUnit"
  19:                                 ErrorMessage="Required" Display="Static" SetFocusOnError="true" />
  20:                         </EditItemTemplate>
  21:                     </asp:TemplateField>
  22:                     <asp:TemplateField HeaderText="Unit Price">
  23:                         <EditItemTemplate>
  24:                             <asp:TextBox ID="txtUnitPrice" runat="server" Text='<%# Bind("UnitPrice") %>' />
  25:                             <asp:RequiredFieldValidator ID="rfvUnitPrice" runat="server" ControlToValidate="txtUnitPrice"
  26:                                 ErrorMessage="Required" Display="Static" SetFocusOnError="true" />
  27:                         </EditItemTemplate>
  28:                     </asp:TemplateField>
  29:                     <asp:TemplateField HeaderText="Units In Stock">
  30:                         <EditItemTemplate>
  31:                             <asp:TextBox ID="txtUnitsInStock" runat="server" Text='<%# Bind("UnitsInStock") %>' />
  32:                             <asp:RequiredFieldValidator ID="rfvUnitsInStock" runat="server" ControlToValidate="txtUnitsInStock"
  33:                                 ErrorMessage="Required" Display="Static" SetFocusOnError="true" />
  34:                         </EditItemTemplate>
  35:                     </asp:TemplateField>
  36:                     <asp:TemplateField HeaderText="Units On Order">
  37:                         <EditItemTemplate>
  38:                             <asp:TextBox ID="txtUnitsOnOrder" runat="server" Text='<%# Bind("UnitsOnOrder") %>' />
  39:                             <asp:RequiredFieldValidator ID="rfvUnitsOnOrder" runat="server" ControlToValidate="txtUnitsOnOrder"
  40:                                 ErrorMessage="Required" Display="Static" SetFocusOnError="true" />
  41:                         </EditItemTemplate>
  42:                     </asp:TemplateField>
  43:                 </Fields>
  44:             </asp:DetailsView>
  45:             <div class="footer">
  46:                 <asp:Button CssClass="button" ID="btnSave" runat="server" Text="Save" OnClick="OnSave" CausesValidation="true" />
  47:                 <asp:Button CssClass="button" ID="btnClose" runat="server" Text="Close" CausesValidation="false" />
  48:             </div>
  49:         </ContentTemplate>
  50:     </asp:UpdatePanel>

Time to explore client side script. At the begining I did just as Matt and registered a handler for pageLoaded client event of the ASP.NET AJAX. In addition I registered another handler for endRequest.

//  attach to the pageLoaded & endRequest events
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoaded);        
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);

The pageLoaded handler task is just to get the updated row and display color animation to indicate the it is updated. We will explore its code later.

The endRequest handler task is to display or hide jqModal window after the record has been retrieved and async request is ended. Just to avoid display empty widnows waiting for the DetailsView to be shown showing data. Also we will explore the details of this handler in a min.

Also I initialize jqModal instance as global on the page:

//Initialize global jqModal instance. Review jqModal Documentation http://dev.iceburg.net/jquery/jqModal/
var $jqm = $('#divProductDetail').jqm({modal: true,overlay: 30, trigger: false, 
                                    onShow: OnShowDetails, onHide: OnHideDetails});

Now I'll explore the client functions starting from the ShowDetails function which will be called when edit button is clicked, Code is commented for better explanation:

   1: function ShowDetails(productId, index)
   2: {
   3:     //Save ProductID in a hidden field to be used by CodeBehind to bind Product Detail DetailsView [dvProductDetail]
   4:     $('#<%=hdnProdId.ClientID %>').val(productId);
   5:     
   6:     //Save Item Index in a hidden field to be used by CodeBehind to RegisterDataItem for using ScriptManager
   7:     //This will be used to get the row that was updated on the GridView to display fading effect indicating that
   8:     //this row was updated.
   9:     $('#<%=hdnEditItemIndex.ClientID %>').val(index);
  10:     
  11:     //Will be used but endRequest to show the DetailsView Modal Window.
  12:     show = true;
  13:     
  14:     //Instruct Update panel to PostBack and update its content which is the Product DetailsView.
  15:     // http://encosia.com/2007/07/13/easily-refresh-an-updatepanel-using-javascript/
  16:     __doPostBack('<%=updPnlProductDetail.ClientID%>', '');
  17: }

As result of calling __doPostBack('<%=updPnlProductDetail.ClientID%>', ''); an AsyncCallBack will be initiationed and when it ends it will rise endRequest event. So its time to explore endRequest event handler:

   1: //Used to show or hide the Modal Popup that display editable Product Detail
   2: function endRequest(sender,args)
   3: {
   4:     if(show)
   5:         $jqm.jqmShow(); //Will rise onShow event of jqModal
   6:     else
   7:         $jqm.jqmHide(); //Will rise onHide event of jqModal
   8:         
   9:     show = false;
  10: }
  11: function OnShowDetails(hash)
  12: {
  13:     //onShow Handler of jqModal. Review jqModal Documentation. http://dev.iceburg.net/jquery/jqModal/
  14:     hash.w.slideDown('slow');
  15: }
  16: function OnHideDetails(hash)
  17: {
  18:     //onHide Handler of jqModal. Review jqModal Documentation. http://dev.iceburg.net/jquery/jqModal/
  19:     hash.w.slideUp('fast',function(){hash.o.remove();});               
  20: }

Call jqmShow or jqmClose will rise onShow and onHide events of jqModal respectively. Event handlers are binded while create the jqModal instance. Event handler are shown in the code above.

Server time processing now should be explored, first I'd show the onLoad event of the Page:

   1: protected override void OnLoad(EventArgs e)
   2: {
   3:     //View and bind DetailsView only if AsyncPostBack is initiated and the initiator is updPnlProductDetail
   4:     if (smDefault.IsInAsyncPostBack && smDefault.AsyncPostBackSourceElementID == updPnlProductDetail.ClientID)
   5:     {
   6:         //  set it to true so it will render
   7:         this.dvProductDetail.Visible = true;
   8:         //  force databinding
   9:         this.dvProductDetail.DataBind();
  10:     }
  11:     base.OnLoad(e);
  12: }

This is going to be excuted each time page loaded. But I placed a condition to check for AsyncPostBack as well as if the AsyncPostBack initiator is the UpdatePanel that contains the DetailsView. Do you remember the __doPostBack('<%=updPnlProductDetail.ClientID%>', ''); this is the key beind the above code.

Final on server side is the Save:

   1: protected void OnSave(object sender, EventArgs args)
   2: {
   3:     if (this.Page.IsValid)
   4:     {
   5:         //  move the data back to the data object
   6:         this.dvProductDetail.UpdateItem(false);
   7:         this.dvProductDetail.Visible = false;
   8:  
   9:         //DataItemIndex to calculate the updated row index in the GridView
  10:         int dataItemIndex = int.Parse(this.hdnEditItemIndex.Value);
  11:         if (dataItemIndex > gvProducts.PageSize)
  12:             dataItemIndex = dataItemIndex - (gvProducts.PageSize * gvProducts.PageIndex);
  13:  
  14:         //  add the css class for our yellow fade
  15:         ScriptManager.GetCurrent(this).RegisterDataItem(
  16:             // The control I want to send data to
  17:             this.gvProducts,
  18:             //  The data I want to send it (the row that was edited)
  19:             dataItemIndex.ToString()
  20:         );
  21:         
  22:         //  refresh the grid so we can see our changed
  23:         this.gvProducts.DataBind();            
  24:         this.updatePanel.Update();
  25:     }
  26: }

This is just a clone from the Matt's sampel with few modifcation to match this example.

Finally and returning back to client side, when the page is loaded again the client event pageLoaded will rise, and it is time to display some animation if the record is updated:

   1: function pageLoaded(sender, args){
   2:     
   3:     //  the data key is the control's ID
   4:     var dataKey = '<%= this.gvProducts.ClientID %>';
   5:     var updatedRowIndex = args.get_dataItems()[dataKey];
   6:     
   7:     //  if there is a datakey for the grid, use it to
   8:     //  identify the row that was updated
   9:     if(updatedRowIndex){
  10:         
  11:         var index = parseInt(updatedRowIndex) + 1;
  12:         //  get the row that was updated
  13:         var $tr = $('#'+dataKey+'>tbody>tr:eq('+index+')');
  14:         
  15:         //Perform Color Animation on the updated row. http://plugins.jquery.com/project/color
  16:         $($tr).animate({}, { queue: false, duration: 7000 }).animate({ backgroundColor: "yellow" }, 1000)
  17:                                                             .animate({ backgroundColor: "white" }, 1000);
  18:     }
  19: }

Conclusion:
The idea of displaying on deman retrieved data on popup is very useful. Here I was showing that same idea can be done in different ways. Giving you options to choose the one that matchs your needs. This is also showing the jQuery can be combined with with ASP.NET AJAX to enhance user experiance.

Feel free to download the sample [51.14 kb] and explore the whole code. I hope that this post was helpful.

kick it on DotNetKicks.com

Comments

Jaswinder Singh
Jaswinder Singh United States on 9/1/2008 4:15 PM This is the Excellent Example. It really helps Developer Who want to use Data Source Object to Bind one Controll to Another.
mosessaur
mosessaur Egypt on 9/1/2008 4:18 PM @Jaswinder Singh Thank you so much for that. I really hope it would be useful.
Debasubhra Dey
Debasubhra Dey India on 9/10/2008 3:22 PM thats really is a fantastic work.I was looking for this kind of animation effect for a long.

thanks a lot.
Ndaba
Ndaba South Africa on 10/11/2008 7:12 AM Does not work with Css Friendly gridview inside a masterpage on the 2nd postback
mosessaur
mosessaur Egypt on 10/11/2008 7:19 AM @Ndaba it might need you to tweak it a little bit to fit your senario. The demo is not supposed to work directly on any application, it might need some modifications
Shalan
Shalan South Africa on 10/14/2008 4:45 PM Hi Muhammad.  Nice article and well worth the read! I have been playing around with jQuery for some time now, but am investigating not having to use the UpdatePanel for certain applications.  Unfortunately for instances such as with gridviews, where the level of HTMl to control is far too vast, I guess we are all left with no other option - I mention this as I am led to believe that the UpdatePanel essentially still performs a fullpage postabck with the entire Viewstate minus the "blink".

@Ndaba - I don't see why MS released a css-friendly adapter for the gridview.  It still renders as a table, and from experience its a pain to style! Also (and this is just speculation based on my testing), but a gridview showing the first 10 rows of the Northwind Customers table renders less HTML traditionally than with the adapter installed. I don;t wana say that u are better off without it, but u can be the judge of that thru your own testing.

cheers!
Milan
Milan Bosnia and Herzegovina on 10/19/2008 8:59 PM Where to download Northwind from? I can't find it.
mosessaur
mosessaur Egypt on 10/19/2008 9:05 PM @Milan Northwind database you mean? you can download it from here
www.microsoft.com/.../details.aspx
DOMINGO M. ASUNCION
DOMINGO M. ASUNCION Republic of the Philippines on 10/21/2008 12:29 PM Very nice... Thank you...
Ahsan Murhsed
Ahsan Murhsed bd on 10/21/2008 12:48 PM Assalamu alaikum,

Thanks for this article. It's a superb article.Really helpful for developer.

Ma assalam
Rasul
Rasul United States on 10/21/2008 1:47 PM Hi, very good but i think that it doesn't work in FF!
mosessaur
mosessaur Egypt on 10/21/2008 7:32 PM @Rasul I tested it with all browsers and it is working fine! which FF version do you have? cause it is working fine on FF 3.x
Rasul
Rasul United States on 10/22/2008 11:05 PM Thanks dear Muhammad,i changed my FF and it's working very well now! It's a great and nice work.
Hi Mosa
Hi Mosa Australia on 10/24/2008 8:44 AM Good work. Please can you make the popup message dragable.
mosessaur
mosessaur Egypt on 10/24/2008 11:17 AM Making the popup dragable is possible, not sure if it is supported in jqModal or not, but there are other jQuery Popup Plugins that support that. You could check the documentation of jqModal for that.
Shail
Shail India on 10/24/2008 5:11 PM Hello Muhammad Mosa,
Why there is a AJAX request back to server when you click close ( it should be same as cancel )
Because if you see what Matt has built, you won't find any request back to server when you click cancel. I checked that in Firbug.

- Shail
mosessaur
mosessaur Egypt on 10/24/2008 5:53 PM @Shail Yes you are right there shouldn't be any AJAX request when just cancel or close. It is happening here because I am handling endRequest of the AJAX to check if I am closing or opening to show or hide.
I am sure there is a better way, but my idea wasn't to show how to close or open without initiating AJAX. I was demonistrating how to display master details data with update feature and color animation of the updated row.
kamal
kamal Indonesia on 10/25/2008 4:48 AM moses, great article, so inspire me..

thanks
Shail
Shail India on 10/25/2008 7:48 PM Yes, I agree.
But if you can modify this code a little bit, then we can really use for Real World cases.
- Shail
mosessaur
mosessaur Egypt on 10/25/2008 7:55 PM @Shail I will, but I highly encourage you to try to play with it and tweak it to fit your needs.

I also recommend the following 2 new posts similar to this scenario:

Modal Delete Confirmation Version Two Using jQuery SimpleModal Plugin Demo
beckelman.net/post.aspx

Inserting Content Using jQuery SimpleModal Plugin Demo
beckelman.net/post.aspx
Cars
Cars United Kingdom on 10/25/2008 11:04 PM Just to add to everyone else, thanks for posting this up... Posts like this really do inspire people like me to do stuff like this Smile
kiki
kiki Tunisia on 10/26/2008 9:12 PM thanks
ONUR
ONUR Turkey on 11/1/2008 11:21 PM Hi I have problem about this detail modal pop up when I want to use a dropdown list  this dropdown list trigged some other dropdown or grid pop up will closing thanks for time consuming happy coding
mosessaur
mosessaur Egypt on 11/2/2008 12:04 AM @ONUR Sorry but I didn't understand your question/issue!
ONUR
ONUR Turkey on 11/2/2008 6:26 AM I need to edit pop up.
I have 2 drop lists.
first one has company and second is department.
Database values of Department are related with the company values.
so I need to post back event.
How could this be done? please advise...
ONUR
ONUR Turkey on 11/3/2008 6:41 AM First of all great article thanks but how we can do any asynchronous operation in modal pop up
For example have two dropdown list first store Company so when I select a Company related department load second dropdown thanks advance... Happy coding
alek
alek Republic of the Philippines on 11/3/2008 11:15 AM hi Sir,
i have a question regarding of Edit Button,
if my data type is varchar as a parameter the divProductDetail will not show, why is that?

thanks
mosessaur
mosessaur Egypt on 11/3/2008 12:52 PM @alek Sorry but I have no idea why is that. It has no relation to the datatype. So I guess you might be doing something wrong! review your code you might find something missing.
Harold Henson
Harold Henson U.A.E. on 11/16/2008 7:44 PM This was of great help, thanks buddy.
Wishing you all success.
indah
indah Indonesia on 1/1/2009 11:42 PM It's great. How if it's implement using ruby on rails? I have problem to make edit pop-up like that, but it's using rails.
Colin Jarvis
Colin Jarvis United Kingdom on 2/20/2009 7:22 PM Hi Great article!!

I have got a problem in that when I try and edit any of the records, the Product id record is always 1 even if I try and edit product id 10

Any suggestions or do you have time to look at the code
Gregory Franz
Gregory Franz South Africa on 3/3/2009 8:00 AM This is one of those good inspiring sites.

I am trying to use this by binding with code instead of via the sqldatasource as set up via the designer. By using code I cannot seem to populate and display the detailsview.

Anybody has any pointers on how to accomplish this.

Thanks
Edgar Najar Galicia
Edgar Najar Galicia Mexico on 4/20/2009 6:29 PM I need to change the code of this example to show tow details:
- First the college petitions
- Second the college information
Both in the same row and in the same page.
I try to change the code, but is using asp:SqlDataSource and my application use dateset.GetXml() that mens that the use of parameters and other forme to make reference to a Viewstate are impossible.

I use this function to obtain my information from SQL:
public string ExecuteXmlQuery(string Sql, string TableName){
            try{
                OpenConnection();
                DataSet _dsData = new DataSet();
                SqlCommand _Command = new SqlCommand(Sql, m_scConnection);
                _Command.CommandType = m_eType;
                using (SqlDataAdapter _saAdapter = new SqlDataAdapter(_Command)){
                    _saAdapter.Fill(_dsData, TableName);
                    return _dsData.GetXml();
                }
            }catch (Exception Err){
                throw (Err);
            }finally{
                CloseConnection();
            }
        }

I need help to create the GridView and the ascx, also need help to send and recive the parameter to make the sentence of SQL.

I use Visual Studio 2005 and AJAX 2.0
Ross McLoughlin
Ross McLoughlin Ireland on 4/30/2009 11:16 AM Nice, but doesn't work in Safari 3.1.2

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading