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; var nodes = []; for (var i = 0; i < cnt; i++) { nodes.push(getNode(0, i, cnt, levels)); } return nodes; } function getNode(level, id, cnt, levels) { // create 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 ref="cmbLevels" :items-source="levels" :selected-value="2" :initialized="initLevelsCmb" ></wj-combo-box> <br> <label for="cmbLevels">Nodes/Level:</label> <wj-combo-box ref="cmbNodesPerLevel" :items-source="nodesPerLevel" :selected-value="5" :initialized="initPreLevelsCmb" ></wj-combo-box> <br> <label></label> <button class="btn btn-primary" id="bind" v-on:click="refresh">Bind Tree</button> <div id="bindingMsg" v-html="bindingMsg"></div> </div> <div class="col-xs-6"> <wj-tree-view ref="theTree" :items-source="treeData" display-member-path="header" child-items-path="items" :initialized="initTreeView" ></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 * as wjCore from "@grapecity/wijmo"; let App = Vue.extend({ name: "app", data: function() { return { levels: [1, 2, 3], nodesPerLevel: [5, 10, 20, 40], treeData: getTreeData(5, 2), bindingMsg: null }; }, methods: { initLevelsCmb: function(s) { this.levelCmb = s; }, initPreLevelsCmb: function(s) { this.preLevelCmb = s; }, initTreeView: function(s) { this.treeView = s; }, refresh: function() { var start = Date.now(); this.treeData = getTreeData( this.preLevelCmb.selectedValue, this.levelCmb.selectedValue ); this.bindingMsg = wjCore.format( "Bound to <b>{cnt:no}</b> nodes in <b>{ms:n0}</b> ms.", { cnt: this.treeView.totalItemCount, ms: Date.now() - start } ); } } }); function getTreeData(cnt, levels) { var nodes = []; for (var i = 0; i < cnt; i++) { nodes.push(getNode_(0, i, cnt, levels)); } return nodes; } function getNode_(level, id, cnt, levels) { // create 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)); } } return node; } 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 * as wjNav from '@grapecity/wijmo.react.nav'; import * as wjInput from '@grapecity/wijmo.react.input'; import * as wjCore from '@grapecity/wijmo'; class App extends React.Component { constructor(props) { super(props); this.state = { levels: [1, 2, 3], selectedLevel: 2, nodesPerLevel: [5, 10, 20, 40], selectedNodesPerLevel: 5, treeData: this.getTreeData(5, 2), bindingMsg: null, }; } initLevelsCmb(s) { this._levelCmb = s; } initPreLevelsCmb(s) { this._preLevelCmb = s; } initTreeView(s) { this._treeView = s; } refresh() { var start = Date.now(); this.setState({ treeData: this.getTreeData(this._preLevelCmb.selectedValue, this._levelCmb.selectedValue), bindingMsg: (<p> Bound to <b>{wjCore.format("{Count:no}", { Count: this._treeView.totalItemCount })}</b> nodes in <b>{Date.now() - start}</b> ms. </p>) }); } 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; } setSelectedNodesPerLevel(e) { this.setState({ selectedNodesPerLevel: e.text }); } setSelectedLevel(e) { this.setState({ selectedLevel: e.text }); } render() { return (<div className="container-fluid"> <div className="row"> <div className="col-xs-6"> <label htmlFor="cmbLevels">Levels:</label> <wjInput.ComboBox ref="cmbLevels" itemsSource={this.state.levels} selectedValue={this.state.selectedLevel} initialized={this.initLevelsCmb.bind(this)} selectedIndexChanged={this.setSelectedLevel.bind(this)}></wjInput.ComboBox> <br /> <label htmlFor="cmbLevels">Nodes/Level:</label> <wjInput.ComboBox ref="cmbNodesPerLevel" itemsSource={this.state.nodesPerLevel} selectedValue={this.state.selectedNodesPerLevel} initialized={this.initPreLevelsCmb.bind(this)} selectedIndexChanged={this.setSelectedNodesPerLevel.bind(this)}></wjInput.ComboBox> <br /> <label></label> <button className="btn btn-primary" id="bind" onClick={this.refresh.bind(this)}>Bind Tree</button> <div id="bindingMsg">{this.state.bindingMsg}</div> </div> <div className="col-xs-6"> <wjNav.TreeView ref="theTree" itemsSource={this.state.treeData} displayMemberPath="header" childItemsPath="items" initialized={this.initTreeView.bind(this)}></wjNav.TreeView> </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>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; }