TreeView Performance

The TreeView control creates DOM elements for each node in the itemsSource array. This sample below shows the performance of the TreeView when binding to sources of different sizes.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as wjCore from '@grapecity/wijmo'; import * as wjInput from '@grapecity/wijmo.input'; import * as wjNav from '@grapecity/wijmo.nav'; document.readyState === 'complete' ? init() : window.onload = init; function init() { // select the number levels and nodes at each level var cmbLevels = new wjInput.ComboBox('#cmbLevels', { itemsSource: [1, 2, 3], selectedValue: 2 }); var cmbNodesPerLevel = new wjInput.ComboBox('#cmbNodesPerLevel', { itemsSource: [5, 10, 20, 40], selectedValue: 5 }); // create the tree var theTree = new wjNav.TreeView('#theTree', { itemsSource: getData(), displayMemberPath: 'header', childItemsPath: 'items', }); // re-bind tree document.getElementById('bind').addEventListener('click', function (e) { var start = Date.now(); theTree.itemsSource = getData(); theTree.loadTree(); // force immediate refresh var msg = wjCore.format('Bound to <b>{cnt:no}</b> nodes in <b>{ms:n0}</b> ms.', { cnt: theTree.totalItemCount, ms: Date.now() - start }); document.getElementById('bindingMsg').innerHTML = msg; }); // get the data function getData() { var cnt = cmbNodesPerLevel.selectedValue, levels = cmbLevels.selectedValue, nodes = []; for (var i = 0; i < cnt; i++) { nodes.push(getNode(0, i, cnt, levels)); } return nodes; } function getNode(level, id, cnt, levels) { // create parent node var node = { header: 'Node ' + (level + 1) + '.' + (id + 1), }; // create child nodes if (level < levels - 1) { node.items = []; for (var i = 0; i < cnt; i++) { node.items.push(getNode(level + 1, i, cnt, levels)); } } // done return node; } } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo TreeView Architecture</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"> <div class="row"> <div class="col-xs-6"> <label for="cmbLevels"> Levels: </label> <div id="cmbLevels"></div> <br /> <label for="cmbLevels"> Nodes/Level: </label> <div id="cmbNodesPerLevel"></div> <br /> <label></label> <button class="btn btn-primary" id="bind"> Bind Tree </button> <div id="bindingMsg"></div> </div> <div class="col-xs-6"> <div id="theTree"></div> </div> </div> </div> </body> </html> /* default trees on this sample */ .wj-treeview { height: 350px; font-size: 120%; margin-top: 8px; margin-bottom: 8px; padding: 6px; background: #f0f0f0; box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); } .wj-combobox { width: 120px; } label { width: 120px; text-align: right; margin-bottom: 12px; } body { margin-bottom: 24pt; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as wjCore from '@grapecity/wijmo'; // import { Component, enableProdMode, NgModule, ViewChild } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { WjInputModule, WjComboBox } from '@grapecity/wijmo.angular2.input'; import { WjNavModule, WjTreeView } from '@grapecity/wijmo.angular2.nav'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { @ViewChild('cmbLevels') cmbLevels: WjComboBox; @ViewChild('cmbNodesPerLevel') cmbNodesPerLevel: WjComboBox; @ViewChild('theTree') theTree: WjTreeView; // levels = [1, 2, 3]; nodesPerLevel = [5, 10, 20, 40]; treeData: string[] = []; bindingMsg = ''; // constructor() { this.treeData = this.getTreeData(5, 2); } refresh() { var start = Date.now(); this.theTree.itemsSource = this.getTreeData(this.cmbNodesPerLevel.selectedValue, this.cmbLevels.selectedValue); this.theTree.loadTree(); // force immediate refresh this.bindingMsg = wjCore.format('Bound to <b>{cnt:no}</b> nodes in <b>{ms:n0}</b> ms.', { cnt: this.theTree.totalItemCount, ms: Date.now() - start }); } getTreeData(cnt: number, levels: number) { var nodes = []; for (var i = 0; i < cnt; i++) { nodes.push(this.getNode_(0, i, cnt, levels)) } return nodes; } private getNode_(level: number, id: number, cnt: number, levels: number) { // create node var node: any = { header: 'Node ' + (level + 1) + '.' + (id + 1), }; // create child nodes if (level < levels - 1) { node.items = []; for (var i = 0; i < cnt; i++) { node.items.push(this.getNode_(level + 1, i, cnt, levels)) } } return node; } } // @NgModule({ imports: [WjNavModule, WjInputModule, BrowserModule], declarations: [AppComponent], providers: [], 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 Wijmo TreeView Architecture</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"> <div class="row"> <div class="col-xs-6"> <label for="cmbLevels"> Levels: </label> <wj-combo-box #cmbLevels [itemsSource]="levels" [selectedValue]="2"> </wj-combo-box> <br /> <label for="cmbLevels"> Nodes/Level: </label> <wj-combo-box #cmbNodesPerLevel [itemsSource]="nodesPerLevel" [selectedValue]="5"> </wj-combo-box> <br /> <label></label> <button class="btn btn-primary" id="bind" (click)="refresh()"> Bind Tree </button> <div id="bindingMsg" [innerHtml]="bindingMsg"></div> </div> <div class="col-xs-6"> <wj-tree-view #theTree [itemsSource]="treeData" [displayMemberPath]="'header'" [childItemsPath]="'items'"> </wj-tree-view> </div> </div> </div> /* default trees on this sample */ .wj-treeview { height: 350px; font-size: 120%; margin-top: 8px; margin-bottom: 8px; padding: 6px; background: #f0f0f0; box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); } .wj-combobox { width: 120px; } label { width: 120px; text-align: right; margin-bottom: 12px; } body { margin-bottom: 24pt; } <template> <div class="container-fluid"> <div class="row"> <div class="col-xs-6"> <label for="cmbLevels">Levels:</label> <wj-combo-box :items-source="levels" :selected-value="level" :text-changed="s => level = s.selectedValue" ></wj-combo-box> <br> <label for="cmbLevels">Nodes/Level:</label> <wj-combo-box :items-source="nodesPerLevel" :selected-value="nodes" :text-changed="s => nodes = s.selectedValue" ></wj-combo-box> <br /> <button class="btn btn-primary" id="bind" @click="refresh"> Bind Tree </button> <div id="bindingMsg" v-html="bindingMsg"></div> </div> <div class="col-xs-6"> <wj-tree-view :display-member-path="'header'" :child-items-path="'items'" :items-source="treeData" :initialized="s => treeView = s" ></wj-tree-view> </div> </div> </div> </template> <script> import "bootstrap.css"; import "@grapecity/wijmo.styles/wijmo.css"; import Vue from "vue"; import "@grapecity/wijmo.vue2.input"; import "@grapecity/wijmo.vue2.nav"; import { format } from "@grapecity/wijmo"; let App = Vue.extend({ name: "app", data: function() { return { levels: [1, 2, 3], level: 2, nodesPerLevel: [5, 10, 20, 40], nodes: 5, treeData: this.getData(5, 2), bindingMsg: null, treeView: null }; }, methods: { // get an array with hierarchical data for the tree getData: function(cnt, levels) { var nodes = []; for (var i = 0; i < cnt; i++) { nodes.push(this.getNode(0, i, cnt, levels)) } return nodes; }, getNode: function(level, id, cnt, levels) { // create parent node var node = { header: 'Node ' + (level + 1) + '.' + (id + 1), }; // create child nodes if (level < levels - 1) { node.items = []; for (var i = 0; i < cnt; i++) { node.items.push(this.getNode(level + 1, i, cnt, levels)) } } // done return node; }, // bind the tree to a new data source and measure how long it took refresh: function() { var start = Date.now(); this.treeView.itemsSource = this.getData(this.nodes, this.level); this.treeView.loadTree(); // force immediate refresh this.bindingMsg = format('Bound to <b>{cnt:no}</b> nodes in <b>{ms:n0}</b> ms.', { cnt: this.treeView.totalItemCount, ms: Date.now() - start }); } } }); new Vue({ render: h => h(App) }).$mount("#app"); </script> <style> .container-fluid .wj-treeview { height: 350px; font-size: 120%; margin-top: 8px; margin-bottom: 8px; padding: 6px; background: #f0f0f0; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); } .container-fluid .wj-combobox { width: 120px; margin: 5px; } label { width: 120px; text-align: right; margin-bottom: 12px; } body { margin-bottom: 24pt; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo TreeView Architecture</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 './app.css'; import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { TreeView } from '@grapecity/wijmo.react.nav'; import { ComboBox } from '@grapecity/wijmo.react.input'; import { Globalize } from '@grapecity/wijmo'; class App extends React.Component { constructor(props) { super(props); this.state = { levels: [1, 2, 3], nodesPerLevel: [5, 10, 20, 40], selectedLevel: 2, selectedNodesPerLevel: 5, nodeCount: null, bindingTime: null }; } render() { return (<div className="container-fluid"> <div className="row"> <div className="col-xs-6"> <label htmlFor="cmbLevels">Levels:</label> <ComboBox itemsSource={this.state.levels} selectedValue={this.state.selectedLevel} selectedIndexChanged={s => { this.setState({ selectedLevel: s.selectedValue }); }}/> <br /> <label htmlFor="cmbLevels">Nodes/Level:</label> <ComboBox itemsSource={this.state.nodesPerLevel} selectedValue={this.state.selectedNodesPerLevel} selectedIndexChanged={s => { this.setState({ selectedNodesPerLevel: s.selectedValue }); }}/> <br /> <button className="btn btn-primary" onClick={() => this.refresh(true)}> Bind Tree </button> <div style={this.state.showBindingMsg ? {} : { display: 'none' }}> Bound to <b>{this.state.nodeCount}</b> items in <b>{this.state.bindingTime}</b> ms. </div> </div> <div className="col-xs-6"> <TreeView displayMemberPath="header" childItemsPath="items" initialized={s => { this._treeView = s; this.refresh(false); }}/> </div> </div> </div>); } refresh(show) { let start = Date.now(); let tree = this._treeView; tree.itemsSource = this.getTreeData(this.state.selectedNodesPerLevel, this.state.selectedLevel); tree.loadTree(); // force immediate refresh this.setState({ showBindingMsg: show, nodeCount: Globalize.format(tree.totalItemCount, 'n0'), bindingTime: Globalize.format(Date.now() - start, 'n0'), }); } getTreeData(cnt, levels) { var nodes = []; for (var i = 0; i < cnt; i++) { nodes.push(this.getNode(0, i, cnt, levels)); } return nodes; } getNode(level, id, cnt, levels) { // create node let node = { items: [], header: "Node " + (level + 1) + "." + (id + 1) }; // create child nodes if (level < levels - 1) { node.items = []; for (var i = 0; i < cnt; i++) { node.items.push(this.getNode(level + 1, i, cnt, levels)); } } return node; } } 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>AutoComplete</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> .container-fluid .wj-treeview { height: 350px; font-size: 120%; margin-top: 8px; margin-bottom: 8px; padding: 6px; background: #f0f0f0; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); } .container-fluid .wj-combobox { width: 120px; margin: 5px; } label { width: 120px; text-align: right; margin-bottom: 12px; } body { margin-bottom: 24pt; }