Row and Column Freezing

The MultiRow control allows you to freeze rows and columns so they remain in view as the user scrolls the grid. You can set the number of frozen rows and columns through the frozenColumns and frozenRows properties.

Frozen cells can be edited and selected as regular cells, exactly as in Excel and in the FlexGrid control.

This example allows you to toggle whether the first group of rows and columns should be frozen.

Learn about MultiRow | MultiRow API Reference

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import './styles.css'; import * as wjMultiRow from '@mescius/wijmo.grid.multirow'; import { generateAppData } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { let appData = generateAppData(); let freezeMultirow = new wjMultiRow.MultiRow('#freezeMultirow', { itemsSource: appData.orders, layoutDefinition: appData.ldTwoLines }); document.getElementById('btnFreeze').addEventListener('click', function (e) { freezeMultirow.frozenColumns = freezeMultirow.frozenColumns ? 0 : 2; freezeMultirow.frozenRows = freezeMultirow.frozenRows ? 0 : 2; e.target.textContent = freezeMultirow.frozenRows == 0 ? 'Freeze' : 'Unfreeze'; }); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo MultiRow Row and Column Freezing</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.21.5/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div id="freezeMultirow"></div> <br /> <button class="btn btn-default" id="btnFreeze"> Freeze </button> </div> </body> </html>
import * as wjCore from '@mescius/wijmo'; import * as wjGrid from '@mescius/wijmo.grid'; export function generateAppData() { // create some data let appData = {}, customers = [], firstNames = 'Aaron,Paul,John,Mark,Sue,Tom,Bill,Joe,Tony,Brad,Frank,Chris,Pat'.split(','), lastNames = 'Smith,Johnson,Richards,Bannon,Wong,Peters,White,Brown,Adams,Jennings'.split(','), cities = 'York,Paris,Rome,Cairo,Florence,Sidney,Hamburg,Vancouver'.split(','), states = 'SP,RS,RN,SC,CS,RT,BC'.split(','); for (let i = 0; i < 50; i++) { let first = firstNames[randBetween(0, firstNames.length - 1)], last = lastNames[randBetween(0, lastNames.length - 1)]; customers.push({ id: i, name: first + ' ' + last, address: randBetween(100, 10000) + ' ' + lastNames[randBetween(0, lastNames.length - 1)] + ' St.', city: cities[randBetween(0, cities.length - 1)], state: states[randBetween(0, states.length - 1)], zip: wjCore.format('{p1:d5}-{p2:d3}', { p1: randBetween(10000, 99999), p2: randBetween(100, 999) }), email: first + '.' + last + '@gmail.com', phone: wjCore.format('{p1:d3}-{p2:d4}', { p1: randBetween(100, 999), p2: randBetween(1000, 9999) }) }); } let cityMap = new wjGrid.DataMap(cities); let shippers = [ { id: 0, name: 'Speedy Express', email: 'speedy@gmail.com', phone: '431-3234', express: true }, { id: 1, name: 'Flash Delivery', email: 'flash@gmail.com', phone: '431-6563', express: true }, { id: 2, name: 'Logitrax', email: 'logitrax@gmail.com', phone: '431-3981', express: false }, { id: 3, name: 'Acme Inc', email: 'acme@gmail.com', phone: '431-3113', express: false } ]; let orders = []; let today = new Date(); for (let i = 0; i < 20; i++) { let shipped = wjCore.DateTime.addDays(today, -randBetween(1, 3000)); orders.push({ id: i, date: wjCore.DateTime.addDays(shipped, -randBetween(1, 5)), shippedDate: shipped, amount: randBetween(10000, 500000) / 100, customer: customers[randBetween(0, customers.length - 1)], shipper: shippers[randBetween(0, shippers.length - 1)] }); } function randBetween(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } // expose orders CollectionView to the app appData.orders = new wjCore.CollectionView(orders); // expose grouped orders CollectionView to the app appData.groupedOrders = new wjCore.CollectionView(orders, { groupDescriptions: [ 'customer.city' ] }); // expose paged orders CollectionView to the app appData.pagedOrders = new wjCore.CollectionView(orders, { pageSize: 4 }); // expose addNew oders CollectionView to the app appData.addNewOrders = new wjCore.CollectionView(orders, { newItemCreator: function () { return { customer: {}, shipper: {} }; }, }); appData.addNewOrders.moveCurrentToLast(); // refresh views when data source changes let ordersRefreshing = false; appData.orders.collectionChanged.addHandler(function () { ordersRefreshing = true; if (!pagedOrdersRefreshing) { appData.pagedOrders.refresh(); } if (!groupedOrdersRefreshing) { appData.groupedOrders.refresh(); } if (!addNewOrdersRefreshing) { appData.addNewOrders.refresh(); } ordersRefreshing = false; }); // addNew orders let addNewOrdersRefreshing = false; appData.addNewOrders.collectionChanged.addHandler(function () { addNewOrdersRefreshing = true; if (!ordersRefreshing) { appData.orders.refresh(); } if (!pagedOrdersRefreshing) { appData.pagedOrders.refresh(); } if (!groupedOrdersRefreshing) { appData.groupedOrders.refresh(); } addNewOrdersRefreshing = false; }); // grouped orders let groupedOrdersRefreshing = false; appData.groupedOrders.collectionChanged.addHandler(function () { groupedOrdersRefreshing = true; if (!ordersRefreshing) { appData.orders.refresh(); } if (!pagedOrdersRefreshing) { appData.pagedOrders.refresh(); } if (!addNewOrdersRefreshing) { appData.addNewOrders.refresh(); } groupedOrdersRefreshing = false; }); // paged orders let pagedOrdersRefreshing = false; appData.pagedOrders.collectionChanged.addHandler(function () { pagedOrdersRefreshing = true; if (!ordersRefreshing) { appData.orders.refresh(); } if (!addNewOrdersRefreshing) { appData.addNewOrders.refresh(); } if (!groupedOrdersRefreshing) { appData.groupedOrders.refresh(); } pagedOrdersRefreshing = false; }); // sample layout definitions appData.ldOneLine = [ { cells: [{ binding: 'id', header: 'ID', cssClass: 'id', isReadOnly: true }] }, { cells: [{ binding: 'date', header: 'Ordered' }] }, { cells: [{ binding: 'shippedDate', header: 'Shipped' }] }, { cells: [{ binding: 'amount', header: 'Amount', format: 'c', cssClass: 'amount' }] }, { cells: [{ binding: 'customer.name', header: 'Customer' }] }, { cells: [{ binding: 'customer.address', header: 'Address', wordWrap: true }] }, { cells: [{ binding: 'customer.city', header: 'City', dataMap: cityMap }] }, { cells: [{ binding: 'customer.state', header: 'State', width: 45 }] }, { cells: [{ binding: 'customer.zip', header: 'Zip' }] }, { cells: [{ binding: 'customer.email', header: 'Customer Email', cssClass: 'email' }] }, { cells: [{ binding: 'customer.phone', header: 'Customer Phone' }] }, { cells: [{ binding: 'shipper.name', header: 'Shipper' }] }, { cells: [{ binding: 'shipper.email', header: 'Shipper Email', cssClass: 'email' }] }, { cells: [{ binding: 'shipper.phone', header: 'Shipper Phone' }] }, { cells: [{ binding: 'shipper.express', header: 'Express' }] } ]; appData.ldTwoLines = [ { header: 'Order', colspan: 2, cells: [ { binding: 'id', header: 'ID', cssClass: 'id', isReadOnly: true }, { binding: 'date', header: 'Ordered' }, { binding: 'amount', header: 'Amount', format: 'c', cssClass: 'amount' }, { binding: 'shippedDate', header: 'Shipped' } ] }, { header: 'Customer', colspan: 3, cells: [ { binding: 'customer.name', header: 'Name' }, { binding: 'customer.email', header: 'EMail', colspan: 2, cssClass: 'email' }, { binding: 'customer.address', header: 'Address' }, { binding: 'customer.city', header: 'City', dataMap: cityMap }, { binding: 'customer.state', header: 'State', width: 45 } ] }, { header: 'Shipper', cells: [ { binding: 'shipper.name', header: 'Shipper', colspan: 2 }, { binding: 'shipper.email', header: 'EMail', cssClass: 'email' }, { binding: 'shipper.express', header: 'Express' } ] } ]; appData.ldThreeLines = [ { header: 'Order', colspan: 2, cells: [ { binding: 'id', header: 'ID', colspan: 2, cssClass: 'id' }, { binding: 'amount', header: 'Amount', format: 'c', colspan: 2, cssClass: 'amount' }, { binding: 'date', header: 'Ordered' }, { binding: 'shippedDate', header: 'Shipped' } ] }, { header: 'Customer', colspan: 3, cells: [ { binding: 'customer.name', header: 'Name' }, { binding: 'customer.email', header: 'EMail', colspan: 2, cssClass: 'email' }, { binding: 'customer.address', header: 'Address', colspan: 2 }, { binding: 'customer.phone', header: 'Phone' }, { binding: 'customer.city', header: 'City', dataMap: cityMap }, { binding: 'customer.state', header: 'State', width: 45 }, { binding: 'customer.zip', header: 'Zip' }, ] }, { header: 'Shipper', cells: [ { binding: 'shipper.name', header: 'Shipper' }, { binding: 'shipper.email', header: 'EMail', cssClass: 'email' }, { binding: 'shipper.express', header: 'Express' } ] } ]; appData.layoutDefs = new wjCore.CollectionView([ { name: 'Traditional', description: 'Traditional grid view, with one row per record. The user must scroll horizontally to see the whole record.', def: appData.ldOneLine }, { name: 'Compact', description: 'This view uses two rows per record. The layout is divided into three groups: order, customer, and shipper', def: appData.ldTwoLines }, { name: 'Detailed', description: 'This view uses three rows per record. The layout is divided into three groups: order, customer, and shipper', def: appData.ldThreeLines } ]); return appData; }
.wj-multirow { height: 400px; width: 600px; margin: 6px 0; } /* custom styling for frozen cells */ .wj-flexgrid .wj-cell.wj-frozen:not(.wj-header):not(.wj-group):not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: #f3fbcc; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);