Oct2009
9

ASP.NET Controls – Problem sorting GridView with SqlDataSource control

by nmgomes

Let me start by saying that Microsoft don't consider this issue as a problem, as you can see here this is a “by design” behavior.

The problem is well described in the referred Connect feedback and it contains a workaround.

Although simple, the workaround requires you to always register the GridView Sorting event and make the tweak according to the current GridView settings. Well, if are like me you will forget to do it half the times needed.

So, I made a not so simple workaround that will take care of the issue for me.

I override the OnSorting method from GridView so I can handle the GridViewEventArgs instance and override its SortDirection value.

To turn this into a general solution I partially reproduce the ParseSortString method from DataTable to find out if the current SortExpression contains either the ASC or DESC keywords.

Here is the code:

public class GridView : global::System.Web.UI.WebControls.GridView{    protected override void OnSorting(GridViewSortEventArgs e)    {        if (!string.IsNullOrEmpty(this.SortExpression))        {            if (this.SortExpression.Equals(this.SortExpression))            {                bool isMultipleSortExpression;                SortDirection? sortDirection = GetSortDirection(this.SortExpression, out isMultipleSortExpression);                if (sortDirection.HasValue)                {                    // To undo bug in GridView.HandleSort(string sortExpression) and then in GridView.CreateDataSourceSelectArguments()                    e.SortDirection = SortDirection.Ascending;                }            }        }        base.OnSorting(e);    }    private SortDirection? GetSortDirection(string sortExpression, out bool isMultipleSortExpression)    {        SortDirection? sortDirection = null;        isMultipleSortExpression = false;        string[] strArray = sortExpression.Split(new char[] { ',' });        for (int i = 0; i < strArray.Length; i++)        {            string strA = strArray[i].Trim();            int length = strA.Length;            if ((length >= 5) && (string.Compare(strA, length - 4, " ASC", 0, 4, StringComparison.OrdinalIgnoreCase) == 0))            {                sortDirection = SortDirection.Ascending;            }            else if ((length >= 6) && (string.Compare(strA, length - 5, " DESC", 0, 5, StringComparison.OrdinalIgnoreCase) == 0))            {                sortDirection = SortDirection.Descending;            }            if (!sortDirection.HasValue)            {                break;            }        }        if (sortDirection.HasValue)        {            if (strArray.Length > 1)            {                isMultipleSortExpression = true;            }        }        return sortDirection;    }}

Enjoy it.

Filed in: ASP.NET | CodeProject

May2009
17

ASP.NET Controls– Applying CSS styles to a Menu control

by nmgomes

This is subject to which I ended up returning from time to time, either for professional purposes, for some pet project or simply to help a friend.

For those of you that already played with Menu control knows that applying CSS styles to the standard ASP.NET Menu control can be a real frustrating task. For all the other I hope that this post avoid or at least reduce this pain.

Problem

The main question regarding styles is how the control renders. The Menu control renders HTML element TABLE, TR and TD and this approach bring us two main problems:

  • Turns CSS style configuration more complex
  • Increases the overall HTML size

I’m not sure why the control renders like this but I believe is related to multi browser CSS compatibility. We must not forget that even today we face several multi browser CSS problems (with IE8 the differences went minimal but we still have to deal with older browsers for many years) and by the time NetFX 2.0 saw the daylight the gap between browsers were larger and rendering HTML elements TABLE, TR and TD was the one that offered less compatibility issues.

Solution

Fortunately, ASP.NET 2.0 added the concept of ControlAdapter. This is a piece that can be associated to a control type and enable us to change the way that control renders.

As you can imagine, this opened a door to adjust controls without changing the control itself. But, there’s always a but, those kind of associations Adapter/Control are (out of the box) an application setting.

The mapping is made with a Browser Definition File Schema that should look similar to:

<browsers>    <browser refID="Default">        <controlAdapters>            <adapter controlType="System.Web.UI.WebControls.Menu"                     adapterType="YourMenuAdapter" />        </controlAdapters>    </browser></browsers>

These files should be deployed to the App_Browsers folder.

Now that we know about the ControlAdapters is clear that we need one to the Menu control. Since I’m a lazy programmer, I won’t create from scratch a MenuAdapter. Instead I will use an existing one.

You can find in CodePlex a project that give us a set of Adapters very useful in most common scenarios: ASP.NET 2.0 CSS Friendly Control Adapters.

Between those adapters there’s a specific one for the Menu control. This adapter renders the Menu control as an unordered list using Div, Ul e Li HTML elements and this way allow us to easily apply CSS styles without loosing multi browser compatibility and with less Html signature. Here is an Html sample:

<div class="AspNet-Menu-Horizontal">    <ul class="AspNet-Menu">        <li class="AspNet-Menu-WithChildren  AspNet-Menu-Selected"><a href="javascript:__doPostBack('ctl00$B$fvMenu','bTest 0')"            class="AspNet-Menu-Link  AspNet-Menu-Selected">Test 0</a>            <ul>                <li class="AspNet-Menu-WithChildren  AspNet-Menu-ParentSelected"><a href="javascript:__doPostBack('ctl00$B$fvMenu','bTest 0\\Test 1')"                    class="AspNet-Menu-Link  AspNet-Menu-ParentSelected">Test 1</a>                    <ul>                        <li class="AspNet-Menu-Leaf  AspNet-Menu-ParentSelected"><a href="javascript:__doPostBack('ctl00$B$fvMenu','bTest 0\\Test 1\\Test 1a')"                            class="AspNet-Menu-Link  AspNet-Menu-ParentSelected">Test 1a</a> </li>                        <li class="AspNet-Menu-Leaf  AspNet-Menu-ParentSelected"><a href="javascript:__doPostBack('ctl00$B$fvMenu','bTest 0\\Test 1\\Test 1b')"                            class="AspNet-Menu-Link  AspNet-Menu-ParentSelected">Test 1b</a> </li>                        <li class="AspNet-Menu-Leaf  AspNet-Menu-ParentSelected"><a href="javascript:__doPostBack('ctl00$B$fvMenu','bTest 0\\Test 1\\Test 1c')"                            class="AspNet-Menu-Link  AspNet-Menu-ParentSelected">Test 1c</a> </li>                    </ul>                </li>                <li class="AspNet-Menu-Leaf  AspNet-Menu-ParentSelected"><a href="javascript:__doPostBack('ctl00$B$fvMenu','bTest 0\\Test 2')"                    class="AspNet-Menu-Link  AspNet-Menu-ParentSelected">Test 2</a> </li>                <li class="AspNet-Menu-Leaf  AspNet-Menu-ParentSelected"><a href="javascript:__doPostBack('ctl00$B$fvMenu','bTest 0\\Test 3')"                    class="AspNet-Menu-Link  AspNet-Menu-ParentSelected">Test 3</a> </li>                <li class="AspNet-Menu-Leaf  AspNet-Menu-ParentSelected"><a href="javascript:__doPostBack('ctl00$B$fvMenu','bTest 0\\Test 4')"                    class="AspNet-Menu-Link  AspNet-Menu-ParentSelected">Test 4</a> </li>            </ul>        </li>    </ul></div>

A great sample is available here and the source code can be downloaded here.

Filed in: ASP.NET

Mar2009
29

ASP.NET Controls - Improving automatic ID generation : Live example

by nmgomes

Two weeks ago I was surprised by a CodeProject post written by Jeff Chin. The post is about How to shorten ASP.NET automatically generated control IDs and is an extension to my previous work on the same subject:

  • ASP.NET Controls - Improving automatic ID generation : Introduction ( Part 1)
  • ASP.NET Controls - Improving automatic ID generation : Concept ( Part 2)
  • ASP.NET Controls - Improving automatic ID generation - Architectural Changes ( Part 3)
  • ASP.NET Controls - Improving automatic ID generation : The ShortIDs Naming Provider (Part 4)

    Also, Jeff is using this approach in the "wild" and you can see it running at http://www.biocompare.com.

    Take a look at these two html source excerpts from the above site:

    <div class="checkBoxList module">
        <span id="T0_T8_T25_T1" class="columnCheckBoxList sub2 module">
            <input id="T0_T8_T25_T1_0" type="checkbox" name="T0$T8$T25$T1$0" /><label for="T0_T8_T25_T1_0">Life
                Science</label><input id="T0_T8_T25_T1_9" type="checkbox" name="T0$T8$T25$T1$9" /><label
                    for="T0_T8_T25_T1_9">Industry News</label><br />
            <input id="T0_T8_T25_T1_1" type="checkbox" name="T0$T8$T25$T1$1" /><label for="T0_T8_T25_T1_1">Molecular
                Biology</label><input id="T0_T8_T25_T1_10" type="checkbox" name="T0$T8$T25$T1$10" /><label
                    for="T0_T8_T25_T1_10">New Technology Alerts</label><br />
            <input id="T0_T8_T25_T1_2" type="checkbox" name="T0$T8$T25$T1$2" /><label for="T0_T8_T25_T1_2">Immunology</label><input
                id="T0_T8_T25_T1_11" type="checkbox" name="T0$T8$T25$T1$11" /><label for="T0_T8_T25_T1_11">Proteomics</label><br />
            <input id="T0_T8_T25_T1_3" type="checkbox" name="T0$T8$T25$T1$3" /><label for="T0_T8_T25_T1_3">Neuroscience</label><input
                id="T0_T8_T25_T1_12" type="checkbox" name="T0$T8$T25$T1$12" /><label for="T0_T8_T25_T1_12">Mass
                    Spectrometry</label><br />
            <input id="T0_T8_T25_T1_4" type="checkbox" name="T0$T8$T25$T1$4" /><label for="T0_T8_T25_T1_4">Cell
                Biology</label><input id="T0_T8_T25_T1_13" type="checkbox" name="T0$T8$T25$T1$13" /><label
                    for="T0_T8_T25_T1_13">Lab Automation</label><br />
            <input id="T0_T8_T25_T1_5" type="checkbox" name="T0$T8$T25$T1$5" /><label for="T0_T8_T25_T1_5">Cancer</label><input
                id="T0_T8_T25_T1_14" type="checkbox" name="T0$T8$T25$T1$14" /><label for="T0_T8_T25_T1_14">Pharmaceutical
                    News</label><br />
            <input id="T0_T8_T25_T1_6" type="checkbox" name="T0$T8$T25$T1$6" /><label for="T0_T8_T25_T1_6">Microbiology</label><input
                id="T0_T8_T25_T1_15" type="checkbox" name="T0$T8$T25$T1$15" /><label for="T0_T8_T25_T1_15">Cell
                    Signaling</label><br />
            <input id="T0_T8_T25_T1_7" type="checkbox" name="T0$T8$T25$T1$7" /><label for="T0_T8_T25_T1_7">Genomics</label><input
                id="T0_T8_T25_T1_16" type="checkbox" name="T0$T8$T25$T1$16" /><label for="T0_T8_T25_T1_16">Bioprocess</label><br />
            <input id="T0_T8_T25_T1_8" type="checkbox" name="T0$T8$T25$T1$8" /><label for="T0_T8_T25_T1_8">Drug
                Discovery</label><input id="T0_T8_T25_T1_17" type="checkbox" name="T0$T8$T25$T1$17" /><label
                    for="T0_T8_T25_T1_17">Plant Biology</label></span> <span id="T0_T8_T25_T2" style="color: Red;
                        display: none;"></span><a href="http://www.biocompare.com/" id='selectAll' class="button buttonGeneral"
                            onclick="return false;">Select All</a>
    </div>
    

    and

    <script type="text/javascript">
    //<![CDATA[
    var Page_ValidationSummaries =  new Array(document.getElementById("T0_T8_T25_T0"));
    var Page_Validators =  new Array(document.getElementById("T0_T5_T5"), document.getElementById("T0_T8_T25_T2"), document.getElementById("T0_T8_T25_T4"), document.getElementById("T0_T8_T25_T5"));
    //]]>
    </script>
    
    <script type="text/javascript">
    //<![CDATA[
    var T0_T5_T5 = document.all ? document.all["T0_T5_T5"] : document.getElementById("T0_T5_T5");
    T0_T5_T5.controltovalidate = "T0_T5_T6";
    T0_T5_T5.errormessage = "Please enter a search word";
    T0_T5_T5.validationGroup = "grpSiteSearch";
    T0_T5_T5.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
    T0_T5_T5.initialvalue = "";
    var T0_T8_T25_T0 = document.all ? document.all["T0_T8_T25_T0"] : document.getElementById("T0_T8_T25_T0");
    T0_T8_T25_T0.validationGroup = "vgNLSignUp";
    var T0_T8_T25_T2 = document.all ? document.all["T0_T8_T25_T2"] : document.getElementById("T0_T8_T25_T2");
    T0_T8_T25_T2.errormessage = "Please select atleast one Newsletter Interest";
    T0_T8_T25_T2.display = "None";
    T0_T8_T25_T2.validationGroup = "vgNLSignUp";
    T0_T8_T25_T2.evaluationfunction = "CustomValidatorEvaluateIsValid";
    T0_T8_T25_T2.clientvalidationfunction = "validateCheckBox";
    var T0_T8_T25_T4 = document.all ? document.all["T0_T8_T25_T4"] : document.getElementById("T0_T8_T25_T4");
    T0_T8_T25_T4.controltovalidate = "T0_T8_T25_T6";
    T0_T8_T25_T4.focusOnError = "t";
    T0_T8_T25_T4.errormessage = "Please enter your email address";
    T0_T8_T25_T4.display = "None";
    T0_T8_T25_T4.validationGroup = "vgNLSignUp";
    T0_T8_T25_T4.evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
    T0_T8_T25_T4.initialvalue = "";
    var T0_T8_T25_T5 = document.all ? document.all["T0_T8_T25_T5"] : document.getElementById("T0_T8_T25_T5");
    T0_T8_T25_T5.controltovalidate = "T0_T8_T25_T6";
    T0_T8_T25_T5.focusOnError = "t";
    T0_T8_T25_T5.errormessage = "Invalid E-mail address";
    T0_T8_T25_T5.display = "None";
    T0_T8_T25_T5.validationGroup = "vgNLSignUp";
    T0_T8_T25_T5.evaluationfunction = "RegularExpressionValidatorEvaluateIsValid";
    T0_T8_T25_T5.validationexpression = "^([\\w-]+(?:\\.[\\w-]+)*(?:[\\+]){0,1})@((?:[\\w-]+\\.)*\\w[\\w-]{0,66})\\.([A-Za-z]{2,6}(?:\\.[A-Za-z]{2})?)$";
    //]]>
    </script>

    As you can see, this approach really shorten the id value and is definitely reducing the total html size.

    It's public that ASP.NET 4.0 will bring some improvements on the generation of client IDs, but I still not seeing the ability to control how the ClientID are generated - "Hope is the last one to die".

    Good luck Jeff !!!

  • Filed in: ASP.NET