Click here to Skip to main content
Click here to Skip to main content

How To Freeze Columns in Radgrid MVC

, 9 Aug 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Shows you how to freeze columns in Telerik Radgrid MVC, a feature which is not currently available in the control

Editorial Note

This article appears in the Third Party Products and Tools section. Articles in this section are for the members only and must not be used to promote or advertise products in any way, shape or form. Please report any spam or advertising.

Introduction

The purpose of this article is to freeze columns in RadGrid MVC. Currently, this feature is not available in RadGrid MVC, so I decided to create one instead. This article demonstrates the capability to make RadGrid columns static when scrolling.This is quite useful when you want to make part of the columns data visible at all times for the end users when having horizontal scrollbar for navigation.

Using the Code

To enable this grid functionality, merely set "freezeGridColumn" of the control from client side, which determines the count of columns (starting from the leftmost column) which will be statically positioned when you drag the horizontal scroll in the grid.

<script type="text/javascript">
    function applyFrozenColumns() {
        $('#CustomerGrid').freezeGridColumn(1);
    }
</script>
<div class="grid">
     <%
        Html.Telerik().Grid(Model)
            .Name("CustomerGrid")
            .Columns(columns => 
                     {
                         columns.Bound(c => c.Title).Width(80);
                         columns.Bound(c => c.FirstName).Width(280);
                         columns.Bound(c => c.LastName).Width(180);
                         columns.Bound(c => c.Address).Width(180);
                         columns.Bound(c => c.Suburb).Width(180);
                         columns.Bound(c => c.State).Width(180);
                         columns.Bound(c => c.Postcode).Width(180);
                         columns.Bound(c => c.Email).Width(180);
                         columns.Bound(c => c.Mobile).Width(180);
                         columns.Bound(c => c.Telephone).Width(180);
                         columns.Bound(c => c.Fax).Width(180);
                     })
        .Sortable(sorting => sorting.SortMode(GridSortMode.SingleColumn))
        .DataBinding(binding => binding.Ajax().Select("GetCustomers", "Home"))
        .Scrollable(scrolling => scrolling.Height(150)).Render();
    %>

    <% Html.Telerik().ScriptRegistrar().OnDocumentReady("applyFrozenColumns();"); %>
</div>

How Does It Work?

In order to implement the frozen columns, we must first understand how Radgrid is rendered, its overall structures and behaviours. If you look into the HTML source code in Firefox using Firebug, you can see that Radgrid contains 3 sections:

  1. Header
  2. Content
  3. Footer

source_code.gif

The sections that we are interested in are the Header and Content. Each section contains a table that defines the layout of the grid. The locking mechanism inside the grid that allows header and content to link together when scrolling are merely set by fixed width in each column.

If we look at the content section, it uses styling to do its basic scrolling. Content section contains a table with fixed width columns, if the table exceeds beyond the fixed width of its parent container, it simply scrolls automatically as default behaviour.

Now that we understand the basic mechanism behind Radgrid scrolling, we can implement our own to do frozen Columns. The steps below provide the high level design and the idea behind how it works.

Step 1

Disable the default scrolling in Content Container. To do that, we simply use jQuery to manipulate the content container style by making “overflow” hidden to prevent it from scrolling.

Step 2

Insert a custom frozen container after content container. Within frozen container, insert a child frozen container. The purpose of this is to create our own custom scrolling that enables frozen column feature. The parent frozen container will determine how much to scroll while the child container will determine the width of the table within content container.

source_code1.gif

Step 3

Determine how many columns are hidden behind content container. Once we have done that, we set scrolling for columns that are hidden behind content container. In doing so, it minimizes the costs of performance.

screenshot5.gif

Step 4

Attach to scroll event in scrolling container. When scroll event is fired, this is where we do our implementation.

Step 5

Determine the current scroll position, then display table header and table content columns accordingly. This is the core part of this feature, we check the current scroll position, if it exceeds the width of the display column then hide it, otherwise show it. The effect of hiding the table cells creates an illusion that we are scrolling when in fact it is only the cell inside the table that is hiding.

Technical Details

Create a new function in jQuery called "FreezeGridColumn" to enable frozen column feature. Then determine its binding behaviour, whether it's server binding or Ajax. With Ajax binding, we need to know when the Ajax request is completed. Otherwise, we will get wrong parent width when rendering is not completed.

 $.fn.freezeGridColumn = function(frozenColumnsCount) {
        
    if (!$(this).data('tGrid').isAjax()) {
        initFreezeColumns($(this), frozenColumnsCount);
    }
    else {
        $(this).ajaxStop(function() {
            initFreezeColumns($(this), frozenColumnsCount);
        });
    }
}; 

Insert custom scrolling handling in RadGrid to implement frozen column feature. Firstly, we need to disable default scrolling by setting style as "overflow hidden". Secondly, we create frozen containers below to handle custom scrolling.

 function createScrollContainer() {
        //disable grid default scrolling
        $(grid).find(".t-grid-content").css("overflow-x", "hidden");

        if ($(frozenContainer).length == 0) {
            //create controls to handle freeze column scrolling
            frozenContainer = $("<div id='gridfrozenContainer' />");
            frozenInnerContainer = $("<div id='gridfrozenInnerContainer' />");
        }

        $(frozenContainer).css("height", "17px").css("overflow", 
				"scroll").css("width", "100%");
        $(frozenInnerContainer).css("height", "17px").css("width", 
				$(gridContentTable).width());

        $(frozenContainer).append($(frozenInnerContainer));
        $(frozenContainer).insertAfter('.t-grid-content');
    } 

Determine how many columns are not visible in content container.

  //get how many columns are not visible behind the content container
    function getHiddenColCount() {
        var visibleWidth = grid.find("div.t-grid-header").width();
        var visibleColCount = 0;

        $.each(tableHeaderGroupCol, function(idx, value) {
            totalColWidth += getWidth($(value));

            if (visibleWidth >= totalColWidth) {
                visibleColCount = idx;
            }
        });

        hiddenColCount = totalColCount - visibleColCount;
    } 

Perform frozen function.

for (i = frozenColumnsCount; i <= hiddenColCount; i++) {
     totalColWidth += columnWidthArr[i - 1];

     var showCol = (scrollPos < totalColWidth);

     $(tableHeaderCol).eq(i).children().toggle(showCol);
     $(tableHeaderGroupCol).eq(i).toggle(showCol);
     $(tableContentGroupCol).eq(i).toggle(showCol);
     $(tableHeaderCol).eq(i).toggle(showCol);
     $(gridContentTable).find("tbody tr td[cellIndex=" + i + "]").toggle(showCol);    
}    

Browser Support

  • Tested in IE6, IE7, IE8, IE9, Chrome and Firefox

Limitations

  • Frozen column does not support when used with hierarchy grid

Screenshots

source_code3.gif

source_code2.gif

History

  • 08/08/2011
    • Fixed bug - Missing column when freezing multiple columns
  • 09/05/2011
    • Fixed last column not displaying correctly
  • 05/05/2011
    • Supports browsers Internet Explorer 8, Internet Explorer 9

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

telaron
Software Developer
Australia Australia
No Biography provided

Comments and Discussions

 
QuestionGood job! But needed some changes for footer PinmemberVasil806220-Mar-14 2:01 
I modify code to support footer
(function($) {
    var defaultFrozenCol = 1; //number of column to freeze
    var scrollDelay = 200; //delay in milliseconds
    var scrollStep = 50; //how big scroll step should be before taking action
    var freezeColStyle = "solid 2px #3462aa";
 
    var hiddenColCount = 0;
    var totalColWidth = 0;
    var currentScrollPos = 0;
 
    var gridHeaderTable;
    var gridContentTable;
    var gridFooterTable;
    var tableHeaderGroupCol;
    var tableHeaderCol;
    var tableFooterCol;
    var tableFooterGroupCol;
    var tableContentGroupCol;
    var totalColCount;
 
    var frozenColWidth = 0;
    var columnWidthArr = new Array();
    var colWidth = 0;
    var grid;
 
    var frozenContainer;
    var frozenInnerContainer;
    var frozenColCount;
    var browserVersion = parseInt($.browser.version);
 
    //Create freezeGridColumn function
    $.fn.freezeGridColumn = function(frozenColumnsCount) {
        initBrowsers();
        frozenColCount = (frozenColumnsCount != null) ? frozenColumnsCount : defaultFrozenCol;
 
        grid = $(this);
 
        var hasLoaded = false;
 
        if (!grid.data('tGrid').isAjax()) {
            initFreezeColumns();
        }
        else {
            grid.ajaxStop(function() {
                if (!hasLoaded) {
                    initFreezeColumns();
                    hasLoaded = true;
                }
                else {
                    refreshFreezeColumns();
                }
            });
        }
    };
 
    //Initialise all necessary grid info to begin frozen column implementation
    function initFreezeColumns() {
        getGridInfo();
        createScrollContainer();
        getHiddenColCount();
        createColumnWidthList();
        getFrozenColumnsWidth();
        showFrozenVerticalLine();
 
        var hasScrolled = false;
 
        $(frozenContainer).scroll(function(e) {
            e.preventDefault();
 
            var scrollDiff = Math.abs(currentScrollPos - $(frozenContainer).scrollLeft());
 
            if (scrollDiff >= scrollStep) {
                hasScrolled = false;
 
                $(frozenContainer).delay(scrollDelay).queue(function() {
                    if (!hasScrolled) {
                        hideFilterPanel();
                        performFreezeColumns();
 
                        hasScrolled = true;
                    }
 
                    $(this).dequeue();
                });
            }
        });
    }
 
    //refresh freeze column when ajax request
    function refreshFreezeColumns() {
        showFrozenVerticalLine();
        performFreezeColumns();
    }
 
    //show vertical line indicates how many columns are currently frozen
    function showFrozenVerticalLine() {
        $(tableHeaderCol).eq((frozenColCount - 1)).css("border-right", freezeColStyle);
        $(gridContentTable).find("tbody tr td[cellIndex=" + (frozenColCount - 1) + "]").css("border-right", freezeColStyle);
    }
 
    //get grid table details 
    function getGridInfo() {
        gridHeaderTable = grid.find(".t-grid-header table");
        gridContentTable = grid.find(".t-grid-content table");
        gridFooterTable = grid.find(".t-grid-footer table");
        tableHeaderGroupCol = $(gridHeaderTable).find("colGroup col");
        tableHeaderCol = $(gridHeaderTable).find("tbody tr th");
        tableContentGroupCol = $(gridContentTable).find("colGroup col");
	tableFooterGroupCol = $(gridContentTable).find("tbody tr.t-group-footer td");
        tableFooterCol = $(gridFooterTable).find("tbody tr td");
        totalColCount = $(tableHeaderGroupCol).length;
    }
 
    //get how many columns are not visible behind the content container
    function getHiddenColCount() {
        var visibleWidth = grid.find("div.t-grid-header").width();
        var visibleColCount = 0;
 
        $.each(tableHeaderGroupCol, function(idx, value) {
            totalColWidth += getWidth($(value));
 
            if (visibleWidth >= totalColWidth) {
                visibleColCount = idx;
            }
        });
 
        hiddenColCount = totalColCount + visibleColCount;
 
        $(tableHeaderGroupCol).eq(totalColCount - 1).css("width", "100%");
        $(tableContentGroupCol).eq(totalColCount - 1).css("width", "100%");
    }
 
    //create column width array to detect which column to hide when scrolling
    function createColumnWidthList() {
        for (i = frozenColCount; i <= hiddenColCount; i++) {
            columnWidthArr.push(getWidth($(tableHeaderGroupCol).eq(i)));
        }
    }
 
    //get total width for frozen column
    function getFrozenColumnsWidth() {
        //determine the total width of frozen columns
        for (i = 0; i <= frozenColCount - 1; i++) {
            frozenColWidth += getWidth($(tableHeaderGroupCol).eq(i));
        }
    }
 
    //create frozen scrolling container
    function createScrollContainer() {
        //disable grid default scrolling
        $(grid).find(".t-grid-content").css("overflow-x", "hidden");
 
        if ($(frozenContainer).length == 0) {
            //create controls to handle freeze column scrolling
            frozenContainer = $("<div id='gridfrozenContainer' />");
            frozenInnerContainer = $("<div id='gridfrozenInnerContainer' />");
        }
 
        $(frozenContainer).css("height", "17px").css("overflow", "scroll").css("width", "100%");
        $(frozenInnerContainer).css("height", "17px").css("width", $(gridContentTable).width());
 
        $(frozenContainer).append($(frozenInnerContainer));
        $(frozenContainer).insertAfter('.t-grid-content');
    }
 
    //get width of a particular element
    function getWidth(element) {
        //chrome and safari detect width differently, getting width from style
        if ($.browser.chrome || $.browser.safari || ($.browser.msie && browserVersion > 7)) {
            return eval(parseInt($(element).css("width").replace("px", "")));
        }
        else {
            return $(element).width();
        }
    }
 
    //initialise browser detection
    function initBrowsers() {
        $.browser.chrome = /chrome/.test(navigator.userAgent.toLowerCase());
        $.browser.safari = /safari/.test(navigator.userAgent.toLowerCase());
        $.browser.opera = /opera/.test(navigator.userAgent.toLowerCase());
    }
 
    //perform freeze columns implementation
    function performFreezeColumns() {
        var colOffset = 10;
        var adjacentColWidth = columnWidthArr[frozenColCount - 1];
        var scrollPos = $(frozenContainer).scrollLeft() + frozenColWidth + adjacentColWidth - colOffset;
 
        totalColWidth = frozenColWidth;
 
        for (i = frozenColCount; i <= hiddenColCount - 1; i++) {
            totalColWidth += columnWidthArr[i - 1];
 
            var showCol = (scrollPos < totalColWidth);
 
            $(tableHeaderCol).eq(i).children().toggle(showCol);
            $(tableFooterCol).eq(i).children().toggle(showCol);
            $(tableHeaderGroupCol).eq(i).toggle(showCol);
            $(tableContentGroupCol).eq(i).toggle(showCol);
	 $($(gridContentTable).find("tbody tr.t-group-footer td")[i]).toggle(showCol);
 
            if (!$.browser.msie || ($.browser.msie && browserVersion > 7)) {
                $(tableFooterGroupCol).eq(i).toggle(showCol);
                $(tableHeaderCol).eq(i).toggle(showCol);
            $(tableFooterCol).eq(i).toggle(showCol);
$($(gridContentTable).find("tbody tr td")[i]).toggle(showCol);
$($(gridContentTable).find("tbody tr.t-group-footer td")[i]).toggle(showCol);
//$(gridContentTable).find("tbody tr td[cellIndex=" + i + "]").toggle(showCol);
            }
            else if ($.browser.msie && browserVersion < 8) {
                //ie 6 and 7 hack, doesn't hide column correctly
                $(tableHeaderCol).eq(i).css("border-right", showCol ? "solid 1px" : "none");
                $(gridContentTable).find("tbody tr td[cellIndex=" + i + "]").css("border-right", showCol ? "solid 1px" : "none");
            }
        }
 
        currentScrollPos = $(frozenContainer).scrollLeft();
    }
 
    // hide filter panel when scrolling
    function hideFilterPanel() {
        var filterPanel = $(grid).find(" .t-animation-container");
 
        if ($(filterPanel).length > 0) {
            $(filterPanel).hide();
        }
    }
 
})(jQuery);
 


QuestionGrid is not showing horizontal scrolling PinmemberMansi Gupta26-Feb-14 1:06 
QuestionCode Not working in IE 8 Pinmemberraja atreya6-Dec-12 23:05 
QuestionScrolling Problem PinmemberBrian Savoie1-Jun-12 20:39 
AnswerRe: Scrolling Problem PinmemberDogEars17-Jul-12 12:53 
AnswerRe: Scrolling Problem PinmemberDogEars18-Jul-12 6:56 
QuestionProblem in paging PinmemberGanesanmani2-Mar-12 0:18 
QuestionExcellent plugin PinmemberGanesanmani9-Feb-12 20:28 
QuestionFilter not working PinmemberMember 171902129-Sep-11 22:27 
QuestionThank you! Pinmemberdeanofharvard21-Jun-11 6:44 
AnswerRe: Thank you! Pinmembertelaron12-Jul-11 22:32 
GeneralHi PinmemberNaga Sridhar Madiraju8-Jun-11 22:28 
GeneralGreat job!!! PinmemberBorismee23-May-11 23:55 
GeneralRe: Great job!!! Pinmembertelaron12-Jul-11 22:29 
GeneralRe: Great job!!! PinmemberBorismee26-Jul-11 5:08 
GeneralRe: Great job!!! PinmemberWude8-Aug-11 6:25 
GeneralRe: Great job!!! Pinmembertelaron8-Aug-11 18:55 
GeneralRe: Great job!!! Pinmembertelaron8-Aug-11 19:00 
GeneralRe: Great job!!! PinmemberWude11-Aug-11 0:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150302.1 | Last Updated 9 Aug 2011
Article Copyright 2011 by telaron
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid