ListBox/ComboBox Grouping

Grouping is managed by the CollectionView class, but to enable the display of groups in Input contorls, like the ComboBox or ListBox, the control's showGroups property must be set to true. This will add group headers tot he dropdown. See sample below for example.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as input from '@grapecity/wijmo.input'; import * as wijmo from '@grapecity/wijmo'; import { getCountryCode, getData } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // get grouped CollectionView let data = getData(); // // toggle showGroups let showGroups = true; document.getElementById('showGroups').addEventListener('click', e => { showGroups = e.target.checked; theComboBox.showGroups = showGroups; theListBox.showGroups = showGroups; }); // // toggle formatItem let formatGroupHeaders = true; document.getElementById('formatGroupHeaders').addEventListener('click', e => { formatGroupHeaders = e.target.checked; data.refresh(); }); // // toggle checkboxes document.getElementById('showCheckboxes').addEventListener('click', e => { theListBox.checkedMemberPath = e.target.checked ? 'checked' : ''; }); // // show in ComboBox let theComboBox = new input.ComboBox('#theComboBox', { showGroups: true, formatItem: formatGroupHeader, displayMemberPath: 'city', itemsSource: data }); // // show in ListBox let theListBox = new input.ListBox('#theListBox', { showGroups: true, formatItem: formatGroupHeader, displayMemberPath: 'city', itemsSource: data }); // // show a flag in the group headers function formatGroupHeader(sender, e) { if (formatGroupHeaders && e.index < 0 && e.data instanceof wijmo.CollectionViewGroup) { let group = e.data, code = getCountryCode(group.name); // if (code) { e.item.innerHTML = `<span class="flag-icon flag-icon-${code}"></span> ${group.name}`; } } } } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity ListBox/ComboBox Grouping</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <label> Show Groups <input id="showGroups" type="checkbox" checked> </label> <label> Format Group Headers <input id="formatGroupHeaders" type="checkbox" checked> </label> <label> Show Checkboxes <input id="showCheckboxes" type="checkbox"> </label> <div class="row"> <div class="col-xs-6"> <h4> ComboBox </h4> <div id="theComboBox"></div> </div> <div class="col-xs-6"> <h4> ListBox </h4> <div id="theListBox"></div> </div> </div> </div> </body> </html> import * as wijmo from '@grapecity/wijmo'; // some data for the list/combo export function getData() { var arr = []; // addCities(arr, 'US', ['New York', 'Los Angeles', 'Chicago', 'Houston']); addCities(arr, 'Japan', ['Tokyo', 'Osaka', 'Kyoto', 'Sendai']); addCities(arr, 'UK', ['London', 'Birmingham', 'Manchester', 'Liverpool']); addCities(arr, 'China', ['Shanghai', 'Beijing', 'Tianjin', 'Shenzhen']); addCities(arr, 'Germany', ['Berlin', 'Hamburg', 'Munich', 'Cologne']); addCities(arr, 'France', ['Paris', 'Marseille', 'Lyon', 'Toulouse']); addCities(arr, 'Canada', ['Toronto', 'Ottawa', 'Vancouver', 'Montreal']); addCities(arr, 'Russia', ['Moscow', 'St Petersburg', 'Novosibirsk', 'Yekaterinburg']); // return new wijmo.CollectionView(arr, { sortDescriptions: ['country', 'city'], groupDescriptions: ['country'], currentItem: null }); } // export function getCountryCode(country) { switch (country) { case 'US': return 'us'; case 'Japan': return 'jp'; case 'UK': return 'gb'; case 'China': return 'cn'; case 'Germany': return 'de'; case 'France': return 'fr'; case 'Canada': return 'ca'; case 'Russia': return 'ru'; } // return null; } // function addCities(arr, country, cities) { cities.forEach(city => { arr.push({ country: country, city: city }); }); } @import 'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/css/flag-icon.css'; .wj-listbox { height: 200px; display: block; } .wj-listbox .wj-listbox-item { line-height: 2em; } .wj-listbox .wj-listbox-item.wj-header { opacity: 1; position: sticky; top: 0; } label { margin: 0 24px; } body { margin-bottom: 40px; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wijmo from '@grapecity/wijmo'; import * as input from '@grapecity/wijmo.input'; // import { Component, enableProdMode, NgModule, ViewChild, Inject } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { WjInputModule } from '@grapecity/wijmo.angular2.input'; import { DataService } from './app.data'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { private _formatGroupHeaders = true; private _showCheckboxes = false; // @ViewChild('theListBox') theListBox: input.ListBox; data: wijmo.CollectionView; showGroups = true; // constructor(@Inject(DataService) private dataService: DataService) { this.data = dataService.getData(); } // get formatGroupHeaders() { return this._formatGroupHeaders; } set formatGroupHeaders(value: boolean) { if (this._formatGroupHeaders !== value) { this._formatGroupHeaders = value; this.data.refresh(); } } // get showCheckboxes() { return this._showCheckboxes; } set showCheckboxes(value: boolean) { if (this._showCheckboxes !== value) { this._showCheckboxes = value; this.theListBox.checkedMemberPath = value ? 'checked' : ''; } } // formatGroupHeader(e: input.FormatItemEventArgs) { if (this.formatGroupHeaders && e.index < 0 && e.data instanceof wijmo.CollectionViewGroup) { let group = e.data, code = this.dataService.getCountryCode(group.name); // if (code) { e.item.innerHTML = `<span class="flag-icon flag-icon-${code}"></span> ${group.name}`; } } } } // @NgModule({ imports: [WjInputModule, BrowserModule, FormsModule], declarations: [AppComponent], providers: [DataService], bootstrap: [AppComponent] }) export class AppModule { } // enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity ListBox/ComboBox Grouping</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html> <div class="container-fluid"> <label> Show Groups <input id="showGroups" type="checkbox" [(ngModel)]="showGroups"> </label> <label> Format Group Headers <input id="formatItem" type="checkbox" [(ngModel)]="formatGroupHeaders"> </label> <label> Show Checkboxes <input id="checkBoxes" type="checkbox" [(ngModel)]="showCheckboxes"> </label> <div class="row"> <div class="col-xs-6"> <h4> ComboBox </h4> <wj-combo-box [showGroups]="showGroups" [displayMemberPath]="'city'" [itemsSource]="data" (formatItem)="formatGroupHeader($event)"> </wj-combo-box> </div> <div class="col-xs-6"> <h4> ListBox </h4> <wj-list-box #theListBox [showGroups]="showGroups" [displayMemberPath]="'city'" [itemsSource]="data" (formatItem)="formatGroupHeader($event)"></wj-list-box> </div> </div> </div> import { CollectionView } from '@grapecity/wijmo'; import { Injectable } from '@angular/core'; // @Injectable() export class DataService { // some data for the list/combo getData() { let arr: any[] = []; // this._addCities(arr, 'US', ['New York', 'Los Angeles', 'Chicago', 'Houston']); this._addCities(arr, 'Japan', ['Tokyo', 'Osaka', 'Kyoto', 'Sendai']); this._addCities(arr, 'UK', ['London', 'Birmingham', 'Manchester', 'Liverpool']); this._addCities(arr, 'China', ['Shanghai', 'Beijing', 'Tianjin', 'Shenzhen']); this._addCities(arr, 'Germany', ['Berlin', 'Hamburg', 'Munich', 'Cologne']); this._addCities(arr, 'France', ['Paris', 'Marseille', 'Lyon', 'Toulouse']); this._addCities(arr, 'Canada', ['Toronto', 'Ottawa', 'Vancouver', 'Montreal']); this._addCities(arr, 'Russia', ['Moscow', 'St Petersburg', 'Novosibirsk', 'Yekaterinburg']); // return new CollectionView(arr, { sortDescriptions: ['country', 'city'], groupDescriptions: ['country'], currentItem: null }); } // getCountryCode(country: string) { switch (country) { case 'US': return 'us'; case 'Japan': return 'jp'; case 'UK': return 'gb'; case 'China': return 'cn'; case 'Germany': return 'de'; case 'France': return 'fr'; case 'Canada': return 'ca'; case 'Russia': return 'ru'; } // return null; } // private _addCities(arr: any[], country: string, cities: string[]) { cities.forEach(city => { arr.push({ country: country, city: city }); }); } } @import 'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/css/flag-icon.css'; .wj-listbox { height: 200px; display: block; } .wj-listbox .wj-listbox-item { line-height: 2em; } .wj-listbox .wj-listbox-item.wj-header { opacity: 1; position: sticky; top: 0; } label { margin: 0 24px; } body { margin-bottom: 40px; } <template> <div class="container-fluid"> <label> Show Groups <input id="showGroups" type="checkbox" v-model="showGroups"> </label> <label> Format Group Headers <input id="formatItem" type="checkbox" v-model="formatGroupHeaders" @change="onFormatItemChanged"> </label> <label> Show Checkboxes <input id="checkBoxes" type="checkbox" v-model="showCheckboxes"> </label> <div class="row"> <div class="col-xs-6"> <h4> ComboBox </h4> <wj-combo-box :showGroups="showGroups" :formatItem="formatGroupHeader" displayMemberPath="city" :itemsSource="data" :initialized="initCombo"> </wj-combo-box> </div> <div class="col-xs-6"> <h4> ListBox </h4> <wj-list-box :showGroups="showGroups" :formatItem="formatGroupHeader" :checkedMemberPath="showCheckboxes ? 'checked' : ''" displayMemberPath="city" :itemsSource="data" :initialized="initList"> </wj-list-box> </div> </div> </div> </template> <script> import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import Vue from 'vue'; import '@grapecity/wijmo.vue2.input'; import { getData, getCountryCode } from './data'; let App = Vue.extend({ name: 'app', data: function () { return { data: getData(), showGroups: true, showCheckboxes: false, formatGroupHeaders: true, theCombo: null, theList: null } }, methods: { initCombo: function(combo){ this.theCombo = combo; }, initList: function(list){ this.theList = list; }, formatGroupHeader: function(sender, e) { if (this.formatGroupHeaders && e.index < 0 && e.data instanceof wijmo.CollectionViewGroup) { let group = e.data, code = getCountryCode(group.name); // if (code) { e.item.innerHTML = `<span class="flag-icon flag-icon-${code}"></span> ${group.name}`; } } }, onFormatItemChanged: function() { this.data.refresh(); } }, mounted() { // To make the formatItem event work this.theCombo.invalidate(); this.theList.invalidate(); } }) let vm = new Vue({ render: h => h(App) }).$mount('#app'); </script> <style> @import 'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/css/flag-icon.css'; .container-fluid .wj-listbox { height: 200px; display: block; } .container-fluid .wj-listbox .wj-listbox-item { line-height: 2em; } .container-fluid .wj-listbox .wj-listbox-item.wj-header { opacity: 1; position: sticky; top: 0; } label { margin: 0 24px; } body { margin-bottom: 40px; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity ListBox/ComboBox Grouping</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app.vue'); </script> </head> <body> <div id="app"></div> </body> </html> import * as wijmo from '@grapecity/wijmo'; // some data for the list/combo export function getData() { var arr = []; // addCities(arr, 'US', ['New York', 'Los Angeles', 'Chicago', 'Houston']); addCities(arr, 'Japan', ['Tokyo', 'Osaka', 'Kyoto', 'Sendai']); addCities(arr, 'UK', ['London', 'Birmingham', 'Manchester', 'Liverpool']); addCities(arr, 'China', ['Shanghai', 'Beijing', 'Tianjin', 'Shenzhen']); addCities(arr, 'Germany', ['Berlin', 'Hamburg', 'Munich', 'Cologne']); addCities(arr, 'France', ['Paris', 'Marseille', 'Lyon', 'Toulouse']); addCities(arr, 'Canada', ['Toronto', 'Ottawa', 'Vancouver', 'Montreal']); addCities(arr, 'Russia', ['Moscow', 'St Petersburg', 'Novosibirsk', 'Yekaterinburg']); // return new wijmo.CollectionView(arr, { sortDescriptions: ['country', 'city'], groupDescriptions: ['country'], currentItem: null }); } // export function getCountryCode(country) { switch (country) { case 'US': return 'us'; case 'Japan': return 'jp'; case 'UK': return 'gb'; case 'China': return 'cn'; case 'Germany': return 'de'; case 'France': return 'fr'; case 'Canada': return 'ca'; case 'Russia': return 'ru'; } // return null; } // function addCities(arr, country, cities) { cities.forEach(city => { arr.push({ country: country, city: city }); }); } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; // import * as wijmo from '@grapecity/wijmo'; import * as wjInput from '@grapecity/wijmo.react.input'; import { getData, getCountryCode } from './data'; // class App extends React.Component { constructor(props) { super(props); this.formatGroupHeader = (sender, e) => { if (this.state.formatGroupHeaders && e.index < 0 && e.data instanceof wijmo.CollectionViewGroup) { let group = e.data, code = getCountryCode(group.name); // if (code) { e.item.innerHTML = `<span class="flag-icon flag-icon-${code}"></span> ${group.name}`; } } }; this.updateFormatGroupHeaderStatus = (e) => { this.setState({ formatGroupHeaders: e.target.checked }, () => { this.state.data.refresh(); }); }; this.updateShowGroupsStatus = (e) => { this.setState({ showGroups: e.target.checked }); }; this.updateShowCheckboxesStatus = (e) => { this.setState({ showCheckBoxes: e.target.checked }); }; this.state = { data: getData(), formatGroupHeaders: true, showGroups: true, showCheckBoxes: false }; } render() { return <div className="container-fluid"> <label> Show Groups <input id="showGroups" type="checkbox" onChange={this.updateShowGroupsStatus.bind(this)} defaultChecked={this.state.showGroups}/> </label> <label> Format Group Headers <input id="formatItem" type="checkbox" onChange={this.updateFormatGroupHeaderStatus.bind(this)} defaultChecked={this.state.formatGroupHeaders}/> </label> <label> Show Checkboxes <input id="checkBoxes" type="checkbox" onChange={this.updateShowCheckboxesStatus.bind(this)} defaultChecked={this.state.showCheckBoxes}/> </label> <div className="row"> <div className="col-xs-6"> <h4>ComboBox</h4> <wjInput.ComboBox showGroups={this.state.showGroups} formatItem={this.formatGroupHeader} displayMemberPath="city" itemsSource={this.state.data}> </wjInput.ComboBox> </div> <div className="col-xs-6"> <h4>ListBox</h4> <wjInput.ListBox showGroups={this.state.showGroups} formatItem={this.formatGroupHeader} displayMemberPath="city" checkedMemberPath={this.state.showCheckBoxes ? 'checked' : ''} itemsSource={this.state.data}> </wjInput.ListBox> </div> </div> </div>; } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity ListBox/ComboBox Grouping</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html> @import 'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/css/flag-icon.css'; .wj-listbox { height: 200px; display: block; } .wj-listbox .wj-listbox-item { line-height: 2em; } .wj-listbox .wj-listbox-item.wj-header { opacity: 1; position: sticky; top: 0; } label { margin: 0 24px; } body { margin-bottom: 40px; } import { CollectionView } from '@grapecity/wijmo'; export function getData() { let arr = []; // _addCities(arr, 'US', ['New York', 'Los Angeles', 'Chicago', 'Houston']); _addCities(arr, 'Japan', ['Tokyo', 'Osaka', 'Kyoto', 'Sendai']); _addCities(arr, 'UK', ['London', 'Birmingham', 'Manchester', 'Liverpool']); _addCities(arr, 'China', ['Shanghai', 'Beijing', 'Tianjin', 'Shenzhen']); _addCities(arr, 'Germany', ['Berlin', 'Hamburg', 'Munich', 'Cologne']); _addCities(arr, 'France', ['Paris', 'Marseille', 'Lyon', 'Toulouse']); _addCities(arr, 'Canada', ['Toronto', 'Ottawa', 'Vancouver', 'Montreal']); _addCities(arr, 'Russia', ['Moscow', 'St Petersburg', 'Novosibirsk', 'Yekaterinburg']); // return new CollectionView(arr, { sortDescriptions: ['country', 'city'], groupDescriptions: ['country'], currentItem: null }); } // export function getCountryCode(country) { switch (country) { case 'US': return 'us'; case 'Japan': return 'jp'; case 'UK': return 'gb'; case 'China': return 'cn'; case 'Germany': return 'de'; case 'France': return 'fr'; case 'Canada': return 'ca'; case 'Russia': return 'ru'; } // return null; } function _addCities(arr, country, cities) { cities.forEach(city => { arr.push({ country: country, city: city }); }); }