Example T947741
Visible to All Users

DataGrid for DevExtreme - How to implement a custom column chooser

This example demonstrates how to implement a custom column chooser for a DataGrid or TreeList.

Custom column chooser

The DataGrid and TreeList include a built-in column chooser that allows users to alter column visibility at runtime. If you need a more flexible solution, you can use the Popup and List components to implement a custom column chooser.

In this example, the custom column chooser includes a Select All checkbox that can be used to select/deselect all columns at once. All alterations in column visibility are applied when you click the button.

Files to Review

Documentation

More Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

jQuery/index.js
JavaScript
$(function(){ $("#gridContainer").dxDataGrid({ dataSource: customers, columns: ["CompanyName", "City", "State", "Phone", "Fax"], showBorders: true, onToolbarPreparing: function(e) { var customColumnChooser = createColumnChooser(e.component); e.toolbarOptions.items.push({ widget: "dxButton", options: { icon: "columnchooser", onClick: function(){ customColumnChooser.show(); }}, location: "after" }); } }); }); function createColumnChooser(grid) { var columns = grid.option("columns").map(function(column) { return { dataField: column, caption: captionize(column) }; }); // Create List var list = $("<div>").dxList({ dataSource: columns, selectedItems: columns, displayExpr: "caption", searchExpr: "caption", searchEnabled: true, showSelectionControls: true, selectionMode: "all" }).dxList("instance"); // Create Popup var popup = $("#customColumnChooser").dxPopup({ title: "Column Chooser", container: grid.element(), contentTemplate: function() { return list.element(); }, width: 250, height: 350, resizeEnabled: true, shading: false, position: { at: "right bottom", my: "right bottom", }, toolbarItems: [{ widget: "dxButton", location: "after", toolbar: "bottom", options: { text: "Apply", onClick: function(e) { var selectedItems = list.option("selectedItems"); grid.beginUpdate(); columns.forEach(function(column) { grid.columnOption(column.dataField, "visible", selectedItems.includes(column)); }); grid.endUpdate(); popup.hide(); } } }, { widget: "dxButton", location: "after", toolbar: "bottom", options: { text: "Cancel", onClick: function(e) { popup.hide(); } } }] }).dxPopup("instance"); // Sync selected items grid.on("optionChanged", function(e) { if (e.name == "columns" && e.fullName.includes("visible")) { var columnIndex = parseInt(e.fullName.charAt(8)); if (e.value) { list.selectItem(columnIndex); } else { list.unselectItem(columnIndex); } } }); return popup; } const DIGIT_CHARS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; function captionize(name) { const captionList = []; let i; let char; let isPrevCharNewWord = false; let isNewWord = false; for(i = 0; i < name.length; i++) { char = name.charAt(i); isNewWord = (char === char.toUpperCase() && char !== '-' && char !== ')' && char !== '/') || (char in DIGIT_CHARS); if(char === '_' || char === '.') { char = ' '; isNewWord = true; } else if(i === 0) { char = char.toUpperCase(); isNewWord = true; } else if(!isPrevCharNewWord && isNewWord) { if(captionList.length > 0) { captionList.push(' '); } } captionList.push(char); isPrevCharNewWord = isNewWord; } return captionList.join(''); };
Angular/src/app/app.component.html
HTML
<dx-data-grid id="gridContainer" [dataSource]="customers" [showBorders]="true" (onOptionChanged)="onOptionChanged($event)" (onToolbarPreparing)="onToolbarPreparing($event)"> <dxi-column *ngFor="let col of columns" [visibleIndex]="col.index" [dataField]="col.dataField" [(visible)]="col.visible"> </dxi-column> </dx-data-grid> <app-column-chooser [(visible)]="isColumnChooserVisible" [container]="'#gridContainer'" [columns]="columns"> </app-column-chooser>
Angular/src/app/app.component.ts
TypeScript
import { Component, ViewChild } from "@angular/core"; import { Customer, Column, Service } from "./app.service"; import { ColumnChooserComponent } from "./column.chooser/column.chooser.component"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], providers: [Service] }) export class AppComponent { customers: Customer[]; columns: Column[]; isColumnChooserVisible = false; @ViewChild(ColumnChooserComponent) columnChooser: ColumnChooserComponent; constructor(service: Service) { this.customers = service.getCustomers(); this.columns = service.getColumns(); } onToolbarPreparing(e) { e.toolbarOptions.items.push({ widget: "dxButton", options: { icon: "columnchooser", onClick: () => { this.isColumnChooserVisible = true; } }, location: "after" }); } onOptionChanged(e) { let listSelectedItems = this.columnChooser.selectedItems; if (e.name == "columns" && e.fullName.includes("visible")) { var columnIndex = parseInt(e.fullName.charAt(8)); if (e.value) { listSelectedItems.push(columnIndex); } else { let index = listSelectedItems.indexOf(columnIndex); if (index > -1) listSelectedItems.splice(index, 1); } } } }
Vue/src/App.vue
Code
<template> <div> <DxDataGrid :element-attr="dataGridAttributes" :data-source="dataSource" key-expr="ID" :columns="columns" :show-borders="true" @toolbar-preparing="onToolbarPreparing" > <DxColumn v-for="column in columns" :key="column.dataField" :data-field="column.dataField" :visible="column.visible" /> </DxDataGrid> <CustomColumnChooser container="#grid" button="#myColumnChooser" :visible="visible" @hiding="onHiding" :columns="columns" @apply="onApply" /> </div> </template> <script> import DxDataGrid, { DxColumn } from "devextreme-vue/data-grid"; import CustomColumnChooser from "./components/CustomColumnChooser.vue"; import { customers } from "./data.js"; export default { name: 'App', components: { DxDataGrid, DxColumn, CustomColumnChooser, }, data() { return { dataSource: customers, columns: [ { dataField: "CompanyName", visible: true, }, { dataField: "City", visible: true, }, { dataField: "State", visible: false, }, { dataField: "Phone", visible: true, }, { dataField: "Fax", visible: true, }, ], visible: false, dataGridAttributes: { id: 'grid' } }; }, methods: { onToolbarPreparing(e) { e.toolbarOptions.items.push({ widget: "dxButton", location: "after", options: { icon: "columnchooser", elementAttr: { id: "myColumnChooser", }, onClick: () => { this.visible = true; }, }, }); }, onHiding() { this.visible = false; }, onApply(changes) { changes.forEach((change) => { var column = this.columns.find( (column) => column.dataField === change.dataField ); column.visible = change.visible; }); this.visible = false; }, } } </script> <style> </style>
React/src/App.js
JavaScript
import 'devextreme/dist/css/dx.common.css'; import 'devextreme/dist/css/dx.light.css'; import React, { useCallback, useState, useReducer } from "react"; import DataGrid, { Column } from "devextreme-react/data-grid"; import { customers } from "./data.js"; import CustomColumnChooser from "./CustomColumnChooser"; const initialState = [ { dataField: "CompanyName", visible: true }, { dataField: "City", visible: true }, { dataField: "State", visible: true }, { dataField: "Phone", visible: true }, { dataField: "Fax", visible: true } ]; function reducer(state, changes) { if (Object.keys(changes).length === 0) return state; var newState = [...state]; changes.forEach((change) => { var column = newState.find((c) => c.dataField === change.dataField); Object.keys(change).forEach((key) => { column[key] = change[key]; }); }); return newState; } export default function App() { const [columns, setColumnsState] = useReducer(reducer, initialState); const [visible, setVisible] = useState(false); const onToolbarPreparing = useCallback((e) => { e.toolbarOptions.items.push({ widget: "dxButton", location: "after", options: { icon: "columnchooser", elementAttr: { id: "myColumnChooser" }, onClick: () => setVisible(true) } }); }, [setVisible]); const onHiding = useCallback(() => { setVisible(false); }, [setVisible]); const onApply = useCallback((changes) => { setColumnsState(changes); setVisible(false); }, [setColumnsState, setVisible]); return ( <React.Fragment> <DataGrid id="grid" dataSource={customers} showBorders={true} onToolbarPreparing={onToolbarPreparing} > {columns.map((column) => { return ( <Column key={column.dataField} dataField={column.dataField} visible={column.visible} /> ); })} </DataGrid> <CustomColumnChooser container="#grid" button="#myColumnChooser" visible={visible} onHiding={onHiding} columns={columns} onApply={onApply} /> </React.Fragment> ); }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.