Background image for a table row in IE7

Having this structure:

<tr class="current">
 <td class="project-number">
 </td>
 <td>
 </td>
</tr>

.current {background-color: #dcdcdc !important; background: url(../images/current-item.png) no-repeat 0 0;}

Class “current”  would display a “>” arrow to highlight the selected row, however IE7 displays the arrow on each table cell.

The solution is to include a css just for IE7:

<!--[if lte IE 8]><link rel="stylesheet" type="text/css" href="css/ie7.css" /><![endif]-->

Then in ie7.css:

.current td {background: none;}
.current td {background-color: #dcdcdc !important;}
.current td:first-child {background-color: #dcdcdc !important; background: url(../images/current-item.png) no-repeat 0 0;}

Update: Chrome and Safari have the same bug as IE7, Opera renders the image correctly.

Multi select DropDownList in ASP.NET

I needed a dropdown list that allows multi select, a sort of checkbox list paired with a dropdown. For example is very useful when working with months. MS Reporting Services has one.

After googling for a while, I couldn’t find one to satisfy me and I started to play with a dropdown. A click on dropdown and a list of checkboxes appears, another click and the list dissappears. Problem was that the dropdown wanted to show it’s “Select month” option.

Long story short, I paired a TextBox, styled with css to have a “select arrow”, with a CheckBoxList and some jQuery:

Multiselect DropDownList

And here is the code: MonthsControl-src.7z

Cannot pass arguments to OnClientClick javascript

I want to give more information to the confirm dialog when the user deletes a record.
I’m using an asp:LinkButton that can execute some javascript when the user clicks it, in the “OnClientClick” property.

<ItemTemplate>
     <asp:LinkButton ID="lnkClose" runat="server" CausesValidation="False"
         data-number='<%# Eval("Number") %>' CommandArgument='<%# Eval("Id") %>'
         CommandName="close" CssClass="selected_item close-project">Close
    </asp:LinkButton>
 </ItemTemplate>

Unfortunately, I cannot pass the project number as an argument to the javascript function in OnClientClick.

Luckily, there is jQuery:

$('#ProjectsList a.close-project').live("click", function()
 {
     var $parentRow = $(this).parent().parent();
     $parentRow.toggleClass("current");

     var projectNumber = $(this).attr("data-number");
     var answer = confirm("Close project " + projectNumber + "?");
     if (!answer)
     {
          $parentRow.toggleClass("current");
     }
     return answer;
 });

Update: There is a way to pass arguments:

http://forums.asp.net/t/980311.aspx/1?Eval+in+an+OnCLientClick

Set active menu item

I have a menu and I need to highlight the menu item that loaded the current page. I tried to set server-side the id of the body to something unique and pertaining to the current page being loaded. Surprisingly, I could not get the page file name (or some other unique Id) but only the page title which wasn’t good enough.

The HTML (in a Master page):

<div id="menu">
    <ul>
        <li id="regions">
            <asp:HyperLink ID="hlRegions" runat="server" CssClass="menu-item"
                                  NavigateUrl="~/Regions.aspx">Regions</asp:HyperLink>
        </li>
        <li id="municipalities">
            <asp:HyperLink ID="hlMunicipalities" runat="server" CssClass="menu-item"
                                  NavigateUrl="~/Municipalities.aspx">Municipalities</asp:HyperLink>
        </li>
    </ul>
</div>

The Javascript:

<script type="text/javascript" >
        function getPageName()
        {
            var loc = document.location.href;
            return loc.substring(loc.lastIndexOf('/') + 1, loc.indexOf('.')).toLowerCase();
        }
        jQuery(document).ready(function($)
        {
            var pageName = getPageName();
            log("pageName:", this, pageName);

            $('#menu li').removeClass("selected-menu");
            $('#menu ul #' + pageName).addClass("selected-menu");
        });
</script>

Each time the page loads, the “selected-menu” class is removed from all li items under #menu. Then, the “selected-menu” class is added to the li item that has the id equal to the page being loaded.

GridView, NHibernate and sorting

If you’re using an ASP.NET GridView with a custom DAL you might get this error when trying to sort the grid by clicking the column headers:

The data source does not support sorting with IEnumerable data. Automatic sorting is only supported with DataView, DataTable, and DataSet.

The web has some solutions to this problem, however I’ve found them either not working, sorting only in memory or too complex.

I’m using NHibernate and the simplest solution was to parse the sortExpression from the ObjectDataSource into a list of NHibernate.Criterion.Order, then add the Orders to the ICriteria.

This way, I only have to add SortParameterName=”sortExpression” to the ObjectDataSource and the corresponding code in the Select method.

The ObjectDataSource:

<asp:ObjectDataSource ID="odsProjects" EnablePaging="true" runat="server"
    SelectMethod="FindAll"
    SortParameterName="sortExpression"
    SelectCountMethod="GetCountOfProjects" TypeName="Model.Project">
</asp:ObjectDataSource>

The GridView:

<asp:TemplateField HeaderText="Title" SortExpression="Title">
    <ItemTemplate>
        <asp:LinkButton ID="lnkProjectTitle" runat="server"
         CommandName="select" Text='<%# Eval("Title") %>'
         CausesValidation="False"></asp:LinkButton>
    </ItemTemplate>
</asp:TemplateField>

The Find method in the Project class:

public static IList<Project> FindAll ( int startRowIndex, int maximumRows, string sortExpression)
{
    ISessionFactoryHolder sessionHolder = ActiveRecordMediator.GetSessionFactoryHolder();
    ISession session = sessionHolder.CreateSession( typeof( Project ) );

    try
    {
        ICriteria crit = session.CreateCriteria( typeof( Project ) );

        crit.SetFirstResult( startRowIndex )
            .SetMaxResults( maximumRows );
        // the important thing
        if ( string.IsNullOrEmpty( sortExpression ) )
            crit.AddOrder( Order.Desc( "Id" ) );
        else
        {
            foreach ( Order order in Extensions.CreateOrdersFrom( sortExpression ) )
            {
                crit.AddOrder( order );
            }
        }

        return crit.SetCacheable( true ).List<Project>();
    }
    catch ( Exception ex )
    {
        log.ErrorException( "FindAll: ", ex );
        return new List<Project>();
    }
    finally
    {
        sessionHolder.ReleaseSession( session );
    }
}

The CreateOrdersFrom method:

/// <summary>
/// Creates a list of NHibernate.Criterion.Order from an sql sort expression. If sort is missing, ASC is assumed.
/// </summary>
/// <param name="sqlSortExpression">Id DESC, Number ASC or Id, Name desc</param>
/// <returns></returns>
public static List<Order> CreateOrdersFrom ( string sqlSortExpression )
{
    if ( string.IsNullOrEmpty( sqlSortExpression ) )
        return new List<Order>();

    List<Order> orders = new List<Order>();
    string[] sortExpressions = sqlSortExpression.Trim().Split( ',' );

    Array.ForEach( sortExpressions, sortExpression =>
    {
        log.Debug( "expr: {0}.", sortExpression );

        string[] columnAndSort = sortExpression.Trim().Split( ' ' );

        string column = "", sort = "asc";

        if ( columnAndSort.Length == 1 )
        {
            column = columnAndSort[0];
        }
        if ( columnAndSort.Length == 2 )
        {
            column = columnAndSort[0];
            sort = columnAndSort[1];
        }
        log.Debug( "{0} x {1}.", column, sort );

        orders.Add( sort.ToLowerInvariant() == "asc" ? Order.Asc( column ) : Order.Desc( column ) );
    } );

    return orders;
}