Moses' Blog

Living {.net} lifestyle

Muhammad Mosa

Moses' profile picture
Logo
Software Engineer.
MCT, MCSD.NET,
MCTS: .Net 2.0 Web, Windows, Distributed Applications
MCTS: .Net 3.5 WF Application Development
MCTS: WSS 3.0, MOSS 2007 Configuration & App Dev
MCPD: Enterprise Application Developer

Send mail My Live Space Moses on Facebook Twitter Moses on Technorati

Sponsors



Calendar

<<  November 2008  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar

Recent Comments

Comment RSS

Community Credit

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008

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

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)