This is a migrated thread and some comments may be shown as answers.

RadTreeView: ServerSideCallBack + Tri-State Checkboxes.

5 Answers 317 Views
TreeView
This is a migrated thread and some comments may be shown as answers.
Kyle Murphy
Top achievements
Rank 1
Kyle Murphy asked on 12 Nov 2009, 01:14 AM
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

5 Answers, 1 is accepted

Sort by
0
Yana
Telerik team
answered on 17 Nov 2009, 02:56 PM
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.
0
Kyle Murphy
Top achievements
Rank 1
answered on 17 Nov 2009, 11:53 PM
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

0
Yana
Telerik team
answered on 20 Nov 2009, 01:10 PM
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.
0
Kyle Murphy
Top achievements
Rank 1
answered on 20 Nov 2009, 06:17 PM
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

0
Yana
Telerik team
answered on 25 Nov 2009, 01:27 PM
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.
Tags
TreeView
Asked by
Kyle Murphy
Top achievements
Rank 1
Answers by
Yana
Telerik team
Kyle Murphy
Top achievements
Rank 1
Share this question
or