Mac type grid view control for asp.net/java web applications

 

For one of my project, I made a very dynamic grid which is totally HTML based and very fast while rendering data from server.

 

Features:

1.       No viewstate

2.       Dynamic grid which gets data on scroll using REST based WCF service in JSon

3.       No DOM modifications which makes the rendering very fast

4.       Custom control which can be integrated in any web application – Java / Asp.Net (However I built it for Asp.Net)

5.       Support for very large record set.

6.       Designer friendly – As the DOM doesn’t change and the basic structure remains the same.

7.       Highly extensible to support any kind of data

 

Concept:

The concept comes from Apple applications and IPhone/IPad where user is provided with very rich user interface which allows him to scroll through the records very fast. Actually what happens is that the basic structure of the grid remains the same (the table , tr, etc.) all that gets modified is the content of the control. For example, if we talk about a link button, only attributes that are modified are text, url. Rest remains the same. This is what will make the grid to function very fast as you are actually not modifying the DOM object from client but modifying some attributes which again would give better performance.

On UI, the grid is divided into two parts –

1.       Actual grid which will show the actual content. This would basically be the table with TR’s inside. The table will have overflow as false so that this grid doesn’t have a scroll bar. The content of this portion will be modified on scroll which is explained in the second part.

2.       Second Div which will have the scroll bar. The concept here would be to insert another Div in this which will have height = Total number of records * height of each TR. This would generate a scrollbar for user. The only thing that needs to be handled now is the scroll event.

Implementation (ASP.NET)

Basically this grid was designed for an ASP.NET project but can eventually be used in java also as it basically works on the basic html tags of HTML. I created a custom control here that would override the render mechanism of a WebControl.

Basic Architecture

The grid basically is a table with multiple number of TR and TD both getting derived from the user input. TD, user would give the column specification for grid in its definition in aspx.

 

<cc1:MashableGrid ID="myGrid" runat="server" ItemCss="row1" AlternatingItemCss="row2"

                        DataKeyColumnName="KeyId" Width="98.5%" Rows="10" Height="330"

                        DefaultSortExpression="KeyId" ServiceName="/BusinessService/GetData"

                        HeaderCss="tableHeader" RequestDelay="250" QStringColumnName="KeyId">

                        <Columns>

                            <Items>

                                <cc1:Column Width="10%" CssClass="boldFont tab_coloum_border"

                                    AllowSorting="true" AllowSearching="true" SortExpression="RoleName" DataFieldName="Name"

                                    DisplayProperty="IsDisplay" DataType="System.String">

                                    <Controls>

                                                                                    <asp:LinkButton ID="lnkBtn" Text="Name" IsResourceKeyRequired="false"

                                                EncryptedQStringColumnName="KeyId" ToolTip="Name" ClientClickFunction="EditName" />

                                       

                                    </Controls>

                                </cc1:Column>

                            </Items>

                        </Columns>

                    </cc1:MashableGrid>

 

What basically goes at the back is that a json collection is expected to the javascript code which basically replaces the control properties on the page with the ones mentioned in the json collection.

  


Let’s call the first div (big one) as DivMain. This div will have the main table which will actual data.

The div on the right is a composite grid with another grid into it. Lets call it DivScroller and this is having another div with name DivDummy.

DivMain has its overflow set to hidden and would never ever show scroller. All it will show is a table of content.

DivScroller having another div in it has overflow set to auto. This acts just as a container to another div which is the main thing that plays in this control. The height of this div would be set dynamically to show scroller in the parent div (DivScroller). Calculation is as follows

Height of DivDummy = total number of records * height of each td (DivMain)

By this you would achieve the desired scroller. Now binding the scroll event of this grid and displaying the data according to the scroll position is the main task left.

This would be achieved by a little calculation on the basis of scroll position

Start record to show = scrollPosition / height of each td(DivMain)

Now only thing left in code is how to get the data and bind it. So on scroll, there would be a service call that would ask for data from server (according to start count and number of records that are displayed).

Next thing and the biggest task left is to map and replace the content according to the data we got in json. If you saw it, we specified some properties while binding the grid (in aspx). That will play the trick. Every html control has max 3-4 attributes that are actually dynamic and are set. And this is what we will replace. Below is the code that does the trick.

 

MashableTree.prototype.FillData = function (list, mashTree) {

    if (list == undefined || list == null || list.length == 0) {

        $('#' + this.actualGridId + ' td').each(function () {

            this.style.display = 'none';

        });

        $('#' + this.actualGridId + ' th').each(function () {

            this.style.display = 'none';

        });

        return;

    }

    var mapping;

    var typeArr;

    var types = '';

    var type = '';

    var dataType = '';

    var strMappings = new Array();

    var map = new Array();

    var index = 0;

    var td = null;

    $('#' + this.actualGridId + ' td').each(function () {

        td = $(this);

        mapping = $(td).attr('mapping');

        dataType = $(td).attr('DataType');

        if (mapping != undefined) {

            types = $(td).attr('type');

            index = $(td).attr('index');

            typeArr = types.split(',')

            for (var t = 0; t < typeArr.length; t++) {

                type = typeArr[t];

                switch (type) {

                    case 'key':

                        str = mapping.split(',');

                        for (var i = 0; i < str.length; i++) {

                            if (str[i] != '') {

                                map = str[i].split(':');

                                if (map[0] == 'key') {

                                    if (index < list.length) {

                                        var id = parseInt(mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]));

                                        $(td).text(id.toString());

                                        if (mashTree.SelectedFolderId > 0) {

                                            if (mashTree.SelectedFolderId == id) {

                                                $(td).parent().addClass('SelectedTD');

                                            }

                                        }

                                    }

                                }

                            }

                        }

                        break;

                    case 'lnk':

                        str = mapping.split(',');

                        for (var i = 0; i < str.length; i++) {

                            if (str[i] != '') {

                                map = str[i].split(':');

                                if (map[0] == 'td_disp') {

                                    if (index < list.length) {

                                        var dispVal = mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]);

                                        if (!dispVal) {

                                            $(td).children().hide();

                                            break;

                                        }

                                        else {

                                            if (mapping.indexOf('img') < 0) {

                                                $(td).children().show();

                                            }

                                        }

                                    }

                                }

                                else {

                                    if (mapping.indexOf('img') < 0) {

                                        $(td).children().show();

                                    }

                                }

                                if (map[0] == 'lnk_text') {

                                    if (index >= list.length) {

                                        $(td).children()[0].innerHTML = '';

                                    }

                                    else {

                                        $(td).children()[0].innerHTML = mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]); // list[index][map[1]];

                                    }

                                }

                                if (map[0] == 'lnk_url') {

                                    if (index >= list.length) {

                                        $(td).children()[t].href = '';

                                    }

                                    else {

                                        if (map[1] != '') {

                                            $(td).children()[t].href = mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]); // list[index][map[1]];

                                        }

                                        else {

                                            $(td).children()[t].href = '#';

                                        }

                                    }

                                }

                                if (map[0] == 'lnk_function') {

                                    if (map[1].length > 0) {

 

                                        if (index >= list.length) {

                                            $(td).children().hide();

                                        }

                                        else {

                                            var tdHtml = $(td).html();

                                            var functionHtml = '';

                                            var indexStart = 0;

                                            var indexEnd = 0;

                                            var textToReplace = '';

                                            if (tdHtml != '') {

                                                indexStart = tdHtml.indexOf(map[1]);

                                                indexEnd = tdHtml.indexOf(';', indexStart);

                                                textToReplace = tdHtml.substring(indexStart, indexEnd + 1);

                                                if (mashTree.QStringColumnName != '') {

                                                    tdHtml = tdHtml.replace(textToReplace, map[1] + "('" + list[index][mashTree.QStringColumnName] + "');");

                                                    $(td).html(tdHtml);

                                                }

                                            }

                                        }

                                    }

                                }

                            }

                        }

                        break;

                    case 'lbl':

                        str = mapping.split(',');

                        for (var i = 0; i < str.length; i++) {

                            if (str[i] != '') {

                                map = str[i].split(':');

                                if (map[0] == 'td_disp') {

                                    if (index < list.length) {

                                        var dispVal = mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]);

                                        if (!dispVal) {

                                            $(td).html('');

                                            $(td).children().hide();

                                            break;

                                        }

                                        else {

                                            $(td).children().show();

                                        }

                                    }

                                }

                                else {

                                    $(td).children().show();

                                }

                                if (map[0] == 'lbl_text') {

                                    if (index >= list.length) {

                                        $(td).text('');

                                    }

                                    else {

                                        if (dataType == 'System.DateTime') {

                                            $(td).text(mashTree.ConvertToDate(mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1])));

                                        }

                                        else {

                                            $(td).text(mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]));

                                        }

                                    }

                                }

                            }

                        }

                        break;

                    case 'img':

                        if (index >= list.length) {

                            $(td).children().hide();

                            break;

                        }

                        else {

                            $(td).children().show();

                        }

                        str = mapping.split(',');

                        for (var i = 0; i < str.length; i++) {

                            if (str[i] != '') {

                                map = str[i].split(':');

                                if (map[0] == 'td_disp') {

                                    if (index < list.length) {

                                        var dispVal = mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]);

                                        if (!dispVal) {

                                            $(td).children().hide();

                                            break;

                                        }

                                        else {

                                            $(td).children().show();

                                        }

                                    }

                                }

                                else {

                                    $(td).children().show();

                                }

                                if (map[0] == 'img_url') {

                                    if (map[1].length > 0) {

                                        if (index >= list.length) {

                                            $(td).children()[t].style.display = 'none';

                                        }

                                        else {

                                            $(td).children()[t].style.display = '';

                                            $(td).children()[t].src = list[index][map[1]];

                                        }

                                    }

                                }

                                if (map[0] == 'img_display') {

                                    if (map[1].length > 0) {

                                        if (index >= list.length) {

                                            $(td).children()[t].style.display = 'none';

                                        }

                                        else {

                                            if (list[index][map[1]]) {

                                                $(td).children()[t].style.display = '';

                                            }

                                            else {

                                                $(td).children()[t].style.display = 'none';

                                            }

                                        }

                                    }

                                }

                                if (map[0] == 'img_function') {

                                    if (map[1].length > 0) {

 

                                        if (index >= list.length) {

                                            $(td).children().hide();

                                        }

                                        else {

                                            var tdHtml = $(td).html();

                                            var functionHtml = '';

                                            var indexStart = 0;

                                            var indexEnd = 0;

                                            var textToReplace = '';

                                            if (tdHtml != '') {

                                                indexStart = tdHtml.indexOf(map[1]);

                                                indexEnd = tdHtml.indexOf(';', indexStart);

                                                textToReplace = tdHtml.substring(indexStart, indexEnd + 1);

                                                if (mashTree.QStringColumnName != '') {

                                                    tdHtml = tdHtml.replace(textToReplace, map[1] + "('" + list[index][mashTree.QStringColumnName] + "');");

                                                    $(td).html(tdHtml);

                                                }

                                            }

                                        }

                                    }

                                }

                            }

                        }

                        break;

                    case 'chk':

                        str = mapping.split(',');

                        for (var i = 0; i < str.length; i++) {

                            if (str[i] != '') {

                                map = str[i].split(':');

                                if (map[0] == 'td_disp') {

                                    if (index < list.length) {

                                        var dispVal = mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]);

                                        if (!dispVal) {

                                            $(td).children().hide();

                                            break;

                                        }

                                        else {

                                            $(td).children().show();

                                        }

                                    }

                                }

                                else {

                                    $(td).children().show();

                                }

                                if (map[0] == 'chk_checked') {

                                    if (index >= list.length) {

                                        $(td).children()[t].style.display = 'none';

                                    }

                                    else {

                                        $(td).children()[t].style.display = '';

                                        $(td).children()[t].checked = mashTree.GetDataFromList(mashTree, list, parseInt(index), map[1]);

                                    }

                                }

                            }

                        }

                        break;

                    default:

                        {

                            if (index >= list.length) {

                                $(td).children()[t].style.display = 'none';

                            }

                            else {

                                $(td).children()[t].style.display = '';

                            }

                            break;

                        }

                }

            }

        }

    });

}

 

If you want to use this grid, I can share the code. Drop a comment on this post along with the email id.