Master-Detail with the GridView, DetailsView and jQuery's ThickBox

And so I continue on messing around with jQuery and looking into ways I can use it in my WebForms applications.  Something I sheepishly admitted to in my last post was that I don't want to use 3 client side libraries {ASP.NET AJAX, AjaxControlToolkit, jQuery}, yet I never really took the time to see what the size of the core jQuery library is.  And for that matter I haven't really looked at what scripts some of my favorite AjaxControlToolkit controls are pulling down either.  I also assumed there is a bunch of overlap between jQuery and ASP.NET AJAX, but I haven't looked into that either. 

So I thought I would check some of this out and along the way rebuild my Master-Detail with the GridView, DetailView and ModalPopup Controls and replace the ModalPopup with jQuery's ThickBox.  I found it pretty interesting - read on to see how it went.

Live Demo | Download

ThickBox Demo

What I Was Looking For

While building this sample I decided I would keep my eye out for the following things ...

  1. Size Comparison (minified, but not gzipped) of AjaxControlToolkit's ModalPopup versus jQuery's ThickBox
  2. Installation / Configuration 
  3. Programming Model / API 
  4. ASP.NET AJAX Integration 

 

jQuery's ThickBox

If you are not familiar with jQuery's ThickBox = here the 2 sentence description taken from the ThickBox home page ...

ThickBox is a webpage UI dialog widget written in JavaScript on top of the jQuery library.  Its function is to show a single image, multiple images, inline content, iframed content, or a content server through AJAX in a hybrid model

Size Comparison

This is what I was most interested in.  I get a lot of comments to my AjaxControlToolkit posts that comment on how large the client side footprint of the Toolkit is.  And I honestly don't really know how to respond - mostly because I don't have a baseline that I can compare it to.  And jQuery's ThickBox and the Toolkit's ModalPopup is not exactly like comparing apples to apples, but I think it is close enough to get a feeling for things.

So here are the numbers ...

 FileSize (Compressed)
ThickBox  
 jQuery.js55KB
 ThickBox.js/ThickBox.css10KB
Total 65KB
ModalPopup  
 Common.js28KB
 BaseScripts.js11KB
 RoundedCornersBehavior.js27KB
 Timer.js1KB
 DropShadowBehavior.js7KB
 DynamicPopulateBehavior.js6KB
 DragDropScripts.js23KB
 DynamicPopulateBehavior.js6KB
 Floatingehavior.js4KB
 ModalPopupBehavior.js18KB
Total 131KB

 

The ModalPopup and its supporting scripts is just over twice the size of the jQuery plus ThickBox combo.  Interesting.  If you are curious about where I got the numbers from - I used the file system to get the jQuery/ThickBox numbers and Reflector to look at the sizes of the Toolkit's supporting JavaScript files (they are embedded resources). 

image

I should mention though that although both the ModalPopup and ThickBox JavaScript files are both minified - this wasn't done with the same tool.  And to my novice eye, it looks like the one jQuery uses does a better job.  So you should keep this in mind when interpreting these numbers.  Also, this is without gzipping - so these numbers will both be smaller after this is applied as well.

 

ThickBox Installation and Configuration

If you want to use the ThickBox, you have to do the following things ...

1.  Download the latest version of jQuery and add a script reference to your page

2.  Download thickbox.js, thickbox-compressed.js, thickbox.css and the loadingAnimation.gif graphic and place them in your web site.  I included both the compressed and non-compressed versions of thickbox just in-case I wanted to debug any issues I ran into. 

After steps #1 and #2, your site should look something like this (the highlighted are the jQuery/ThickBox components) ...

image

And your page will include the following JavaScript and CSS references.

image

3.  Next, you need to update the thickbox-compressed.js and thickbox.js files to include the location of the loading graphic you would like to display while the ThickBox is loading.  The ThickBox component displays an animated gif while the ThickBox is being displayed.  By default the component assumes the location of this graphic is images/loadingAnimation.gif.  So if this isn't the location on your web server you will have to manually update the thickbox.js and thickbox-compressed,js files and update this variable.  In my app, I am storing the graphic at _assets/img/loading.gif so I updated the thickbox JavaScript files to use this path instead of the default one.

 

   1: //  change this ...
   2: var tb_pathToImage = "images/loadingAnimation.gif";
   3:  
   4: //  to this ...
   5: var tb_pathToImage = "_assets/img/loading.gif";

 

 

Programming Model

The ThickBox programming model works as follows ...

You add an A or INPUT to your page and adorn it with the thickbox CSS class.  Then, like magic, when the element is clicked the ThickBox is displayed.  Here is a real simple example that shows an image in a ThickBox - when the A is clicked the nested IMG element is displayed in a ThickBox.

   1: <a href="images/single.jpg" title="add a caption to title attribute / or leave blank" class="thickbox">
   2:     <img src="images/single_t.jpg" alt="Single Image"/>
   3: </a>

 

The ThickBox also supports other scenarios too - like displaying some in-line content from the page inside the ThickBox (like a hidden DIV or something) as well as showing an IFRAME in the ThickBox.  These other scenarios require some additional parameters - and the ThickBox uses some of the existing INPUT and A attributes to specify these values ...

  • title - you can use the A or INPUT's title attribute to specify a title for the ThickBox
  • A.href and INPUT.alt - the value of this attribute is parsed into some of the parameters that control how the ThickBox behaves

For example, here is how you would use the ThickBox to display some hidden content on your page...

  • The href attribute of the A element contains the encoded parameters
  • #TB_inline tells the ThickBox that the content to be displayed exists some where on the current page
  • height/width specify the height and width of the ThickBox
  • inlineId is the ID of the element that will be displayed
  • modal specifies that the ThickBox will be displayed modally
   1: <a href="#TB_inline?height=155&width=300&inlineId=hiddenModalContent&modal=true" class="thickbox">Show hidden modal content.</a>

 

And here is a very simple example of an anchor that when clicked displays some hidden content.

   1: <a href="#TB_inline?height=50&width=300&inlineId=hiddenModalContent" title="Simple Demo" class="thickbox">Show hidden content.</a>
   2: <div id="hiddenModalContent" style="display:none;">
   3:     <div style="text-align:center;">Hello ThickBox!</div>
   4: </div>

 

And here is what it looks like.  In this sample, the dialog isn't modal so it can be closed via the Close or pressing the Escape key or just by clicking any where else on the page.

image

 

ASP.NET AJAX Integration

And now of the real question - how well does the ThickBox work with my WebForms apps?  Well, I tried porting over my previous sample of using the ModalPopup to edit rows in a GridView.  Here is how it went ...

  • I started down the path of using the ThickBox to display some hidden inline content, but that didn't work out to well.  The ThickBox takes the contents of the inline content and moves the DOM elements out to an immediate child of the document's BODY element.  That doesn't work too well when used with WebForms because I need everything contained within the FORM element for my page to work properly. 

Check it out - below is what the DOM looks like.  See how the FORM element and the TB_window DIV are siblings - that isn't going to work. 

image

But this isn't exactly a show stopper either - I can still move my detail content over to a separate page and have the ThickBox load it up using an IFRAME.  So that's were I went next.

  • I updated the markup for my main GridView and changed the last column to render an A tag with all of the parameters I need to tell the ThickBox to load up a new page in an IFRAME.  Remembering how the ThickBox parses the arguments from the anchor's href and title attributes, I used a databinding expression to build out the values for these attributes. 

When the dialog shows I want to include the Customer name in the title, so I am using the ContactName property to build the title value.  I also want to show the Detail.aspx page passing the current rows ID value as an argument.  So I again use a databinding expression to set the href attribute to the value I want.

   1: <asp:GridView ID="gvCustomers" runat="server" DataSourceID="odsCustomerList" CssClass="datagrid" GridLines="None" AutoGenerateColumns="false">
   2:     <Columns>
   3:         <asp:BoundField DataField="ID" HeaderText="ID" ReadOnly="true" />
   4:         <asp:BoundField DataField="CompanyName" HeaderText="Company" ReadOnly="true" />
   5:         <asp:BoundField DataField="ContactName" HeaderText="Name" ReadOnly="true" />
   6:         <asp:BoundField DataField="ContactTitle" HeaderText="Title" ReadOnly="true" />                
   7:         <asp:BoundField DataField="Address" HeaderText="Address" ReadOnly="true" />                
   8:         <asp:BoundField DataField="City" HeaderText="City" ReadOnly="true" />                
   9:         <asp:TemplateField>
  10:             <ItemTemplate>
  11:                 <a id="btnShowPopup" runat="server" class="thickbox" 
  12:                     title='<%# Eval("ContactName", "Details for {0}") %>' 
  13:                     href='<%# Eval("ID", "Detail.aspx?ID={0}&TB_iframe=true&height=220&width=400") %>'>Edit</a>                
  14:             </ItemTemplate>
  15:         </asp:TemplateField>                        
  16:     </Columns>                    
  17: </asp:GridView>
  • Next, I created the Detail.aspx page that displays the detailed information for a given Customer.  This page requires a single parameter, ID, that passed via the querystring.  This page displays the Customer detail information in a DetailsView and allows you to edit the values.  The page has a single Save button that when clicked ensures the page is valid, saves the changes back to the data store, and closes the ThickBox window.

The interesting part here is that the Detail page is responsible for closing itself after the user presses the Save button.  So to accomplish this, I need to register a small script that will run after the Save has completed that will close the ThickBox.  To get this bit of work done, I first created a JavaScript function on the main page called updated that closes the ThickBox window using the ThickBox's tb_remove function.  Next, I need to let the main grid know that it needs to refresh itself (otherwise you wouldn't see any of the changes you just made).  I handle this by setting up a hidden Refresh button that when clicked causes the main grid to refresh itself.

Here is what the updated function looks like ... 

   1: function updated() {
   2:     //  close the popup
   3:     tb_remove();
   4:     
   5:     //  refresh the update panel so we can view the changes  
   6:     $('#<%= this.btnRefreshCustomers.ClientID %>').click();      
   7: }

 

And here is what the Detail page's Save event handler looks like ...

   1: protected void BtnSave_Click(object sender, EventArgs args)
   2: {
   3:     if (this.Page.IsValid)
   4:     {
   5:         //  move the data back to the data object
   6:         this.dvCustomerDetail.UpdateItem(false);
   7:         
   8:         //  register the script to close the popup
   9:         this.Page.ClientScript.RegisterStartupScript(typeof(detail_aspx), "closeThickBox", "self.parent.updated();", true);
  10:     }
  11: } 

 

  • And I actually need make one more modification.  My customer grid is contained within an UpdatePanel.  And even though this grid doesn't sorting or paging, the example as-is wouldn't work too well after the user triggered an event that causes the UpdatePanel to refresh itself.  The reason why is because the ThickBox works by fetching all of the A and INPUT's that contain the thickbox class - and this selection occurs just after the page is initially loaded.  And it doesn't happen again after that.  So we need to add a special piece of logic that will reapply the ThickBox to the A elements that are contained within our UpdatePanel.  And here is the bit of JavaScript that I am using to do this ...
   1: function pageLoad(sender, args) {
   2:     if(args.get_isPartialLoad()){
   3:         //  reapply the thick box stuff
   4:         tb_init('a.thickbox');
   5:     }
   6: }

 

This is again scanning the complete page for anchor elements with the thickbox class and adding the ThickBox behavior to them.  This bit of logic could be optimized because ...

  • We don't need to scan the complete page again, just inside any UpdatePanels that were refreshed.  This is where it might be useful to use a Sys.Component to auto-magically keep track of this stuff for us.  We could easily hook into some of the ASP.NET client side page events and reapply the ThickBox stuff to any elements that contained within an UpdatePanel.  This is pretty similar to the approach I used here to maintain scroll position across partial post-backs.    

 

Conclusion

So, after all of this, here are a few things I found interesting.

  • When I saw all of the scripts the ModalPopup required I couldn't help think it might be interesting if there was a way for the control to only render the scripts it needed to function based on the features you are currently using.  That would be useful.
  • It took me a while to learn the ThickBox's API.  Encoding the parameters into the href is crafty, but it also makes the features hard to discover without documentation.  The Toolkit has a very consistent API.
  • None of the jQuery stuff is going to have a server side API - so when using jQuery from my WebForms I am going to be injecting quite a bit more JavaScript from the page's codebehind than I am used to.
  • I didn't like how the ThickBox forces me to separate my detail widget into a separate page.  But the reality is that most of the jQuery stuff is not written with WebForms in mind.  So running into this stuff might be the norm when trying to use these scripts.

Also, keep in mind this is just one sample.  In my mind, this doesn't tell me to replace the Toolkit with jQuery or vice versa. 

 

That's it.  Enjoy!   


TrackBack

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

Listed below are links to weblogs that reference Master-Detail with the GridView, DetailsView and jQuery's ThickBox:

» Amazing web development articles of from Janko At Warp Speed
Amazing web development articles of Summer 2008 [Read More]

» Amazing web development articles of from Janko At Warp Speed
Amazing web development articles of Summer 2008 [Read More]

Comments


Im not sure if you read my post that cloned your previous sample that were using ModalPopup Popup Master-Detail using GridView, DetailsView and JQuery with jqModal
I that post I was using another popup of jQuery, but this is not the issue, it was really lite I have to admit. Also With jQuery I was able to do some color animation for the updated row very quickly and with simple code.
Very nice post Matt! And dont give up cloning jQuery features into ASP.NET AJAX. Because many would like to use one JavaScript Library and those who used to use ASP.NET AJAX will find that useful to them. Beside it is a good challenge to extend something like ASP.NET AJAX and provide similar easeness of use that is available in jQuery. Regardless of the heavy BYTES that ASP.NET AJAXControlToolkit renders.
Good luck

You mention that you compared the minified java scripts of both the toolkit and jquery. Have you found a way to minify the toolkit java scripts on the fly? Im still using the full size copies, which is far from ideal.

Posted by: Cristovao Morgado on June 23, 2008 12:00 AM

Hi, just one ideia..
I also use jQuery, but I use them as embedded resources, with this I don´t have to configure IIS to get them Cached ;)

Posted by: Lee on June 23, 2008 12:00 AM

Hi Matt,

I figured the best way to test the performance difference would be to compare in fiddler. I am a relative newb to fiddler but from my basic testing i found only a slight improvement with the jquery approach and when i used the ToolkitScriptManager instead of the ScriptManager they were roughly the same.

I wouldnt trust my findings too much and it would be great if someone with a better understanding of fiddler could test the 3 different scenarios.

- jquery approach
- ajax control toolkit
- ajax control toolkit (with the toolkit script manager)

Thanks

Posted by: Steve on June 23, 2008 12:00 AM

I find it interesting to see all the hoops you have to jump through to use common libraries with webforms.

I can see why MS had to create their own library and set of controls to shield developers from needing to understand how overly complex it was designed.

But to me, that is the cost of making a webform framework whose attempt is to abstract away the underlying web stuff.

I still see Webforms as mostly just a server-side development framework with an ajax framework bolted on because of Web 2.0 ideas :)

Great post, Im glad to see you dig in and investigate. Although I know the target is webforms, Im sure over technologies, such as MS MVC are going to certainly benefit with jQuery. My only fear is somehow MS will release a MVC that will force you to only use their libraries ;)

@Muhammad -
I had not seen that one yet - nice post and sweet demo.

I am still on the fence about duplicating jQuery stuff inside ASP.NET AJAX. jQuery is pretty darn small and I would have to do the same for all of the plug-ins I want to use. Have to think more about this.

@Rob -
Are you sure you are using the full version? I believe the Toolkit only contains the minified versions as embedded resources. At least that is what Reflector is telling me - looks like each of the behaviors are embedded already minified. Can anyone else confirm this?

@Cristovao Morgado -
Plus they gzipped by default right? Good idea.

@Lee -
Yep - and I am not expert in this area either. But I think you have to be careful to make sure all scripts are run through the same optimization processes: minified, combined and gzipped. My numbers above just include the minifing.

I am with you though - I would love to see some numbers and make sure I am doing everything I need to to ensure all of my scripts are optimized.

@Steve -
I agree with you Steve - MSFT did such a good job abstracting away the details of what it takes to build a web app (CSS, HTML, State Management, etc...) that many ASP.NET developers dont truly understand some of this stuff (I put myself in this group as well).

But, it is what it is. My boss wants web 2.0 features in our app. But no one here wants to eat the cost of rewriting everything on top of another web framework (MVC - what ever flavor you choose). Plus, even if we did rewrite on an MVC type framework, where would we find DEVs that understand this stuff? The market is flooded with ASP.NET web devs, but what about finding a nuts and bolts HTML/CSS guy? Thats a bit tougher in our market.

So the answer for us is to stay with WebForms and see what options we have. And the Toolkit has been working pretty well so far. Its got a programming model we all understand plus it works with all of ASP.NET’s quirks. I just wish it had the following of jQuery as far as new controls and components.

Good comment and this really is an interesting discussion.

Posted by: Brett Jacobson on June 23, 2008 12:00 AM

How much real impact does all the .js files and such have on an Intranet app? If my user visits 20 different pages, that all have MS AJAX stuff on them, do the .js files get downloaded 20 times? Do they have to get "reparsed" by the browser 20 times? Its my understanding that there is a hit on the FIRST of those 20, but not the other 19. Am I correct? If so, the discussion is really only about "initial cost" right?

Great Article Matt!

jQuery looks great! I think im going to implement the same sample with MVC and jQuery to see how fast it can be

Thanks for researching the file size difference; that bit of information is priceless to me. I cant help myself from scoffing at the toolkit for its inherent obesity. Not only is jQuery smaller, but it packs a bigger punch. I have a hopeful feeling that brevity will soon become the focus of Microsofts AJAX developers. It really should be, in my opinion.

Great post!

@Brett Jacobson -
Good question. There is an interesting article over on the yuiblog that mentions that 20% of all page views are done with an empty browser cache! Scarry.

@stelios a -
Good luck and let me know how it turns out.

@Josh -
Yea the raw numbers look large, but everything should come down a bit after it is compressed.

Also, I am not sure what version of the Toolkit I was using to generate the numbers for ModalPopup, but either the RoundedCornersBehavior went on a diet or there is a bug in my table (guess what is more likely). The RoundedCornersBehavior is only 6KB and not 27KB - so this drops the total down quite a bit.

Thank you for posting this article. Im just starting to explore jQuery and its good to hear about other peoples experences with it. I think you have inspired me do a writeup on my experience with creating a tabbed navigation setup using jQuery.

You have opened so many questions, and I see there are many different opinions, which is good.

Great work!

Posted by: Steve on June 24, 2008 12:00 AM

Im starting to lean to Silverlight for these Web 2.0 apps :)

Awesome post! Thanks a lot!
jQuery is ridiculously lightweight and simple to use. Thanks again!

@jstengel -
A tab panel sounds interesting - Good luck!

@Janko -
Yep. The comments left on this post and my last two are pretty interesting - its kind of all over the board with what people are using and how they feel about it.

@Erik Hoff -
No problem.

Posted by: Francisco Pérez on July 2, 2008 12:00 AM

Pretty neat!! Im having problems making a gridview just for entering the details of my master order. Can you suggest me how to do it, I would appreciate it.

I can not access it form IE even from the demo but on firefox it work fine any reason for this

Posted by: Alex Cabrera on July 9, 2008 12:00 AM

Excellent help Matt! This worked great, I implemented your ModalPopUp and the 3 sec delay is no way acceptable so I was happy to see this example and I really like jQuerys Thickbox.

Keep up the excellent work!

Posted by: George on July 13, 2008 12:00 AM

As usual another excellent article. Just wonder if possible to drag this panel around to make it complete, thanks.

Thanks, that is absolute brilliant work.

Posted by: jeff on July 31, 2008 01:18 AM

hi.
download demo file and run it on my computer. updated ok and changed on display. but DB field or xml file always no changed or updated. maybe i loss something...

thank you.

Posted by: Alex Cabrera on August 10, 2008 10:13 PM

How can you pass the data back from the jQuery pop up to the calling .net page?

Posted by: avinash on August 28, 2008 04:03 AM

Nice article !!
everything is same in this example and modalpopup extender example except one thing-there is a progressbar before the popup opens in this example.
i want to use progressbar in modalpopup example.
can u plz tell me how to do this?

thanx a lot in advance !!

Hi Matt,

I have the anchor in a gridvew template column. A click of the link displays the thickbox popup (which is great). When I save the changes a script error appears on the Default.aspx page. I believe the function pageload on default.aspx can not find the anchors in the gridview to reset them.

Do you have any suggestion to fix this?

If I move the btnRefreshCustomers outside of the updatepanel in default.aspx it works fine but there is a traditional postback.

Hope you can help.

Thanks,

Rob

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

  • Rob P wrote: Hi Matt, I have the anchor in a gridvew template column. A click of the link displays the thickbox...
  • avinash wrote: Nice article !! everything is same in this example and modalpopup extender example except one thing-...
  • Alex Cabrera wrote: How can you pass the data back from the jQuery pop up to the calling .net page?...
  • jeff wrote: hi. download demo file and run it on my computer. updated ok and changed on display. but DB field o...
  • Gordon wrote: Thanks, that is absolute brilliant work. ...
  • George wrote: As usual another excellent article. Just wonder if possible to drag this panel around to make it com...
  • Alex Cabrera wrote: Excellent help Matt! This worked great, I implemented your ModalPopUp and the 3 sec delay is no way ...
  • shhaid wrote: I can not access it form IE even from the demo but on firefox it work fine any reason for this ...