Tuesday, July 14, 2009

Turning off ASP.Net's Unique ID Generation

One of the things I am not a fan of (but I understand why they have it), is the unique id generation for server side controls in ASP.Net. The unique Id generation allows the framework to ensure that all controls will have a unique client Id, allowing it avoid collisions.

While this is great it does add a lot of complexity as you start to move away from the traditional ASP.Net Web Forms model. This complexity is one of the reasons this feature doesn't exist in ASP.Net MVC. What are some of the issues with the unique id generation?

  • If you have javascript in your pages the controls are given a somewhat dynamic name, this leaves you with two options. Either you dynamically generate your javascript, or you can assume that you won't be moving the control, and can hard code the ID. The ID is based upon a concept of Naming Containers which is beyond the scope of this post. But essentially as long as you don't introduce new containers in the hierarchy the Id will always be the same.
  • If you want to use post a form to a non-web form page(or different page then the generating page), and want to make use form fields, the field names are associated to their unique id.

The first issue I have always solved by either generating the javascript dynamically in my user controls for example (A user control acts as a naming container and thus all controls in the user control will have it's unique id based on the user control's id). Or by just hard coding the value and hopping I don't change it.

During my latest round of enhancements to my current project I ran into a scenario where I felt I had to disable this unique id generation and couldn't find a workaround that didn't involve rewriting a ton of code. Let me give you some background on the scenario.

This site deals with types of documents, and allows the user to printout (or Email) various PDF files based on the document. There are currently roughly a dozen various printouts and emails which are generated, and the number is always growing. In order to streamline my code and simplify adding new types of printouts, I created a method of doing this which essentially is as follows:

  • On the document's details page we have a second form, this form contains all the printing and email options. There are quite a few variations of options, and certain options depend on other options being set.
  • We preserve the last options used to printout the document, which are set at render time for the page.
  • The form is smart enough to know which type of printout it is doing and will either open a new window, or reuse the current window when doing a post.
  • This form posts to an ASP.Net Handler which based on the type of printout directs the request to what I call a print handler. The print handler will either execute (if its sending an email for example), or it might render a new page to let the user see a preview, or it might just generate a PDF for the user.
  • When generating PDF's we generate the Html and then use a third party tool to convert it to Html.

It's actually a pretty slick mechanism and has streamlined a lot of the code. My new requirement is to enable printing from other pages then the Details page. At this point I have two choices. I can either duplicate a subset of my code to allow printing for just document types we need, or I can try and find a way to consolidate this printing framework to make it reusable.

I opted for the second option of course. So the first thing was to move all the markup, script and code behind outside of the details page into its own user control. A quick test showed that some basic functionality is there so I dropped the control onto my new page and things were looking good. This might be possible I thought.

Then I started doing a deeper more thorough testing (And I wish I had an automated unit test for this UI stuff but ah well It's on my ever growing to do list). I quickly found that all the Id's had changed, this messed with all the javscript which enables or disabled options based on what you've selected.

Well we can fix this one easily so I pick one of the controls, and modify the javascript so it is dynamically generated, and things are looking good again. So then I go and printout the most complex of the documents, this time paying close attention to it, and I noticed that none of the options that were set, were being honored.

The issue is the form fields changed so in the old version I was expecting to see a field with id of "printDescription" now it's coming out as "printOptions_printDescription". Furthermore when I drop it into a content page, which happens to user a master page which is also nested, the Id becomes a long mess. So now at this point how can I tell ASP.Net to not auto generate the Id's. I could write my own custom server side controls but that's a lot of work. I could get rid of the server side setting of options but to do that I would now have to either generate all the html dynamically, use code in the markup, or make a second call to get an json representation of the print options and set them on the client.

So let's get back to the original point of this post. I am going to turn off the Unique ID Generation. The first thing is this works if you have a naming container, which you can override properties on. A user control works perfectly, and since a user control doesnt render any markup by default it doesn't change the structure of the page.

I have found that by overriding the following properties results in the control's child control's from picking up the containers uniqueid and appending it.

    public override string UniqueID
    {
        get { return ""; }
    }

    public override Control NamingContainer
    {
        get { return null;}
    }


Caution


I have not tested this beyond my simple usage scenario. I do not know what would happen if these controls were used in a normal postback scenario, if they would be able to pickup their state from the form fields or not.

Wednesday, June 24, 2009

I need a vacation

So this morning I was thinking about some basic things we can do to speed up the download of our pages, including combining and minifying our JS / CSS files. I decided to go look at YuiCompressor, which is a minifier from Yahoo.

I think to myself neat let me bookmark that and come back to it.



I guess I had this same thought the last time I thought about the subject. Ugh!!!

Monday, June 8, 2009

How not to use a user control

I am in the middle of a nice refactoring session, changing a setup dialog, which allowed you to edit 4 types of email templates. Since the data was the same I had a user control which was then placed on four different tabs.

We are going to tripple the number of types of emails that are going to be configured. So now my simple dialog of four tabs explodes into 12, and while I do not claim to be a UX expert by any means, and I am not able to design the latest slickest easiest to use interface; I can recognize a bad UI very easily.

I have decided to use a drop downlist paradigm where the user selects from the drop down the template they are going to work with, and then we present them this information.

During my refactoring I stripped out my tabs and added the drop down. I removed all but one instance of my user control. I have everything working except I'd like for drop down to line up better with the contents of the user control.

The html that gets rendered is like this:


    <div>

        Select a Template:

        <select>

            <option></option>

        </select>

        <!--This table comes my user control -->

        <table>

            <tbody>

                <tr>

                    <td>

                        Item 1:

                    </td>

                    <td>

                        <input>

                    </td>

                </tr>

            </tbody>

        </table>

    </div>




The problem is the select template doesn't line up with the rest of the labels. This lead me to the ahhahh moment that lead to this post. I do not recommend doing this in production code. I think it makes the code very unreadable.

Nothing says that the user control must render a valid html fragment. Using that to my advantage I modified the code so my aspx page looks like:


        <table>

            <tr>

                <td>

                    Select A Template:

                </td>

                <td>

                    <asp:DropDownList runat="server" ID="DropDownList1" AutoPostBack="true">

 

                    </asp:DropDownList>

                </td>

            </tr>

            <croixUser:emailConfig runat="server"  />




This is basically my entire, I never close the table in the page, I do that in the user control. While we shouldn't be able to do this it makes sense that Microsoft hasn't wasted any energy in blocking this. If you want to shoot your own foot off with convoluted code go ahead.

Now I must be off to refactor that user control to dev\null

Wednesday, June 3, 2009

Jquery 1.3.2 & Intellisense in Visual Studio 2008

After following the directions to get intellisense working with the latest version of jquery, I ran into some issues.

First I would get this error:



Error updating JScript IntelliSense: D:\Source\...\JS\jquery\jquery-1.3.2.js: Object doesn't support this property or method @ 18:9345


The first problem is that the file which has the enhanced comments for intellisense is named jquery-1.3.2-vsdoc2.js. Visual Studio is looking for a *vsdoc.js. Make sure you rename the file to jquery-1.3.2-vsdoc.js.

After renaming the file I was greated with this lovely error:


Error updating JScript IntelliSense: D:\Source\...\JS\jquery\jquery-1.3.2-vsdoc.js: 'div.childNodes' is null or not an object @ 1487:1


My understanding is that the code inside the methods doesn't really matter (Assuming it's not changing the object definitions of course). Since this code will never be ran, I simply removed line 1488 which looks like:


elem = jQuery.makeArray(div.childNodes);



And now we have intellisense working with jquery 1.3.2




Edit


I've noticed a lot of traffic recently on this post, if this post has helped resolve your issue (or not helped) leave a comment let others know.

Thank you and good luck

-jb

Thursday, May 21, 2009

One more VSS bites the dust

I am happy to announce, that I have just my finished my second VSS to TFS migration, and I hope and pray that I will never be forced to open up VSS again. Yahooo!