True Tri-State Checkbox with TreeView

2 posts, 1 answers
  1. Craig
    Craig avatar
    10 posts
    Member since:
    Jan 2015

    Posted 17 Mar 2015 Link to this post

    I am attempting to create a TreeView with Tri-State Checkboxes. However, I want tri-state checkboxes right down to bottom level of the tree, not just to represent mixed selection of attributes on a parent node. Every node must have the ability to be tri-state. This doesn't seem possible using the built in checkbox capability of the TreeView, so what I've done is use a TreeView Template and embed a RadButton Type=ToggleType with tri-state functionality. As Such:

    <telerik:RadTreeView runat="server" ID="RadTreeView1">
            <NodeTemplate>
                <telerik:RadTreeNode Text="<%# DataBinder.Eval(Container.DataItem, "Name") %>">
                <NodeTemplate>
                <telerik:RadButton OnClientCheckedChanged="GetSiblings" ID="RadButton1" runat="server" ToggleType="CustomToggle" ButtonType="ToggleButton" AutoPostBack="false" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>'>
                    <ToggleStates>
                        <telerik:RadButtonToggleState Value="Unchecked" PrimaryIconCssClass="rbToggleCheckbox"></telerik:RadButtonToggleState>
                        <telerik:RadButtonToggleState Value="Default" PrimaryIconCssClass="rbToggleCheckboxFilled"></telerik:RadButtonToggleState>
                        <telerik:RadButtonToggleState Value="Checked" PrimaryIconCssClass="rbToggleCheckboxChecked" Selected="true">    </telerik:RadButtonToggleState>
                    </ToggleStates>
                </telerik:RadButton>
                </NodeTemplate>
                </telerik:RadTreeNode>
            </NodeTemplate>
        </telerik:RadTreeView>
    <telerik:RadButton ID="Button1" runat="server" OnClick="Button1_Click" Text="Traverse the Tree"></telerik:RadButton>
    <div class="module">
       <h2>You have selected the following items:</h2>
       <asp:Label ID="nodesClientside" runat="server" BorderStyle="None" CssClass="text"></asp:Label>
    </div>

    This has worked just fine so far for the functionality I need. Where I am running into a problem, is trying to manage changing parent checkboxes to match when all children or no children are selected. Obviously if a node has three children and it is selected then I want all the children to match the selected state. Likewise, if any one child node is selected I would like to get all sibling nodes, check if they are all the same state and set the parent node state appropriately.

    However, Client Side when the OnClientCheckedChanged event fires for the RadButton within the Node, how do I pass the javascript a reference to the RadTreeNode that the RadButton is contained within? Or even on the backend if i call OnCheckedChanged from the RadButton embedded inside a RadTreeNode, is there a way I can get a reference to that node so that I can use get_parent.get_nodes() or traverse the TreeView to retrieve all sibling nodes for processing? 

    I've pasted the complete code files below

    Thanks.


    Complete Front end ascx file code:
    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TreeView.ascx.cs" Inherits="DDW.TreeView" %>
    <%@ Register Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" TagPrefix="telerik" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns='http://www.w3.org/1999/xhtml'>
    <head>
        <title>Telerik ASP.NET Example</title>
        <style type="text/css">
            .node-album
            {
                width: 186px;
                height: 35px;
                background: transparent url('images/album.png');
            }
     
            .node-album .band,
            .node-album .album
            {
                margin-left: 37px;
                display:block;
            }
     
            .node-album .band
            {
                font-size: 14px;
            }
            .node-album .album
            {
                color:#871fdf;
                font-size: 11px;
                line-height: 13px;
            }
            .node-album-data 
            {
                width:420px;
            }
            .node-album-data img
            {
                float:left;
                margin-top: 5px;
            }
            .node-album-data .album
            {
                font-size: 18px;
            }
            .node-album-data .album,
            .node-album-data .details
            {
                display:block;
                margin-left: 90px;
            }
     
            .node-album-data .details
            {
                margin-top: 10px;
                white-space:normal;
            }
            .RadTreeView_Default .rtHover span.rtIn
            {
                color: black !important;
                background:none !important;
                border: none !important;
                padding: 2px !important;
            }

            .RadTreeView_Default .rtSelected span.rtIn
            {
                color: black !important;
                background:none !important;
                border: none !important;
                padding: 2px !important;
            }
            .rtToggleCheckboxMixed {
                background: #CCC;
            }
        </style>
        <script type="text/javascript">
            function GetSiblings(textName) {
                var tree = $find("<%= RadTreeView1.ClientID %>");
                var node = tree.findNodeByText(textName);
                var allSiblingNotes = node.get_parent.get_nodes();
                for (var i = 0; i < allSiblingNotes.length; i++) {
                    var node = allSiblingNotes[i];
                    var CheckBox = node.findControl("RadButton1");
                    alert(CheckBox.get_text());
                }
            }
        </script>
    </head>
    <body>
        <div class="demo-container size-medium">
                <telerik:RadTreeView runat="server" ID="RadTreeView1">
                    <NodeTemplate>
                        <telerik:RadTreeNode Text="<%# DataBinder.Eval(Container.DataItem, "Name") %>">
                        <NodeTemplate>
                        <telerik:RadButton OnClientCheckedChanged="GetSiblings" ID="RadButton1" runat="server" ToggleType="CustomToggle" ButtonType="ToggleButton" AutoPostBack="false" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>'>
                            <ToggleStates>
                                <telerik:RadButtonToggleState Value="Unchecked" PrimaryIconCssClass="rbToggleCheckbox"></telerik:RadButtonToggleState>
                                <telerik:RadButtonToggleState Value="Default" PrimaryIconCssClass="rbToggleCheckboxFilled"></telerik:RadButtonToggleState>
                                <telerik:RadButtonToggleState Value="Checked" PrimaryIconCssClass="rbToggleCheckboxChecked" Selected="true"></telerik:RadButtonToggleState>
                            </ToggleStates>
                        </telerik:RadButton>
                        </NodeTemplate>
                        </telerik:RadTreeNode>
                    </NodeTemplate>
                </telerik:RadTreeView>
            <telerik:RadButton ID="Button1" runat="server" OnClick="Button1_Click" Text="Traverse the Tree"></telerik:RadButton>
            <div class="module">
               <h2>You have selected the following items:</h2>
               <asp:Label ID="nodesClientside" runat="server" BorderStyle="None" CssClass="text"></asp:Label>
            </div>

        </div>
    </body>
    </html>


    Backend Code Complete:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using Telerik.Web.UI;


    namespace DDW
    {
        public partial class TreeView : System.Web.UI.UserControl
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                if (!Page.IsPostBack)
                {
                    RadTreeView1.DataSource = GetAttributes();

                    RadTreeView1.DataFieldID = "ID";
                    RadTreeView1.DataFieldParentID = "ParentID";
                    //attributesTree.DataTextField = "Name";

                    RadTreeView1.DataBind();

                }
            }

            public List<Attribute> GetAttributes()
            {
                List<Attribute> attributesList = new List<Attribute>();

                attributesList.Add(new Attribute(1, null, "Attribute A", "Description for Attribute A", true, false, false));
                attributesList.Add(new Attribute(2, 1, "Attribute A1", "Description for Attribute A1", true, false, false));
                attributesList.Add(new Attribute(3, 1, "Attribute A2", "Description for Attribute A2", false, false, false));
                attributesList.Add(new Attribute(4, 1, "Attribute A3", "Description for Attribute A3", false, false, false));
                attributesList.Add(new Attribute(11, 4, "Attribute A3a", "Description for Attribute A3a", false, false, false));
                attributesList.Add(new Attribute(12, 4, "Attribute A3b", "Description for Attribute A3b", false, false, false));
                attributesList.Add(new Attribute(5, null, "Attribute B", "Description for Attribute B", true, false, true));
                attributesList.Add(new Attribute(6, 5, "Attribute B1", "Description for Attribute B1", true, false, true));
                attributesList.Add(new Attribute(7, 5, "Attribute B2", "Description for Attribute B2", false, false, false));
                attributesList.Add(new Attribute(8, null, "Attribute C", "Description for Attribute C", false, false, false));
                attributesList.Add(new Attribute(9, 8, "Attribute C1", "Description for Attribute C1", false, false, false));
                attributesList.Add(new Attribute(10, 8, "Attribute C2", "Description for Attribute C2", false, false, false));


                return attributesList;

            }
            public class Attribute
            {
                public int ID { get; set; }
                public int? ParentID { get; set; }
                public string Name { get; set; }
                public string Description { get; set; }
                public bool Include { get; set; }
                public bool Exclude { get; set; }
                public bool Default { get; set; }

                public Attribute()
                {

                }

                public Attribute(int id, int? parentID, string name, string description, bool def, bool include, bool exclude)
                {
                    ID = id;
                    ParentID = parentID;
                    Name = name;
                    Description = description;
                    Default = def;
                    Include = include;
                    Exclude = exclude;
                }

            }

            protected void Button1_Click(object sender, EventArgs e)
            {
                ShowCheckedNodes(RadTreeView1, nodesClientside);
            }
            private static void ShowCheckedNodes(RadTreeView treeView, Label label)
            {
                string message = string.Empty;
                foreach (RadTreeNode node in treeView.GetAllNodes())
                {
                    RadButton CheckBoxState = (RadButton)node.FindControl("RadButton1");
                    message += CheckBoxState.Text + ": " + CheckBoxState.ToggleStates[CheckBoxState.SelectedToggleStateIndex].Value + "<br/>";

                }
                label.Text = message;
            }
        }
    }











  2. Answer
    Dimitar
    Admin
    Dimitar avatar
    281 posts

    Posted 20 Mar 2015 Link to this post

    Hello,

    I am afraid that there are several errors that would stop the GetSiblings() function to achieve what is intended.

    First of all, you should use the RadButton OnClientToggleStateChanged event, instead of OnClientCheckedChanged. This change is required because you are using ToggleType="CustomToggle" and it that case OnClientCheckedChanged is not fired, as specified in the above articles.

    Second, note that get_parent.get_nodes() won't work - it should be get_parent().get_nodes().

    And third, allSiblingNodes is not an array, but a RadTreeNodeCollection. Thus, you should get its length through the get_count() method. Using allSiblingNodes.length returns undefined. And as it is a RadTreeNodeCollection, you should get its nodes through the getNode(i) method - allSiblingNodes.getNode(i), instead of just using an indexer.

    All this said and changed, you could use the RadTreeView method _extractNodeFromDomElement() to get the node, which contains the toggled button. The snippet below shows the OnClientToggleStateChanged handler:

    <script type="text/javascript">
        var treeView;
     
        function pageLoad() {
            treeView = $find("<%= RadTreeView1.ClientID %>");
        }
     
        function GetSiblings(sender, args) {
            var buttonElement = sender.get_element();
            var buttonNode = treeView._extractNodeFromDomElement(buttonElement);
     
            var allSiblingNodes = buttonNode.get_parent().get_nodes();
     
            for (var i = 0; i < allSiblingNodes.get_count(); i++) {
                var node = allSiblingNodes.getNode(i);
                var CheckBox = node.findControl("RadButton1");
                alert(CheckBox.get_text());
            }
        }
    </script>

    Now you have a reference to the RadButton1 control in each sibling node. Please try it on your side and tell me if it does the trick.

    Regards,
    Dimitar
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  3. UI for ASP.NET Ajax is Ready for VS 2017
Back to Top