RadTreeView: ServerSideCallBack + Tri-State Checkboxes.

6 posts, 0 answers
  1. Kyle Murphy
    Kyle Murphy avatar
    6 posts
    Member since:
    Sep 2009

    Posted 11 Nov 2009 Link to this post

    Hello,

    I have been stuck trying to get a working implementation of RadTreeView using tri-state checkboxes and the ServerSideCallBack expand mode. I have a tree view that has a lot of items in it (500+ first-level nodes, each containing 3-6 second-level nodes) so ServerSideCallBack is required for optimal client-side performance. Also, I do not want to do a post back when a node is checked or unchecked. 

    The problem occurs when the first-level node is checked or unchecked and the node is then expanded. Regardless of whether the node was checked or unchecked prior to expansion, the server copy of the node is always checked! If the node is expanded without checking or unchecking prior, the server copy of the node is correct. I have tried many workarounds and methods to fix this problem:

    - Client-side updating the server side with RadTreeView.ClientChanges. Apparently a checkbox change is not considered a client change, as the change is not recorded in the RadTreeView.ClientChanges property. Other events, such as adding a node, register with this property but not checkbox changes.
    - Client-side events. Both the OnClientNodeExpanding and the OnClientNodeExpanded events fire before the child nodes are actually expanded, so a custom variable cannot be used to update the checked state of the expanded nodes after the expansion completes.
    - Setting the CheckState of the node and its children manually upon node expansion. This could potentially work for a non-tri-state checkbox tree, but since the CheckState property of a RadTreeNode is read-only, I do not know of a way to make a node Indeterminate without adding its children as such.
    - Looked at a lot of samples and documentation on telerik's website, nothing looks promising but perhaps I have missed something..

    Below is a very simplified version of my project. If you check any one of the three first-level nodes and then expand that node, the node effectively reverts back to its previous value. If you debug the server-side expansion function, you will see that changing the checked state of a node prior to expansion always results in Node.Checked = true. Is there any way to remedy this problem? Or is this a known (or unknown) shortcoming of the RadTreeView ?

    Also, I noticed in the attached demo project that checking the first-level node labeled "1" updates the root node as fully selected, even though the first-level node labeled "3" is not fully selected (see picture attachment).

    Thank you for any assistance you can provide with this issue.

    Kyle


    Default.aspx:

    <%@ Page Language="c#" CodeFile="Default.aspx.cs" AutoEventWireup="true" Inherits="_Default" %> 
     
    <%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %> 
     
    <!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 id="Head1" runat="server"
        <link rel="stylesheet" href="styles.css" type="text/css" /> 
    </head> 
    <body> 
        <form id="Form1" runat="server"
         
            <telerik:RadScriptManager runat="server" ID="RadScriptManager1" /> 
            <telerik:RadCodeBlock runat="server" ID="RadCodeBlock1"
     
                <script type="text/javascript"
                //<![CDATA[
                
                    var treeView;
                    
                    function pageLoad() {
                        treeView = $find ("<%= RadTreeView1.ClientID %>");
                    }
                    function onTreeNodeCheck(sender, args) {
                        //do client stuff here
                    }
                    function onTreeNodeExpand(sender, args) {
                        //do client stuff here
                    }
                //]]> 
                </script> 
     
            </telerik:RadCodeBlock> 
             
            <div style="overflow: hidden;"
                <ul class="qsfexAvailableActions"
                    <li> 
                        <asp:Button ID="Button2" CssClass="qsfButton" Text="Postback" runat="server" OnClick="Button2_Click"
                        </asp:Button> 
                    </li> 
                </ul> 
                <asp:UpdatePanel ID="treeUpdatePanel" runat="server" UpdateMode="Conditional"
                    <ContentTemplate> 
                        <telerik:RadTreeView ID="RadTreeView1" runat="server" BorderColor="#898772" BorderWidth="1" BorderStyle="Solid" Skin="Hay" CheckBoxes="true" TriStateCheckBoxes="true" CheckChildNodes="true" OnClientNodeChecked="onTreeNodeCheck" OnClientNodeExpanded="onTreeNodeExpand" OnNodeExpand="treeNodeExpand" Height="420px" Width="189px"
                        </telerik:RadTreeView> 
                    </ContentTemplate> 
                </asp:UpdatePanel> 
            </div> 
             
        </form> 
    </body> 
    </html> 

    Default.aspx.cs

    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; 
    using System.Data; 
     
    public partial class _Default : System.Web.UI.Page  
     
        public DataTable treeDataTable;  
     
        protected void Page_Load(object sender, EventArgs e) 
        { 
     
            if (!IsPostBack) 
            { 
     
                //create datatable to fill tree 
                DataRow row; 
                treeDataTable = new DataTable(); 
     
                treeDataTable.Columns.Add("Selected"); 
                treeDataTable.Columns.Add("Market"); 
                treeDataTable.Columns.Add("Site"); 
                treeDataTable.Columns.Add("Sector"); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "0"; row["Market"] = "Cincinnati"; row["Site"] = "1"; row["Sector"] = "1"
                treeDataTable.Rows.Add(row); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "0"; row["Market"] = "Cincinnati"; row["Site"] = "1"; row["Sector"] = "2"
                treeDataTable.Rows.Add(row); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "0"; row["Market"] = "Cincinnati"; row["Site"] = "1"; row["Sector"] = "3"
                treeDataTable.Rows.Add(row); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "1"; row["Market"] = "Cincinnati"; row["Site"] = "2"; row["Sector"] = "1"
                treeDataTable.Rows.Add(row); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "1"; row["Market"] = "Cincinnati"; row["Site"] = "2"; row["Sector"] = "2"
                treeDataTable.Rows.Add(row); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "1"; row["Market"] = "Cincinnati"; row["Site"] = "2"; row["Sector"] = "3"
                treeDataTable.Rows.Add(row); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "0"; row["Market"] = "Cincinnati"; row["Site"] = "3"; row["Sector"] = "1"
                treeDataTable.Rows.Add(row); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "1"; row["Market"] = "Cincinnati"; row["Site"] = "3"; row["Sector"] = "2"
                treeDataTable.Rows.Add(row); 
     
                row = treeDataTable.NewRow(); 
                row["Selected"] = "0"; row["Market"] = "Cincinnati"; row["Site"] = "3"; row["Sector"] = "3"
                treeDataTable.Rows.Add(row); 
     
                //populate tree with the data 
                populateClusterTree(); 
     
                treeUpdatePanel.Update(); 
     
            } 
     
        } 
     
        protected void populateClusterTree() 
        { 
     
            //clear any existing values 
            RadTreeView1.Nodes.Clear(); 
     
            //init node building vars 
            string lastMarket = treeDataTable.Rows[0]["Market"].ToString(); 
            string lastMarketID = "4391"
            string lastSite = "-1"
            RadTreeNode siteNode = null
            RadTreeNode sectorNode = null
     
            //add root market node 
            RadTreeNode rootNode = new RadTreeNode(lastMarket, lastMarketID); 
            rootNode.Expanded = true
            RadTreeView1.Nodes.Add(rootNode); 
     
            //go through each element returned from query 
            foreach (DataRow clusterElementRow in treeDataTable.Rows) 
            { 
     
                //see if this row has a new site 
                if (lastSite != clusterElementRow["Site"].ToString()) 
                { 
     
                    //add last site to root node, if not null 
                    if (siteNode != null
                    { 
     
                        rootNode.Nodes.Add(siteNode); 
     
                    } 
     
                    //new site 
                    lastSite = clusterElementRow["Site"].ToString(); 
     
                    //create site node for newly encountered site 
                    siteNode = new RadTreeNode(lastSite, lastMarketID + "-" + lastSite); 
                    siteNode.ExpandMode = TreeNodeExpandMode.ServerSideCallBack; 
     
                } 
     
                //create new sector node 
                sectorNode = new RadTreeNode(clusterElementRow["Sector"].ToString(), lastMarketID + "-" + lastSite + "-" + clusterElementRow["Sector"].ToString()); 
     
                //check sector node, if appropriate 
                if (clusterElementRow["Selected"].ToString() == "1"
                    sectorNode.Checked = true
     
                //add sector node to last site node 
                siteNode.Nodes.Add(sectorNode); 
                 
            } 
     
            //add last site to root node, if not null 
            if (siteNode != null
            { 
     
                rootNode.Nodes.Add(siteNode); 
     
            } 
     
        } 
     
        protected void treeNodeExpand(object sender, RadTreeNodeEventArgs e) 
        { 
     
            //DEBUG THIS LINE 
            bool nodeChecked = e.Node.Checked; 
     
        } 
     
        protected void Button2_Click(object sender, EventArgs e) 
        { 
     
            foreach (ClientOperation<RadTreeNode> operation in RadTreeView1.ClientChanges) 
            { 
                 
                RadTreeNode node = operation.Item; 
     
                switch (operation.Type) 
                { 
                    case ClientOperationType.Insert: 
                        break
                    case ClientOperationType.Remove: 
                        break
                    case ClientOperationType.Update: 
                        UpdateClientOperation<RadTreeNode> update = (UpdateClientOperation<RadTreeNode>)operation; 
                        break
                } 
     
            } 
     
        } 
     
     

    styles.css:

    .qsfexAvailableActions 
        list-style-typenone
        margin:0; 
        padding:0; 
     
    .qsfexAvailableActions li 
        clearleft
        padding4px 0; 
     
    .qsfexAvailableActions li span, 
    .qsfexAvailableActions li input 
        vertical-align:middle
     
    .qsfexAvailableActions .qsfButtonBig, 
    .qsfexAvailableActions .qsfButton 
        text-align:center
        color#333
        text-decorationnone
        line-height22px
     
    .validator 
        font-size13px
        padding: 0 4px

  2. Yana
    Admin
    Yana avatar
    5013 posts

    Posted 17 Nov 2009 Link to this post

    Hi Kyle,

    Have you tested this code with the latest Q3 2009 version of RadControls? I've tested it with this release and e.Node.Checked returns correct values, I've attached my test project based on your code. Please download it and give it a try.

    Best wishes,
    Yana
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Watch a video on how to optimize your support resource searches and check out more tips on the blogs.
  3. Kyle Murphy
    Kyle Murphy avatar
    6 posts
    Member since:
    Sep 2009

    Posted 17 Nov 2009 Link to this post

    Yana,

    Thanks for your reply, however I have debugged the project you attached and the problem still exists. I even tried using the .dll you provided in my version of the full project, to no avail. As I stated in my first post:

    The problem occurs when the first-level node is checked or unchecked and the node is then expanded. Regardless of whether the node was checked or unchecked prior to expansion, the server copy of the node is always checked!

    Please follow these concise steps to see the problems I am describing.

    Problem 1:

    1. Open the project you attached in your reply.
    2. Place a breakpoint in the Default.aspx.cs file at the line following the comment "//DEBUG THIS LINE"
    3. Begin debugging the project.
    4. Once the page is loaded, uncheck the first-level node labeled "2".
    5. Expand the node that you just unchecked.
    6. The project breaks at the breakpoint you placed in step 2. e.Node.Checked is true and e.Node.CheckState is Checked, when it is not checked. The child nodes are populated as checked despite the fact you just unchecked their parent.

    Problem 2:

    1. Run the project.
    2. Check the first-level node labeled "1".
    3. The root node "Cincinnati" becomes checked even though it has two grandchildren (under child node labeled "3") which are not checked.

    Regards,
    Kyle

  4. Yana
    Admin
    Yana avatar
    5013 posts

    Posted 20 Nov 2009 Link to this post

    Hi Kyle Murphy,

    Thank you for providing the steps for reproducing these issues.

    1. the reason for this issue is that you're loading the subnodes of first-level "2" node in Page_Load event.  So when you expand the node, their subnodes are already present and are unchecked, so it becomes also unchecked.  You should add the subnodes in NodeExpand event handler, I've modified the project to demonstrate this.

    2. the node's checked state is not affected by it's grand nodes

    Sincerely yours,
    Yana
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Watch a video on how to optimize your support resource searches and check out more tips on the blogs.
  5. Kyle Murphy
    Kyle Murphy avatar
    6 posts
    Member since:
    Sep 2009

    Posted 20 Nov 2009 Link to this post

    Yana,

    Thanks again for your assistance.

    Your solution for #1 works well, but it does not allow for indeterminate first-level nodes since RadTreeNode.CheckState is read-only. I have a workaround for marking indeterminate nodes as such on the client side, so no problem there (for me, at least). 

    For #2, a node is certainly affected by its grandchild nodes. For proof, simply check a second-level node in your latest code and the root node (grandparent) turns to indeterminate state. I believe this is a bug due to the fact that a CheckState property value of Indeterminate coincides with a Checked property value of true (I am guessing your updateParents() method considers only the checked property).

    Overall, I think it would be beneficial for RadTreeView to have the ability to be fully constructed on the server side and render child nodes only when they are expanded, without having to do a full postback. I know this is a big request, but I certainly think it's something Telerik should at least consider.

    Regards,
    Kyle

  6. Yana
    Admin
    Yana avatar
    5013 posts

    Posted 25 Nov 2009 Link to this post

    Hi Kyle,

    When you check a second-level node, its parent node is turned indeterminate (because it contains checked and unchecked subnodes) and this affects the root level which also is turned indeterminate.

    Greetings,
    Yana
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Watch a video on how to optimize your support resource searches and check out more tips on the blogs.
Back to Top