This example demonstrates how to implement a custom column chooser for a DataGrid or TreeList.
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
- jQuery
- Angular
- Vue
- React
- ASP.NET
Documentation
More Examples
- DataGrid - How to implement a custom editing form using Form and Popup
- DataGrid - Column Customization
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
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('');
};
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>
TypeScriptimport { 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);
}
}
}
}
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>
JavaScriptimport '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>
);
}