Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Multiselect Treeview

0.00/5 (No votes)
29 Sep 2017 1  
This is the implementation of Treeview with Multiselect node functionality wide requested by users.

Introduction

This article implements the Treeview with multiselect option like items inside server-side binding and searches within the tree. This is an enhancement to the previous article [https://www.codeproject.com/Tips/862784/Treeview-Dropdown-With-Search] after I received the request to enable MultiSelect functionality inside this treeview.

Background

As this is an enhancement to the previous article https://www.codeproject.com/Tips/862784/Treeview-Dropdown-With-Search, please visit the article to know the basics. Although you can still understand it without going through the previous one as it also contains sufficient information.

Having knowledge of HTML and Kendo is appreciated.

Note: For binding the dropdown with server side, having knowledge of WCF/MVC/WebMethod is a prerequisite.

Using the Code

What we will have in the end: We will have a treeview where on selecting any node/nodes, it will list in multiselect and again clicking the node, you can remove it as well.

We will create a webform with client side code only. We will need to include some 'links' and 'Scripts' to the Head part of the page so that we can have required files to run this plug in.

Head: Please include the below files to the Head of HTML

<link href="http://cdn.kendostatic.com/2013.2.716/styles/kendo.common.min.css"

rel="stylesheet" />
<link href="http://cdn.kendostatic.com/2013.2.716/styles/kendo.default.min.css"

rel="stylesheet" />
<link href="http://cdn.kendostatic.com/2013.2.716/styles/kendo.dataviz.min.css"

rel="stylesheet" />
<link href="http://cdn.kendostatic.com/2013.2.716/styles/kendo.mobile.all.min.css"

rel="stylesheet" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.2.716/js/kendo.all.min.js"></script>

The above fields are links to resource files from Telerik Kendo. You can get them by clicking on the files or you can manually download the from Telerik itself. 4th script is jquery files.

Now let us move to the Body part of the page.

BODY: Place the following HTML to body

<form id="form1" runat="server">
    <div>
        <p><label id="lblselected"></label></p>
            <!--Kendo multiselect start-->
        <div id="lblSelectTree">
            <select id="multiselect" multiple="multiple" 

            placeholder="select leagal topics">
                <option value="" disabled selected>Select your option</option>
            </select>
        </div>
        <!--Kendo multiselect end-->
        <div id="container" style="float: left; min-width: 175px; 
        background-color: rgb(198, 198, 198);">
            <span><input type="text" id="treeViewSearchInput" 

            placeholder=" -- select --" /><input id="treeviewDropdownBtn" 

            type="button" value="V" /></span>
            <div id="treeview" style="display: none;">
            </div>
        </div>
    </div>
    </form>

As you can see, here we use a 'Label' to display the selected value, a 'div' to bind the dropdown items.

We are using Telerik Kendo Multiselect to display the list of selected tree nodes.

To search, we are using an 'input' of type text (it can be of type 'search'). After this, we also used an 'input' of type button to act as Dropdown explorer button.

CSS: We have also used little CSS to make it more attractive and dropdown like the design. I've used the following CSS.

<style type="text/css" scoped>
        span.k-in > span.highlight
        {
            background: #7EA700;
            color: #ffffff;
            border: 1px solid green;
            padding: 1px;
        }
        .sele {
            font-weight:bold;
        }
    </style>

JavaScript: Now next is the big part that is the JavaScript/JQuery code which is all responsible to make the plug in work. Please go through the code carefully to understand it, although it's self-explanatory.

<script type="text/javascript">
        var selectedVlaues = [];
        var multiSelect;

        function createMultiSelect() {

            // Create kedno multiselect
            multiSelect = $("#multiselect").kendoMultiSelect({
                optionLabel: "Select Legal Topics...",
                dataTextField: "text",
                dataValueField: "value",
                readonly: true,
                disabled: true                
            }).data("kendoMultiSelect");

            // Kendo multiselect in read only mode
            multiSelect.readonly();
        }

        function InitSearch(treeViewId, searchInputId, treeviewDropdownBtn) {
            var tv = $(treeViewId).data('kendoTreeView');

            $(searchInputId).on('keyup', function () {
                $(treeViewId + ' li.k-item').show();

                $('span.k-in > span.highlight').each(function () {
                    $(this).parent().text($(this).parent().text());
                });

                // ignore if no search term
                if ($.trim($(this).val()) === '') {
                    tv.select() //gets currently selected <li> element
                        .find("span.k-state-selected")
                            .removeClass("k-state-selected"); //removes the highlight class

                    $('#lblselected').html("Selecting: --");
                    return;
                }

                var term = this.value.toUpperCase();
                var tlen = term.length;

                $(treeViewId + ' span.k-in').each(function (index) {

                    var text = $(this).text();
                    var html = '';
                    var q = 0;
                    var p;

                    while ((p = text.toUpperCase().indexOf(term, q)) >= 0) {
                        html += text.substring(q, p) + '<span class="highlight">' + 
                        text.substr(p, tlen) + '</span>';
                        q = p + tlen;
                    }

                    if (q > 0) {
                        html += text.substring(q);
                        $(this).html(html);

                        $(this).parentsUntil('.k-treeview').filter
                        ('.k-item').each(function (index, element) {
                            tv.expand($(this));
                            $(this).data('SearchTerm', term);
                        });
                    }
                });

                $(treeViewId + ' li.k-item:not(:has(".highlight"))').hide();
            });

            $(searchInputId).on('blur', function () {
                if ($('#treeViewSearchInput').val() == '') 
                {
                    //$('#treeview').hide();              
                } else {
                    $('#treeview').show();
                }
            });

             $(searchInputId).on('focus', function () {
                $('#treeview').show();$('#treeViewSearchInput').keyup();
             });

             $(treeviewDropdownBtn).on('click', function () {
                $('#treeview').toggle();
             });
             
             $('#treeview').on('show', function() {
                 SetSelectedNodes();
             });
        }
        
        (function ($) {
            $.each(['show', 'hide'], function (i, ev) {
                var el = $.fn[ev];
                $.fn[ev] = function () {
                    this.trigger(ev);
                    return el.apply(this, arguments);
                };
            });
        })(jQuery);

         $(document).ready(function () {
          
            var $tv = $("#treeview").kendoTreeView({
                //template: kendo.template($("#treeview-template").html()),
                dataSource: [
                    { id: "1-0", text: "Furniture", expanded: true, items: [
                        { id: "1-1", text: "Tables & Chairs" },
                        { id: "1-2", text: "Sofas" },
                        { id: "1-3", text: "Occasional Furniture" }
                    ] },
                    { id: "2-0", text: "Decor", items: [
                        { id: "2-1", text: "Bed Linen" },
                        { id: "2-2", text: "Curtains & Blinds" },
                        { id: "2-3", text: "Carpets" }
                    ] },
                    { id: "3-0", text: "Storage" }
                ],
                select: onSelect,
                dragAndDrop: true
            }).data("kendoTreeView")
         
            InitSearch("#treeview", "#treeViewSearchInput", "#treeviewDropdownBtn");               

            createMultiSelect();

            $('.k-multiselect-wrap').click(function (){
                $('#treeview').toggle();
            });
        });

        function onSelect(e) {
            var item = this.dataItem(e.node);
            
            $('#treeview').hide();

            var item = {text: item.text,value: item.id};

            saveMultiselectArray(item, selectedVlaues);
            // Set selected value to kendo multiselect
            multiSelect.dataSource.data(selectedVlaues);
            var values = $.map(multiSelect.dataSource.data(), function (dataItem) {
                return dataItem.value;
            });
            multiSelect.value(values);
            e.preventDefault();
        }

        function saveMultiselectArray(item, arr) {
            var found = arr.findIndex(x => x.value==item.value);
            if (found >= 0) {
                // Element was found, remove it.
                arr.splice(found, 1);
                $('#treeViewSearchInput').val('');
                $('#lblselected').html('');
                
            } else {
                // Element was not found, add it.
                arr.push(item);
                $('#treeViewSearchInput').val(item.text);
                $('#lblselected').html("Selecting: " + item.text + " & ID:" + item.value);
            }
        }
        function setTreeView() {           
            if ($('#treeViewSearchInput').val() == '') 
            {
                //$('#treeview').hide();              
            } else {
                $('#treeview').show();
            }
        }
        function SetSelectedNodes(){
            var tv = $('#treeview').data('kendoTreeView');
            tv.element.find("span.k-in").each(function () {   
                //check if item in selected values
                var item = tv.dataItem(this);
                var found = selectedVlaues.findIndex(x => x.value==item.id);
                if (found >= 0) {
                    $(this).addClass('sele');
                }else{
                    $(this).removeClass('sele');
                }
            });
        }
    </script>

Please view the previous article for binding the treeview or you can use json array on the client side.

How Code Works (In Depth Explanations)

For this, I have added the 'Select' element and convert it to Telerik Multiselect using the below code:

function createMultiSelect() {

            // Create kedno multiselect
            multiSelect = $("#multiselect").kendoMultiSelect({
                optionLabel: "Select Legal Topics...",
                dataTextField: "text",
                dataValueField: "value",
                readonly: true,
                disabled: true                
            }).data("kendoMultiSelect");

            // Kendo multiselect in read only mode
            multiSelect.readonly();
        }

The above function is called at page load inside '$(document).ready' event.

Next, on selecting any element in the tree, I am pushing that into multiselect using the below code:

//This is inside onSelect(e) function
saveMultiselectArray(item, selectedVlaues);

//Method
function saveMultiselectArray(item, arr) {
            var found = arr.findIndex(x => x.value==item.value);
            if (found >= 0) {
                // Element was found, remove it.
                arr.splice(found, 1);
                $('#treeViewSearchInput').val('');
                $('#lblselected').html('');
                
            } else {
                // Element was not found, add it.
                arr.push(item);
                $('#treeViewSearchInput').val(item.text);
                $('#lblselected').html("Selecting: " + item.text + " & ID:" + item.value);
            }
        }

Next, the below code highlights the preselected nodes of the tree by making them bold.

function SetSelectedNodes(){
            var tv = $('#treeview').data('kendoTreeView');
            tv.element.find("span.k-in").each(function () {   
                //check if item in selected values
                var item = tv.dataItem(this);
                var found = selectedVlaues.findIndex(x => x.value==item.id);
                if (found >= 0) {
                    $(this).addClass('sele');
                }else{
                    $(this).removeClass('sele');
                }

            });
        }

Now, we are all ready to test our code. Just paste the code in your project on respective position of page and run the project.

Points of Interest

This (Kendo) is one of the most impressive things I ever found for web development as it provides full customization and control to the developer and with little modification, you can create anything and everything you want. Following are the interesting features of this control.

  1. Treeview in Dropdown: This will automatically convert your data to Treeview like structure on the basic of Json string supplied.

    Note: The Json string is all responsible that tells Kendo about Parent and Child of the node. So please observe the Json string carefully.

  2. Searching: From the input box on the top, you can start type and voila!! your data be filtered in the dropdown. Isn't it bolt fast?
  3. Highlighted Text: As soon as you type a keyword, you will see the filtration with matching text highlighted.
  4. Multiselected Item: You can see the selected item to the input box just like the selected item in the dropdown.

Thanks for reading!

That's all about the Treeview Dropdown With Multiselect with filtering, fully client-side controlled. Any comments, suggestions are welcome. Please do leave your feedback and appropriate query. I will reply to you for sure.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here