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-type: none; |
margin:0; |
padding:0; |
} |
.qsfexAvailableActions li |
{ |
clear: left; |
padding: 4px 0; |
} |
.qsfexAvailableActions li span, |
.qsfexAvailableActions li input |
{ |
vertical-align:middle; |
} |
.qsfexAvailableActions .qsfButtonBig, |
.qsfexAvailableActions .qsfButton |
{ |
text-align:center; |
color: #333; |
text-decoration: none; |
line-height: 22px; |
} |
.validator |
{ |
font-size: 13px; |
padding: 0 4px; |
} |