in

ASP.NET Weblogs

Andy Smith's Blog

Page.RegisterStartupScript('Andy', 'MetaBuilders_WebControls_GainKnowledge();');

September 2003 - Posts

  • subclassing and working with your events

    I see code like this alot, and there is a bit of a problem with it under certain circumstances.

    protected override OnFoo(EventArgs e) {
     base.OnFoo(e);
     //Do My Thing
     if ( bar ) {
      DoSomeReallyCoolStuff();
      System.NeatThings.NeatnessLevel = NeatnessLevel.High;
      /*
      Line And Lines of really interesting code here
      */
     }
    }

    Code like that is the general advice for people who want to derive from a class, and want to do stuff before/during/after a certain event. It is a much better idea than handling your own events. However this can cause a real problem for people who want to derive from your superdooper class. You see... you've combined the raising of an event with code that needs to happen during an event. With code like this, there is no way for me to derive from your class, and change this code that happens during OnFoo, without also stopping the Foo event from being raised.

    A much better way to do this is something like this:

    protected override OnFoo(EventArgs e) {
     base.OnFoo(e);
     DoMyThing();
    }
    protected virtual DoMyThing() {
     if ( bar ) {
      DoSomeReallyCoolStuff();
      System.NeatThings.NeatnessLevel = NeatnessLevel.High;
      /*
      Line And Lines of really interesting code here
      */
     }
    }

    Now a derived class can rip out this implementation of DoMyThing much more independantly of the raising of the Foo event.

    If you need a real world example to see what I'm talking about... I had thought about adding support for item groups in the DropDownList and ListBox controls. In html, this is done by adding OptGroup tags around Options. I was planning on looking at a custom "group" attribute in the ListItem to see if I should create a group. But then I got to thinking about databinding, and that it's not really usefull unless I can support that easily too. So I thought that I could add a DataGroupField string property, which would set the group attribute on the new ListItems as they were being created. I was ok with the idea of reimplemening the databinding code, but then this problem hit me. The ListControl class has the code which creates the ListItems directly in the OnDataBinding method. This means that I have to override that method, and not call the base OnDataBinding, in order to implement my group data binding. But if I do that, then my control never raises its DataBinding event. And for those of you wondering why I can't just call the base OnDataBinding, and then run thru the datasource again to bind the group property to the list items... I can't do that because not all datasources can be run thru twice, such as a DataReader, which, for better or worse, is commonly used in web apps for data binding.

  • all script techniques have a drawback

    It turns out that the object intialiser javascript technique that I've been raving about has a downside, in that it's only defined in ecmascript v1.2+. For those of you not up on your ecmascript history, that's v4 browser era.

    This wouldn't be _that_ bad if I didn't want to use it in the Array Declaration area of asp.net. This area doesn't let you use script to determine browser support. It's declarative by nature. So if it gets emited to an older browser, I get a nasty bad syntax error with no way to get around it. Not only that, but it would probably disrupt every other script on the page, because all asp.net array declarations go in the same script block.

    So, to keep my nice syntax ( no magic numbers! ) in the library area of the script, I have to do something else for older browsers.

    What you can do is check the script version server side, and emit something like the following function for v1.1- browsers:

    function MetaBuilders_Initialiser() {
      for( var i = 0; i < arguments.length; i = i + 2 ) {
        this[ arguments[ i ] ] = arguments[ i + 1 ];
      }
      return this;
    }

    then change the array declaration to call it like this:

    var Foos = new Array( new MetaBuilders_Initialiser('FirstName','Andy','LastName','Smith') , new MetaBuilders_Initialiser('FirstName','Jon','LastName','Doe') );

    For script version 1.2+ browsers, you can use the simpler syntax:

    var Foos = new Array( { FirstName:'Andy', LastName:'Smith' }, { FirstName:'Jon', LastName:'Doe' } );

    The two have the same result. With this branch in logic at the array level, you can support simple objects in the Init function so you don't have to deal with array indexes. I'm not sure if i'm going to do this or just go back to my old ways. But I just wanted to let everybody know my findings.

  • documentation of previous javascript syntax discovery

    After hunting a bit in the official standard for ecmascript ( javascript ), I found where it defines the behavior darren found, and that I really liked.

    It is listed in section 11.1.5 as “Object Initialiser”.

    An object initialiser is an expression describing the initialisation of an Object, written in a form resembling a literal. It is a list of zero or more pairs of property names and associated values, enclosed in curly braces. The values need not be literals; they are evaluated each time the object initialiser is evaluated.

    Being able to

    define, instantiate, and initialize an object, declaratively, in one statement-section of code... it's amazingly useful.

    I just don't get how... after my YEARS of using javascript... that I've never come across this syntax.

  • neat javascript syntax

    I was talking with Darren earlier after he uncovered a neat javascript syntax. Besides being fairly neat looking, I also think it would be incredibly useful when using RegisterArrayDeclaration method for customizing instances of controls that use script.

    I was just testing it a little bit, and this is the kind of syntax that you can use with it.

    <html><body><form method="get" action="foo.html">
    <script language="javascript">
    // This is the result of RegisterClientScriptBlock
    function Foo_Init() {
     for( var i = 0; i < Foos.length; i++ ) {
      // regardless of the number of properties being pulled, 
      // it's always a simple i++ to the next item
      var fooProperties = Foos[i];
      
      // note the complete lack of of magic numbers to get the properties out.
      // the order of the items in the array declaration are irrelevant
      // and this is much more readable
      var fooInstance = document.getElementById(fooProperties.ID);
      fooInstance.innerHTML = fooProperties.html;
     }
    }
    </script>
    <span id="FooInstance1"></span>
    <br>
    <span id="FooInstance2"></span>
    <script language="javascript">
    // This is the result of RegisterArrayDeclaration
    // Each server control simply wraps its properties in { .. } to indicate an item block
    var Foos = new Array( {ID:"FooInstance1", html:"I'm a Foo!"}, {ID:"FooInstance2", html:"I'm a \"Foo\" Too!"} );
    </script>
    <script language="javascript">
    // This is the result of RegisterStartupScript
    Foo_Init();
    </script>
    </form></body></html>

    This is a great syntax. It's my new standard

  • update and solution to my "you don't want to disable buttons' post

    A few days I did something I don't generally like to do.

    I posted a reason to not use one solution to a problem without giving a better way to solve it. I told everybody not to disable buttons after they've been clicked. Well, I had a flash of brilliance, and now I can offer a real solution. Introducing OneClick.

    Serverside, no javascript required, and doesn't have any of the problems of disabling the button.

    Hope you like it.

  • why do i get sucked into these things

    I generally don't indulge in the define-me-tests that tend to sweep across blogs.

    What OS am I? What Matrix character am I? blah blah blah...

    But I do happen to think that Meyers Briggs is fairly useful and accurate. I've taken the M-B test quite a few times in my relativily short lifetime, and every time I've come back as a very strong INTP. Which as Luke Hutteman heard about, and I confirm... is something we share with Mr Albert Einstein. Which is why, if you've ever chatted with me on MS Messenger 6, you'd have seen my icon to be a rather famous and humorous picture of the man.

  • asp.net dialogs aren't impossible

    Custom Dialogs in asp.net

    It seemed like they'd always be a real pain, until Jesse Ezell blogged about his team's frustration with it, along with some neato example code for how they solved it. It was a great idea, and Jesse should definitely be praised for posting it to the community.

    However, typical me, I wanted a better Plop-It-In© experience with the dialog control. I was also a little unhappy with a couple aspects of it, such as violating the rule of Thou Shalt Not Call __doPostBack Directly. So I started pounding out code.

    The first thing I did was reorganize everything so that handling the standard dialog was simply a matter of adding event handler that got the results. However, the first time I went to test my new creation... my Google Toolbar Popup Blocker blocked the popup dialog. D'oh. This was because the window.open was written to the postback's response stream from a serverside call to Open. Holding down ctrl quickly got old. So I added button and linkbutton controls that are pointed at dialog window controls, and open the dialog immediately on clientside click, thus eliminating both the popup blocker problem and the extra postback.

    After that initial framework was done, I was thinking hey, I should provide some useful generic dialogs in the box... And since jscript is missing the InputBox and flexible MessageBox of VB, I figured those would be great candidates. I decided to implement them using classes inside the assembly, using the IHttpHandler interface stuff. There is a bit of Good vs. Bad in that decision. Good: Only one file, the control assembly, needs to be kept track of by the app developer. Bad: The handler factory needs to be registered in web.config or the built in dialogs don't work. And what made the Bad even worse, was that there's no api to determine if handlers are registered. But in the end I decided to stick with a handler factory, because, well, I think they are cool and ultimately easier to manage.

    The actual implementation of the InputBox and MessageBox are fairly vanilla. They derive from DialogPage, which derives from Page, so I got the IHttpHandler implementation for free. They are basically just no-declaration aspx files embedded in my assembly. The messagebox does have one neat thing tho. It includes the standard messagebox icons like the exclamation point and stop sign. To do this, I added gifs of the icons as embedded resources, and included an IHttpHandler which reads the image resources and writes it to the response stream. very nifty... and another reason why I went with handlers instead of external files.

    Oh, ya, make that a few more neat things. Both InputBox and MessageBox use system colors on systems that can do it. Under mozilla, it actually looks like a real dialog box, while IE still has a system window border. Oh, and I even did a little guesstimating to try to make sure that the messagebox is always big enough to hold the prompting text you give it.

    The only thing i'm not happy with right now is that the dialogs aren't modal. I plan on correcting that in the future, but since it's a big browser-compat problem, I decided to release what I have now, and work on that for v2.

    Anyway, I really hope everybody, and especially Jesse, find my version of the dialog control as useful and neat as I did.

  • DataGridGirl has a great new column on MSDN

    Just read on my MSDN feed that Marcie DataGridGirl Robillard has had a new article published on Custom DataGrid Colulmns.

    Besides the fact that it's a great column, I also wanted to point at it because she was nice enough to mention MetaBuilders. Seeing my website mentioned on a microsoft.com property always brings a smile to my face. :)

    Oh, and also wanted to thank Scott Mitchell for pimping me as well, in his column on injecting clientside script from controls

  • determining if your httphandler is registered correctly

    I've googled. I've forumed. I've dissassembled.

    I was unable to find any API for determining, at runtime, from a custom control, if a custom IHttpHandler in the same assembly has been registered correctly for the application that my custom control is running in. This makes it rather awkward to do IHttpHandlers in 3rd party components, as there's no nice way to alert the developer that things aren't set up quite right. A 404 error isn't very useful if the developer didn't know he was supposed to register a handler.

    So at this point, I gave up trying to do things a nice way, and wrote a horribly hackish helper function to determine if a handler is installed. I decided to make a full webrequest to my own application looking for the handler. A 404 means it's not installed correctly. Of course, I cache the result so I'm not doing this too often. here you go:

      private void EnsureHandler(String handlerName) {
       String key = "IHttpHandler Installed " + handlerName;
       if ( HttpContext.Current.Cache[key] == null ) {
        Uri url = HttpContext.Current.Request.Url;
        String urlForHandler = "http://" + url.Host + ":" + url.Port.ToString() + this.ResolveUrl("~/" + handlerName);
        System.Net.WebRequest request = System.Net.WebRequest.Create(urlForHandler);
        try {
         System.Net.WebResponse myResponse = request.GetResponse();
         HttpContext.Current.Cache[key] = true;
        } catch( System.Net.WebException ex ) {
         HttpContext.Current.Cache[key] = false;
        }
       }
       Boolean isInstalled = (Boolean)HttpContext.Current.Cache[key];
       if ( !isInstalled ) {
        throw new ApplicationException("The '" + handlerName + "' handler must be installed in web.config.");
       }
      }
    

    This code assumes that the handler is a specific, fully-named handler, like MyReallyCoolHandler.axd.

    It also assumes that the handler won't throw an exception if the handler is called with an empty query string. You may want to adjust the querystring and your handler so that can do something simple that doesnt' throw an exception when testing for the handler's existance like this.

    Oh, and if you know of a good way to do this... PLEASE let me know.

    UPDATE
    Scott wanted to know why I don't just check the web.config. You can't do that, because the httphandler might be registered in any .config up the chain, including machine.config. It would give out false positives.

  • you don't want to disable buttons after they are clicked

    No, i'm not trying to use jedi mind tricks on you.

    You really don't want to go down the road of disabling buttons after they've been clicked. Here's what happens... A developer sees that some user with zero patience is clicking the “Process Page” button a million times while the server is processing the request. This is causing havoc, because your click handler for the button is running a million times, and now you have quite a few records in your database, or database errors about duplicated records, depending on how you have your db set up.

    The knee-jerk reaction is, “ok, set the disabled property to true on the button after the first time they click it.”
    This is a bad idea.

    A disabled button doesn't have it's Name/Value pair sent in the form post. Without the Name/Value pair, the serverside has no idea that the button was clicked. So your Click event doesn't get run.

    “Ok, so don't disable it. set the onclick event to just return false immediately.”

    Ah, ok, this is a little better... but there's still a couple subtle problems. The user is given no visual cues that the button is no longer functional. This doesn't seem like a problem, unless the original button click didn't actually work. Replace “zero patience user” with “patient user with a horrible net connection”. Let's say the first click didn't work because the net connection died while he was viewing the page. He clicks the button, the button is now “disabled”, but the form post doesn't reach your server at all. So he reconnects to the net, and clicks the button... and nothing happens. Your average user has no clue why your form doesn't work, and leaves promptly, never to return. D'oh.

    “couldn't the hit refresh to regain the button's functionality?”

    That makes 3 big assumptions about the user:
    1) They know what a “refresh” button is
    2) They know where the refresh butto is.
    3) The know that clicking it will solve their problem.

    Sure, YOU understand what's going on... you developed it. How would you feel if you needed to stop your car, turn it off, turn it back on, and continue driving... when you've hit the turn blinker too many times in a row. It's just a horrible answer.

    So anyway...

    The real answer to Mr Zero Patience Guy is on the serverside. Make your db design able to handle multiple calls to the “Process Page” button gracefully. Now, I don't know enough about bulletproof db design to give you the best-practice for that... It'd depend on your existing db design and processing model, i'd imagine... but trust me, this is where the answer lives.

    UPDATE
    Alert reader Nicole commented here that the db isn't the best place either. And I agree. I was getting in a hurry to finish the blog entry, and kinda misspoke. I meant to say that the solution is somewhere on the serverside instead of the clientside, whether it be in the db, business layer, or UI layer of the server code. She pointed at a post she's made before about a “submission tracker” concept that I've seen before. And tho I've never seen any actual code samples for the concept, I do think it's a good plan.

More Posts Next page »