Click here to Skip to main content
15,886,724 members
Articles / Web Development / HTML

How To Freeze Columns in Radgrid MVC

Rate me:
Please Sign up or sign in to vote.
4.67/5 (2 votes)
9 Aug 2011CPOL3 min read 57.8K   1K   13  
Shows you how to freeze columns in Telerik Radgrid MVC, a feature which is not currently available in the control

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.

(function($) {
    var $t = $.telerik;
    var escapeQuoteRegExp = /'/ig;
    var filterFunctions = ['startswith', 'substringof', 'endswith'];
    var fx = $t.fx.slide.defaults();
    var $selectedFilterColumn;

    $t.filtering = {};

    $t.filtering.initialize = function(grid) {
        $.extend(grid, $t.filtering.implementation);

        $('.t-grid-content', grid.element).bind('scroll', function() {
            grid.hideFilter();
        });

        $(document).click($t.delegate(grid, grid.filterDocumentClick));

        $('.t-grid-filter', grid.element)
			.click($t.delegate(grid, grid.showFilter))
			.live('mouseenter', $t.hover)
			.live('mouseleave', $t.leave);
    }

    /* Here `this` is the Grid instance*/

    $t.filtering.implementation = {
        createFilterCommands: function(html, column) {
            var filters = {};

            $.each(this.localization, function(key, value) {
                var prefix = 'filter' + column.type;
                var index = key.indexOf(prefix);
                if (index > -1)
                    filters[key.substring(index + prefix.length).toLowerCase()] = value;
            });

            html.cat('<select class="t-filter-operator">');
            $.each(filters, function(key, value) {
                html.cat('<option value="')
					.cat(key)
					.cat('">')
					.cat(value)
					.cat('</option>');
            });

            html.cat('</select>');
        },

        createTypeSpecificInput: function(html, column, fieldId, value) {
            if (column.type == 'Date') {
                html.cat('<div class="t-widget t-datepicker">')
	                .cat('<input class="t-input" id="')
	                .cat(fieldId)
	                .cat('" type="text" value="" />')
	                .cat('<label class="t-icon t-icon-calendar" for="')
	                .cat(fieldId)
	                .cat('" title="Open the calendar popup." /></div>');
            } else if (column.type == 'Boolean') {
                html.cat('<div><input type="radio" style="width:auto;display:inline" id="').cat(fieldId + value)
				    .cat('" name="').cat(fieldId)
				    .cat('" value="').cat(value).cat('" />')
				    .cat('<label style="display:inline" for="').cat(fieldId + value)
				    .cat('">is ').cat(value)
				    .cat('</label></div>');
            } else if (column.type == 'Enum') {
                html.cat('<div><select><option>')
                    .cat(this.localization.filterSelectValue)
                    .cat('</option>');
                $.each(column.values, function(key, value) {
                    html.cat('<option value="')
                        .cat(value)
                        .cat('">')
                        .cat(key)
                        .cat('</option>');
                });
                html.cat('</select></div>');
            } else if (column.type == 'Number') {
                html.cat('<div class="t-widget t-numerictextbox">')
	                .cat('<input class="t-input" name="')
	                .cat(fieldId)
	                .cat('" id="')
	                .cat(fieldId + '-input')
	                .cat('" type="text" value=""/>')
	                .cat('</div>');
            } else {
                html.cat('<input type="text" />');
            }
        },

        createFilterMenu: function(column) {
            var filterMenuHtml = new $t.stringBuilder();

            filterMenuHtml.cat('<div class="t-animation-container"><div class="t-filter-options t-group" style="display:none">')
					.cat('<button class="t-button t-state-default t-clear-button"><span class="t-icon t-clear-filter"></span>')
					.cat(this.localization.filterClear)
					.cat('</button><div class="t-filter-help-text">')
					.cat(this.localization.filterShowRows)
					.cat('</div>');

            var fieldIdPrefix = $(this.element).attr('id') + column.member;

            if (column.type == 'Boolean') {
                this.createTypeSpecificInput(filterMenuHtml, column, fieldIdPrefix, true);
                this.createTypeSpecificInput(filterMenuHtml, column, fieldIdPrefix, false);
            } else {
                this.createFilterCommands(filterMenuHtml, column);
                this.createTypeSpecificInput(filterMenuHtml, column, fieldIdPrefix + 'first');
                filterMenuHtml.cat('<div class="t-filter-help-text">')
                              .cat(this.localization.filterAnd)
                              .cat('</div>');
                this.createFilterCommands(filterMenuHtml, column);
                this.createTypeSpecificInput(filterMenuHtml, column, fieldIdPrefix + 'second');
            }

            filterMenuHtml.cat('<button class="t-button t-state-default t-filter-button"><span class="t-icon t-filter"></span>')
                          .cat(this.localization.filter)
				          .cat('</button></div></div>');

            var $filterMenu = $(filterMenuHtml.string());

            $.each(column.filters || [], function(i) {
                $filterMenu.find('.t-filter-operator:eq(' + i + ')')
                           .val(this.operator)
                           .end()
                           .find(':text:eq(' + i + '),select:not(.t-filter-operator):eq(' + i + ')')
                           .val(this.value)
                           .end()
                           .find(':radio[id$=' + this.value + ']')
                           .attr('checked', true);
            });

            return $filterMenu
                        .find('.t-datepicker')
                        .each(function() {
                            var match = /\{0(:([^\}]+))?\}/.exec(column.format);
                            $(this).tDatePicker({ format: match ? match[2] : $t.cultureInfo.shortDate });
                        })
                        .end()
                        .find('.t-numerictextbox')
                        .each(function() {
                            $(this).tTextBox({ type: 'numeric', minValue: '-100000', maxValue: '100000', groupSeparator: '' });
                        })
                        .end()
                        .appendTo(this.element);
        },

        showFilter: function(e, element) {
            e.stopPropagation();
            this.hideFilter(function() {
                return this.parentNode != element;
            });

            var $element = $(element);

            var $filterMenu = $element.data('filter');

            if (!$filterMenu) {
                // filtering menu should be created
                var column = this.columns[this.$columns().index($element.parent())];

                $filterMenu = this.createFilterMenu(column)
                        .data('column', column)
                        .click(function(e) {
                            e.stopPropagation();

                            if ($(e.target).parents('.t-datepicker').length == 0) {
                                $('.t-datepicker', this)
                                    .each(function() {
                                        $(this).data('tDatePicker').hidePopup();
                                    });
                            }
                        })
                        .find('.t-filter-button').click($t.delegate(this, this.filterClick)).end()
                        .find('.t-clear-button').click($t.delegate(this, this.clearClick)).end()
                        .find('input[type=text]').keyup($t.delegate(this, this.filterKeyUp)).end();

                $element.data('filter', $filterMenu);
            }

            // position filtering menu
            var top = 0;
            $('thead, .t-grid-header, .t-grouping-header, .t-grid-toolbar', this.element).each(function() {
                top += this.offsetHeight;
            });
            var position = { top: top };
            var parent = $element.parent()[0];
            var headerRow = $element.parent().parent();
            var width = -this.$headerWrap.scrollLeft() - 1;

            headerRow.find('.t-header').each(function() {
                width += this.offsetWidth;
                if (this == parent)
                    return false;
            });

            var left = width - element.offsetWidth;
            
            // constrain filtering menu within grid
            var outerWidth = $filterMenu.outerWidth() || $filterMenu.find('.t-group').outerWidth();

            // Nathan added offset 50 to fix out of screen filter in ie6
            //if (left + outerWidth  > headerRow.outerWidth())
            left = width - outerWidth + 1;

            if ($(this.element).hasClass('t-grid-rtl'))
                position['right'] = left + ($.browser.mozilla || $.browser.safari ? 18 : 0);
            else
                position['left'] = left;

            $filterMenu.css(position);

            $t.fx[$filterMenu.find('.t-filter-options').is(':visible') ? 'rewind' : 'play'](fx, $filterMenu.find('.t-filter-options'), { direction: 'bottom' });
        },

        hideFilter: function(filterCallback) {
            filterCallback = filterCallback || function() { return true; };

            $('.t-filter-options .t-datepicker', this.element)
                .each(function() { $(this).data('tDatePicker').hidePopup(); });

            $('.t-filter-options', this.element)
                .filter(filterCallback)
                .each(function() {
                    $t.fx.rewind(fx, $(this), { direction: 'bottom' });
                });
        },

        getColumn: function($element) {
            return $element.closest('.t-animation-container').data('column');
        },

        clearClick: function(e, element) {
            e.preventDefault();
            var $element = $(element);
            var column = this.getColumn($element);
            column.filters = null;

            $element.parent()
                    .find('input, select')
                    .val('')
                    .removeAttr('checked')
                    .removeClass('t-state-error');

            this.filter(this.filterExpr());
        },

        filterClick: function(e, element) {
            e.preventDefault();
            var $element = $(element);
            var column = this.getColumn($(element));
            $selectedFilterColumn = column; //Nathan
            column.filters = [];
            var hasErrors = false;

            $element.parent().find('input[type=text],select:not(.t-filter-operator)').each($.proxy(function(index, input) {
                var $input = $(input);
                var value = $.trim($input.val());

                if (!value) {
                    $input.removeClass('t-state-error');
                    return true;
                }

                var valid = this.isValidFilterValue(column, value);

                $input.toggleClass('t-state-error', !valid);

                if (!valid) {
                    hasErrors = true;
                    return true;
                }

                var operator = $input.prev('select').val() || $input.parent().prev('select').val();
                if (value != this.localization.filterSelectValue)
                    column.filters.push({ operator: operator, value: value });
            }, this));

            $element.parent().find('input:checked').each($.proxy(function(index, input) {
                var $input = $(input);
                var value = $(input).attr('value');
                column.filters.push({ operator: 'eq', value: value });
            }, this));

            if (!hasErrors) {
                if (column.filters.length > 0)
                    this.filter(this.filterExpr());

                this.hideFilter();
            }
        },

        filterKeyUp: function(e, element) {
            if (e.keyCode != 13)
                return;
            this.filterClick(e, element);
        },

        filterDocumentClick: function(e, element) {
            if (e.which == 3)
                return;

            this.hideFilter();
        },

        isValidFilterValue: function(column, value) {
            if (column.type == 'Number') {
                return !isNaN(value);
            }

            return true;
        },

        encodeFilterValue: function(columnIndex, value) {
            var column = this.columns[columnIndex];
            switch (column.type) {
                case 'String':
                case 'Enum':
                    return "'" + value.replace(escapeQuoteRegExp, "''") + "'";
                case 'Date':
                    var ticks;
                    if (value.indexOf('Date(') > -1) {
                        ticks = parseInt(value.replace(/^\/Date\((.*?)\)\/$/, '$1'));
                    } else {
                        ticks = Date.parse(value);
                    }
                    return "datetime'" + $t.formatString('{0:yyyy-MM-ddThh-mm-ss}', new Date(ticks)) + "'";
            }

            return value;
        },

        filterExpr: function() {
            var result = [];
            for (var columnIndex = 0, length = this.columns.length; columnIndex < length; columnIndex++) {
                var column = this.columns[columnIndex];

                if (!column.filters)
                    continue;

                for (var filterIndex = 0; filterIndex < column.filters.length; filterIndex++) {
                    var filter = column.filters[filterIndex];
                    var expr = new $t.stringBuilder();

                    //Nathan 
                    if (filter.value == "false") {
                        filter.operator = "ne"; //not equal to
                    }

                    if ($.inArray(filter.operator, filterFunctions) > -1) {
                        expr.cat(filter.operator)
                        .cat('(')
                        .cat(column.member)
                        .cat(',')
                        .cat(this.encodeFilterValue(columnIndex, filter.value))
                        .cat(')');
                    } else {
                        expr.cat(column.member)
                        .cat('~')
                        .cat(filter.operator)
                        .cat('~')
                        .cat(this.encodeFilterValue(columnIndex, filter.value));
                    }
                    result.push(expr.string());
                }
            }

            return result.join('~and~');
        },

        filter: function(filterBy) {
            this.currentPage = 1;
            this.filterBy = filterBy;

            if (!this.isAjax())
                location.href = $t.formatString(unescape(this.urlFormat),
                    this.currentPage, this.orderBy || '~', this.groupBy || '~', escape(this.filterBy) || '');
            else {
                this.$columns().each($.proxy(function(index, element) {
                    //Nathan - prevent all filters from highlight
                    $('.t-grid-filter', element).removeClass("t-active-filter");

                    if ($selectedFilterColumn.member == this.columns[$(element).index()].member) {
                        $('.t-grid-filter', element)
                           .toggleClass('t-active-filter', !!this.columns[index].filters);
                    }
                    //Nathan
                }, this));
                this.ajaxRequest();
            }
        }
    };
})(jQuery);

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions