Understanding TreeView in .NET Core

Treeview

This article is about Treeview in .NET Core. The beauty of this code is you can extend on top of this code. This treeview is flexible instead of jstree.

You have to add nodes in the below format.

You can add nodes in any order.

TreeNode node11 = new TreeNode
{
    NodeText = "Text11",
    NodeValue = "Value11",
    NodeType = "PARENT",
    NodeLevel = "11"
};
liNodes.Add(node11);

TreeNode node112 = new TreeNode
{
    NodeText = "Text112",
    NodeValue = "Value112",
    NodeType = "CHILD",
    NodeLevel = "112"
};
liNodes.Add(node112);

This is based on C#.

Below is cshtml

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
@using treeview.Models;
@model IEnumerable<TreeNode>
<script src="/js/javascript.js?128"></script>
<script>
    const nedelevels = '@ViewBag.NodeLevels'.split(';');
</script>

@foreach (TreeNode node in Model)
{
    if (node.NodeType == "PARENT")
    {
        var marginlength = @node.NodeLevel.Length * 10;
        var class1 = "Class" + node.NodeLevel;
        var visibility = (node.NodeLevel.Length == 1) ? "block" : "none";
        
        <div>
            <label style="margin-left:@marginlength; display:@visibility" 
                   onclick="toggleexpand(event, '@node.NodeLevel', nedelevels)" 
                   class="@class1">+ @node.NodeText</label>
        </div>
    }
    else
    {
        var visibility = (node.NodeLevel.Length == 1) ? "block" : "none";
        var marginlength = @node.NodeLevel.Length * 10;
        var class2 = "Class" + node.NodeLevel;
        var margin = marginlength + "px";

        <div style="margin-left:@margin;">
            <label style="display:@visibility" 
                   class="@class2" 
                   onclick="SelectNode('@node.NodeValue', '@node.NodeLevel')">@node.NodeText</label>
        </div>
    }
}

<script>
    restoreTreeState('@ViewBag.ExpandNodes');
</script>

Below is Controller

public IActionResult Privacy(string NODEVALUE, string NODELEVEL)
{
    if (NODELEVEL == null)
        NODELEVEL = "";
    if (NODEVALUE == null)
        NODEVALUE = "";

    List<TreeNode> liNodes = new List<TreeNode>();

    TreeNode node1 = new TreeNode
    {
        NodeText = "Text1",
        NodeValue = "Value1",
        NodeType = "PARENT",
        NodeLevel = "1"
    };
    liNodes.Add(node1);

    TreeNode node2 = new TreeNode
    {
        NodeText = "Text2",
        NodeValue = "Value2",
        NodeType = "PARENT",
        NodeLevel = "2"
    };
    liNodes.Add(node2);

    TreeNode node11 = new TreeNode
    {
        NodeText = "Text11",
        NodeValue = "Value11",
        NodeType = "PARENT",
        NodeLevel = "11"
    };
    liNodes.Add(node11);

    TreeNode node112 = new TreeNode
    {
        NodeText = "Text112",
        NodeValue = "Value112",
        NodeType = "CHILD",
        NodeLevel = "112"
    };
    liNodes.Add(node112);

    TreeNode node111 = new TreeNode
    {
        NodeText = "Text111",
        NodeValue = "Value111",
        NodeType = "CHILD",
        NodeLevel = "111"
    };
    liNodes.Add(node111);

    TreeNode node12 = new TreeNode
    {
        NodeText = "Text12",
        NodeValue = "Value12",
        NodeType = "CHILD",
        NodeLevel = "12"
    };
    liNodes.Add(node12);

    TreeNode node21 = new TreeNode
    {
        NodeText = "Text21",
        NodeValue = "Value21",
        NodeType = "CHILD",
        NodeLevel = "21"
    };
    liNodes.Add(node21);

    TreeNode node22 = new TreeNode
    {
        NodeText = "Text22",
        NodeValue = "Value22",
        NodeType = "CHILD",
        NodeLevel = "22"
    };
    liNodes.Add(node22);

    liNodes = liNodes.OrderBy(x => x.NodeLevel).ToList();

    var nodes = "";
    foreach (TreeNode node in liNodes)
    {
        nodes += node.NodeLevel.ToString() + ";";
    }

    ViewBag.NodeLevels = nodes;
    var expandnodes = splitstring(NODELEVEL);
    ViewBag.ExpandNodes = expandnodes;

    return View(liNodes);
}

Below is JavaScript

function restoreTreeState(pastnodevalues) {
    const expandnodes = pastnodevalues.split(';');
    expandnodes.forEach(clicknodes);
}

function clicknodes(item) {
    var cl = '.' + 'Class' + item;
    $(cl).click();
}

function toggleexpand(event, level, nodelevels2) {
    var nextLevel = '';
    var found = false;

    if ($(event.target).text().indexOf('-') > -1) {
        $(event.target).text($(event.target).text().replace("-", "+"));
        var lastchar = level.substring(level.length - 1);
        var nxtlevel = level.substring(0, level.length - 1);
        var nxtlevel2 = nxtlevel + (Number(lastchar) + 1);
        nextLevel = level + '1';

        for (let i = 1; i < nodelevels2.length; i++) {
            if (nodelevels2[i] >= nextLevel) {
                if (nodelevels2[i].startsWith(nxtlevel2)) {
                    break;
                }
                var cl = '.' + 'Class' + nodelevels2[i];
                $(cl).css('display', 'none');
            }
        }
    } else {
        $(event.target).text($(event.target).text().replace("+", "-"));
        nextLevel = level + 1;
        var length = nextLevel.length;
        var x = 1;

        for (let i = 1; i < nodelevels2.length; i++) {
            if (nodelevels2[i] == nextLevel) {
                if (nodelevels2[i].length != length) break;

                var cl = '.' + 'Class' + nodelevels2[i];
                if ($(cl).text().indexOf('-') > -1) {
                    $(cl).text($(cl).text().replace("-", "+"));
                }
                $(cl).css('display', 'block');
                $(cl).css('margin-left', (Number(nodelevels2[i].length) * 10).toString() + 'Px');
                x++;
                nextLevel = (level + x).toString();
            }
        }
    }
}

function SelectNode(nodevalue, nodelevel) {
    var url = window.location.toString();
    url = updateQueryStringParameter(url, "NODEVALUE", nodevalue);
    url = updateQueryStringParameter(url, "NODELEVEL", nodelevel);
    window.location = url;
}

function updateQueryStringParameter(uri, key, value) {
    var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
    var separator = uri.indexOf('?') !== -1 ? "&" : "?";
    
    if (uri.match(re)) {
        return uri.replace(re, '$1' + key + "=" + value + '$2');
    } else {
        return uri + separator + key + "=" + value;
    }
}

Below is model

namespace treeview.Models
{
    public class TreeNode
    {
        public string NodeText { get; set; }
        public string NodeValue { get; set; }
        public string NodeType { get; set; }
        public string NodeLevel { get; set; }
    }
}