Efran Cobisi's Blog

.NET wonders (and alike)

  • Identifying ASP.NET controls is the first step

    Every time I build an ASP.NET web form, I invest a certain amount of my development effort in giving each control a meaningful identifier. This practice tends to give back cleaner code, leading the whole code base more mainteinable. And, as you probably know, code mainteinability is a key rule to cut down the total cost of ownership (TCO) of software projects.

    I've seen, however, a worrying number of ASP.NET developers who simply do not care about control naming and leave their code full of meaningless identifiers. So, for example, many tend to name their controls after the flow they follow inside the page: HeadLabel, BodyLabel, FooterLabel and so on. Many prefer to use Visual Studio designer and leave it automatically generate identifiers for their controls: this way, each control identifier would be made of a common type prefix, optionally defined by a ToolboxData attribute, and an incremental number (ie. Label1, Label2, and so on). These habits, which could effectively deflate the effort required to build a demo or a very small project, would eventually have the opposite effect in greater projects; trying to find out which of your 100 labels is the right one is like looking for a needle in a haystack.

    As general code naming rules suggest, a better approach consists in thinking about what your controls are about and what they are used for within the hosting page. Given that, you have a starting point you could use to give your controls a correct identifier; the string you would eventually come up with is usually made up of one to three-four combined words, in pascal case, for example: TotalAmount, Result, LastInsertedAlert, etc.
    Many developers (me included) then tend to prefix this string with a small one (usually two to four characters in length), that should make clear the type of the control at the final users (yes, you included). This practice, which has its roots in the Hungarian code notation, a naming convention created by Microsoft's Charles Simonyi, actually lead to self-describing control identifiers. So, following this convention, a label identifier could be for example lblTotalAmount, where the "lbl" prefix is there just to tell the reader that it is a Label control and the rest of the identifier tells her its purpose. A big improvement over seeing your code full of Label1, Label2, Labeln, eh?

  • A simple PowerShell script to find and replace using regular expressions in multiple files

    One thing I need which I come across from time to time is the ability to perform a find and replace operation in multiple files, using regular expressions. When this happens, I usually tend to exploit Visual Studio's own support for this kind of necessity; soon, however, I have to give it up and blame my favorite IDE for the lack of adherence with the regular expressions syntax adopted by the .NET framework, which I'm used to.
    So, today, after my umpteenth unsuccessful attempt with Visual Studio, I resolved to implement a simple PowerShell script stub, which would act as a strating point for performing this job for me hereafter. No, this is not by far a complete grep-like tool; I would like it to be just a demonstration of how easy, powerful and "clean" are PowerShell scripts like this one. And yes, I know there is plenty of third party tools which do this kind of things...

    To go down into the specifics of my problem, I was trying to combine a set of html files, that I grabbed after a CHM to HTM conversion, into a single one; since images inside these documents are just thumbnails contained inside an hyperlink which let the user eventually click to see the image at the original size, I want to perform some regular expressions substitution in order to have the original size image embedded directly into the document, have the thumbnails removed and the header and footer of each individual html file removed before being combined into the target one.
    Since PowerShell is a .NET managed shell, we can naturally use our beloved Regex class to perform our regular expressions substitution, thus adopting the syntax we are accustomed with.

  • Panel's DefaultButton whims: not every IButtonControl is the same...

    Today's story begins with a poor developer trying to obey his creative director new guidances in terms of how that web application should have its buttons rendered. I will omit the motivation behind this decision, because it is not relevant to this post's subject.
    The creative director said: "Hey, I'd like them to be rendered as html anchors!". The poor developer replied saying: "I can't see any difference, what about leaving them as they are (html input buttons)?". And the other: "Well, we can apply CSS styles better to html anchors than to html input buttons!".
    So, the poor developer started reflecting his creative director desires.
    After some hours spent in playing nicely with the new html anchor based buttons, something hurt the poor developer heart: all the Panels which uses the DefaultButton property in the web application and previously worked well now started behaving in a strange manner. To his amazement, the poor developer discovered that all of the DefaultButtons now worked only in Internet Explorer. Oh my... What about that cross browser thing?

    Let's help that poor developer.

    Whenever you use the DefaultButton property of the Panel control, you can choose among a set of ready to use controls which could act as buttons, all of them implementing the IButtonControl interface. At runtime, the Panel control registers a client script which would fire that default button, upon the user has hitted the enter key inside one of the html element hosted inside that Panel.
    The client function which handles the keypress event for the Panel client element is WebForm_FireDefaultButton. Here's its body:

    var __defaultFired = false;
    function WebForm_FireDefaultButton(event, target) {
        if (!__defaultFired && event.keyCode == 13 && !(event.srcElement && (event.srcElement.tagName.toLowerCase() == "textarea"))) {
            var defaultButton;
            if (__nonMSDOMBrowser) {
                defaultButton = document.getElementById(target);
            }
            else {
                defaultButton = document.all[target];
            }
            if (defaultButton && typeof(defaultButton.click) != "undefined") {
                __defaultFired = true;
                defaultButton.click();
                event.cancelBubble = true;
                if (event.stopPropagation) event.stopPropagation();
                return false;
            }
        }
        return true;
    }


    As you could see, it checks (among other things) if the default button "click" property is defined before getting the work done. Unluckily, and this is the culprit of our poor developer pain, the "click" property is not supported by every IButtonControl rendered html element. In fact, html anchors seem to not support that property (yes, IE does support it, but we wanted it to be cross browser, do you remember?).
    So, how could we solve this problem and make DefaultProperty works with... say... FireFox? One way could be to redefine WebForm_FireDefaultButton and make it smarter than the original one; we could check for the "click" property first and then for the "href" property, which html anchors have.

    So, here's a little smarter reimplementation of the aforementioned client function, ready to be pasted in your web page or client script file:

    function WebForm_FireDefaultButton(event, target) {
        if (!__defaultFired && event.keyCode == 13 && !(event.srcElement && (event.srcElement.tagName.toLowerCase() == "textarea"))) {
            var defaultButton;
            if (__nonMSDOMBrowser)
                defaultButton = document.getElementById(target);
            else
                defaultButton = document.all[target];

            if (typeof(defaultButton.click) != "undefined") {
                __defaultFired = true;
                defaultButton.click();
                event.cancelBubble = true;
                
                if (event.stopPropagation) event.stopPropagation();
                return false;
            }

            if (typeof(defaultButton.href) != "undefined") {
                __defaultFired = true;
                eval(defaultButton.href.substr(11));
                event.cancelBubble = true;
                
                if (event.stopPropagation) event.stopPropagation();
                return false;
            }

        }
        return true;
    }

    As all of the postback event references begin with the "javascript :" fake protocol, we could get the href of the DefaultButton, strip that protocol prefix and evaluate the rest. That will perform our craved postback, in a cross browser way.

    And our poor developer is finally happy. Now guess who was that poor developer... :)

  • Weird trap while using WebForm_DoCallback() inside my external client scripts

    Today, while I was trying to move large portions of a web application I'm working on from Ajax.NET to ASP.NET AJAX and callback support, I've bumbed into a weird trap related to how callback targets are resolved at server side.
    The story begins with me trying to adapt a custom control in such a way that events that originally got fired at client side through the elegant Ajax.NET APIs would instead get fired via ASP.NET callback; the reason behind this decision is twofold.
    First of all, I've chosen to leave behind Ajax.NET because I'd like to start using a "standard" library for my ajax behaviors. Even if I've never run into any problems using Ajax.NET and I've loved it for quite some time, I hope Microsoft will make their best effort to support and integrate this platform inside the ASP.NET framework, even better than any other ISV could. Then, why callbacks and not, for example, async postbacks? Well, to my surprise, I've ascertained async postbacks are quite verbose in their communications. Since my project goals include saving as much bandwidth as possible, I've looked for an alternative. Callbacks come to my rescue... And average payloads of this technology are very similar to Ajax.NET ones.

    Back to my original problem. I started preparing my control for callbacks support: I've coded its implementetion for ICallbackEventHandler interface and called ClientScript.GetCallbackEventReference() in its OnInit (...in vain, just to setup the callback support. Very annoying!). Now I was trying to use the WebForm_DoCallback function inside my javascript code; yes, I'm using an external script file, so I've no way to use the ClientScript.GetCallbackEventReference() method to obtain what I was looking for.
    At first, it seems WebForm_DoCallback expects the control ID of the target as its first argument. So, I've retrieved the control's ClientID and fed WebForm_DoCallback with it. Everything worked as expected and I was very happy: the test page that was hosting my control played very nice.
    But, suddenly after I've moved my control inside another page, a page which uses a master page, the trick ceased to work. After my callback started, the page on the server side answer with an exception saying: "The target ... for the callback could not be found or did not implement ICallbackEventHandler.".
    I will not tell you how much time I've spent in trying to understand what was wrong with my code...

    In short, the answer to my problem was that WebForm_DoCallback() does expect the UniqueID of the target control, not the ClientID. If you miss this point, you will get callback support that wouldn't work with master pages!

  • Tips about exam 70-431 (MCTS: SQL Server 2005)

    On Friday, 9 February, 2007 at about 11:30 AM I passed exam 70-431 and earned the title of MCTS: SQL Server 2005; now that the excitement is growing dim, I'd like to take the time to share my impressions on the new certification model embraced by Microsoft.
    First of all, I've been quite impressed by the number of simulations included in the exam. For those of you who are wondering, simulations are a (relatively) new kind of questions, very different from the usual multiple choice ones we were used to face in the past: they consist in a series of interactive screens which (almost) closely resemble the ones you should see while working with the GUI tools included with SQL Server 2005. So, to pass this kind of questions, you should be familiar with these tools.