Twad and Jason and others,
I need one or more volunteers to try an experimental fix for this problem. I'm posting below a modified version of the TreeView adapter (in C#, but I can give you a VB version if you really need it) that may fix at least some of your problems related to retaining the checkbox state and reexpanded automatically to the selected node. Could you try this and let me know what you think? It may not be perfect because it only reexpands automatically to the selected node; it doesn't reexpand the tree to the same exact state it was in when the page was posted back. Still, this new version may be better overall then what you got in the beta 2.0 version of the adapter kit. In any case, please help me out by testing this and giving me some feedback. THANKS.
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Configuration;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Reflection;
namespace CSSFriendly
{
public class TreeViewAdapter : System.Web.UI.WebControls.Adapters.HierarchicalDataBoundControlAdapter, IPostBackEventHandler, IPostBackDataHandler
{
private WebControlAdapterExtender _extender = null;
private WebControlAdapterExtender Extender
{
get
{
if (((_extender == null) && (Control != null)) ||
((_extender != null) && (Control != _extender.AdaptedControl)))
{
_extender = new WebControlAdapterExtender(Control);
}
return _extender;
}
}
private int _checkboxIndex = 1;
public TreeViewAdapter()
{
//
}
// Implementation of IPostBackDataHandler
public virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
return true;
}
public virtual void RaisePostDataChangedEvent()
{
TreeView treeView = Control as TreeView;
if (treeView != null)
{
ExpandToSelectedNode();
TreeNodeCollection items = treeView.Nodes;
_checkboxIndex = 1;
UpdateCheckmarks(items);
}
}
// IPostBackEventHandler
public void RaisePostBackEvent(string eventArgument)
{
TreeView treeView = Control as TreeView;
if (treeView != null)
{
TreeNodeCollection items = treeView.Nodes;
if (eventArgument != null)
{
if (eventArgument.StartsWith("s") || eventArgument.StartsWith("e"))
{
string selectedNodeValuePath = eventArgument.Substring(1).Replace("\\", "/");
TreeNode selectedNode = treeView.FindNode(selectedNodeValuePath);
if (selectedNode != null)
{
bool bSelectedNodeChanged = selectedNode != treeView.SelectedNode;
ClearSelectedNode(items);
selectedNode.Selected = true; // does not raise the SelectedNodeChanged event so we have to do it manually (below).
ExpandToSelectedNode();
if (eventArgument.StartsWith("e"))
{
selectedNode.Expanded = true;
}
if (bSelectedNodeChanged)
{
Extender.RaiseAdaptedEvent("SelectedNodeChanged", new EventArgs());
}
}
}
else if (eventArgument.StartsWith("p"))
{
string parentNodeValuePath = eventArgument.Substring(1).Replace("\\", "/");
TreeNode parentNode = treeView.FindNode(parentNodeValuePath);
if ((parentNode != null) && ((parentNode.ChildNodes == null) || (parentNode.ChildNodes.Count == 0)))
{
ExpandToNode(parentNode);
parentNode.Expanded = true; // Raises the TreeNodePopulate event
}
}
}
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (Extender.AdapterEnabled)
{
RegisterScripts();
}
}
private void RegisterScripts()
{
Extender.RegisterScripts();
string folderPath = (WebConfigurationManager.AppSettings.Get("CSSFriendly-JavaScript-Path") != null) ? WebConfigurationManager.AppSettings.Get("CSSFriendly-JavaScript-Path") : "~/JavaScript";
string filePath = folderPath.EndsWith("/") ? folderPath + "TreeViewAdapter.js" : folderPath + "/TreeViewAdapter.js";
Page.ClientScript.RegisterClientScriptInclude(GetType(), GetType().ToString(), Page.ResolveUrl(filePath));
}
protected override void RenderBeginTag(HtmlTextWriter writer)
{
if (Extender.AdapterEnabled)
{
Extender.RenderBeginTag(writer, "AspNet-TreeView");
}
else
{
base.RenderBeginTag(writer);
}
}
protected override void RenderEndTag(HtmlTextWriter writer)
{
if (Extender.AdapterEnabled)
{
Extender.RenderEndTag(writer);
}
else
{
base.RenderEndTag(writer);
}
}
protected override void RenderContents(HtmlTextWriter writer)
{
if (Extender.AdapterEnabled)
{
TreeView treeView = Control as TreeView;
if (treeView != null)
{
writer.Indent++;
_checkboxIndex = 1;
BuildItems(treeView.Nodes, true, true, writer);
writer.Indent--;
writer.WriteLine();
}
}
else
{
base.RenderContents(writer);
}
}
private void BuildItems(TreeNodeCollection items, bool isRoot, bool isExpanded, HtmlTextWriter writer)
{
if (items.Count > 0)
{
writer.WriteLine();
writer.WriteBeginTag("ul");
if (!isExpanded)
{
writer.WriteAttribute("class", "AspNet-TreeView-Hide");
}
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
foreach (TreeNode item in items)
{
BuildItem(item, writer);
}
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("ul");
}
}
private void BuildItem(TreeNode item, HtmlTextWriter writer)
{
TreeView treeView = Control as TreeView;
if ((treeView != null) && (item != null) && (writer != null))
{
writer.WriteLine();
writer.WriteBeginTag("li");
writer.WriteAttribute("class", GetNodeClass(item));
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
writer.WriteLine();
if (IsExpandable(item))
{
writer.WriteBeginTag("span");
writer.WriteAttribute("class", ((item.Expanded == true) ? "AspNet-TreeView-Collapse" : "AspNet-TreeView-Expand"));
if (HasChildren(item))
{
writer.WriteAttribute("onclick", "ExpandCollapse__AspNetTreeView(this)");
}
else
{
writer.WriteAttribute("onclick", Page.ClientScript.GetPostBackEventReference(treeView, "p" + (Page.Server.HtmlEncode(item.ValuePath)).Replace("/", "\\"), true));
}
writer.Write(HtmlTextWriter.TagRightChar);
writer.Write(" ");
writer.WriteEndTag("span");
writer.WriteLine();
}
bool bItemIsLink = ((item.NavigateUrl.Length > 0) || (item.SelectAction == TreeNodeSelectAction.Select) || (item.SelectAction == TreeNodeSelectAction.SelectExpand) || item.PopulateOnDemand);
if (bItemIsLink)
{
writer.WriteBeginTag("a");
if (item.NavigateUrl.Length > 0)
{
writer.WriteAttribute("href", Page.Server.HtmlEncode(treeView.ResolveClientUrl(item.NavigateUrl)));
}
else
{
string codePrefix = "";
if (item.SelectAction == TreeNodeSelectAction.Select)
{
codePrefix = "s";
}
else if (item.SelectAction == TreeNodeSelectAction.SelectExpand)
{
codePrefix = "e";
}
else if (item.PopulateOnDemand)
{
codePrefix = "p";
}
writer.WriteAttribute("href", Page.ClientScript.GetPostBackClientHyperlink(treeView, codePrefix + (Page.Server.HtmlEncode(item.ValuePath)).Replace("/", "\\"), true));
}
if (item.Target.Length > 0)
{
writer.WriteAttribute("target", item.Target);
}
if (item.ToolTip.Length > 0)
{
writer.WriteAttribute("title", item.ToolTip);
}
else if (treeView.ToolTip.Length > 0)
{
writer.WriteAttribute("title", treeView.ToolTip);
}
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
writer.WriteLine();
}
else
{
writer.WriteBeginTag("span");
if (IsExpandable(item))
{
writer.WriteAttribute("class", "AspNet-TreeView-ClickableNonLink");
writer.WriteAttribute("onclick", "ExpandCollapse__AspNetTreeView(this.parentNode.getElementsByTagName('span')[0])");
}
else
{
writer.WriteAttribute("class", "AspNet-TreeView-NonLink");
}
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
writer.WriteLine();
}
string imgSrc = GetImageSrc(treeView, item);
if (imgSrc.Length > 0)
{
writer.WriteBeginTag("img");
writer.WriteAttribute("src", treeView.ResolveClientUrl(imgSrc));
writer.WriteAttribute("alt", item.ToolTip.Length > 0 ? item.ToolTip : (treeView.ToolTip.Length > 0 ? treeView.ToolTip : item.Text));
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
}
if (((item.ShowCheckBox != null) && (item.ShowCheckBox.Value == true)) ||
(treeView.ShowCheckBoxes == TreeNodeTypes.All) ||
((treeView.ShowCheckBoxes == TreeNodeTypes.Leaf) && (!IsExpandable(item))) ||
((treeView.ShowCheckBoxes == TreeNodeTypes.Parent) && (IsExpandable(item))) ||
((treeView.ShowCheckBoxes == TreeNodeTypes.Root) && (item.Depth == 0)))
{
writer.WriteBeginTag("input");
writer.WriteAttribute("type", "checkbox");
writer.WriteAttribute("id", treeView.ClientID + "n" + _checkboxIndex.ToString() + "CheckBox");
writer.WriteAttribute("name", treeView.UniqueID + "n" + _checkboxIndex.ToString() + "CheckBox");
if (item.Checked)
{
writer.WriteAttribute("checked", "checked");
}
writer.Write(HtmlTextWriter.SelfClosingTagEnd);
_checkboxIndex++;
}
writer.Write(item.Text);
if (bItemIsLink)
{
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("a");
}
else
{
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("span");
}
if (HasChildren(item))
{
BuildItems(item.ChildNodes, false, (item.Expanded == true), writer);
}
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("li");
}
}
private void UpdateCheckmarks(TreeNodeCollection items)
{
TreeView treeView = Control as TreeView;
if ((treeView != null) && (items != null))
{
foreach (TreeNode item in items)
{
if (((item.ShowCheckBox != null) && (item.ShowCheckBox.Value == true)) ||
(treeView.ShowCheckBoxes == TreeNodeTypes.All) ||
((treeView.ShowCheckBoxes == TreeNodeTypes.Leaf) && (!IsExpandable(item))) ||
((treeView.ShowCheckBoxes == TreeNodeTypes.Parent) && (IsExpandable(item))) ||
((treeView.ShowCheckBoxes == TreeNodeTypes.Root) && (item.Depth == 0)))
{
string name = treeView.UniqueID + "n" + _checkboxIndex.ToString() + "CheckBox";
bool bIsNowChecked = (Page.Request.Form[name] != null);
if (item.Checked != bIsNowChecked)
{
item.Checked = bIsNowChecked;
Extender.RaiseAdaptedEvent("TreeNodeCheckChanged", new TreeNodeEventArgs(item));
}
_checkboxIndex++;
}
if (HasChildren(item))
{
UpdateCheckmarks(item.ChildNodes);
}
}
}
}
private string GetNodeClass(TreeNode item)
{
string value = "AspNet-TreeView-Leaf";
if (item != null)
{
if (item.Depth == 0)
{
if (IsExpandable(item))
{
value = "AspNet-TreeView-Root";
}
else
{
value = "AspNet-TreeView-Root AspNet-TreeView-Leaf";
}
}
else if (IsExpandable(item))
{
value = "AspNet-TreeView-Parent";
}
if (item.Selected)
{
value += " AspNet-TreeView-Selected";
}
else if (IsChildNodeSelected(item))
{
value += " AspNet-TreeView-ChildSelected";
}
else if (IsParentNodeSelected(item))
{
value += " AspNet-TreeView-ParentSelected";
}
}
return value;
}
private string GetImageSrc(TreeView treeView, TreeNode item)
{
string imgSrc = "";
if ((treeView != null) && (item != null))
{
imgSrc = item.ImageUrl;
if (imgSrc.Length == 0)
{
if (item.Depth == 0)
{
if ((treeView.RootNodeStyle != null) && (treeView.RootNodeStyle.ImageUrl.Length > 0))
{
imgSrc = treeView.RootNodeStyle.ImageUrl;
}
}
else
{
if (!IsExpandable(item))
{
if ((treeView.LeafNodeStyle != null) && (treeView.LeafNodeStyle.ImageUrl.Length > 0))
{
imgSrc = treeView.LeafNodeStyle.ImageUrl;
}
}
else if ((treeView.ParentNodeStyle != null) && (treeView.ParentNodeStyle.ImageUrl.Length > 0))
{
imgSrc = treeView.ParentNodeStyle.ImageUrl;
}
}
}
if ((imgSrc.Length == 0) && (treeView.LevelStyles != null) && (treeView.LevelStyles.Count > item.Depth))
{
if (treeView.LevelStyles[item.Depth].ImageUrl.Length > 0)
{
imgSrc = treeView.LevelStyles[item.Depth].ImageUrl;
}
}
}
return imgSrc;
}
private bool HasChildren(TreeNode item)
{
return ((item != null) && ((item.ChildNodes != null) && (item.ChildNodes.Count > 0)));
}
private bool IsExpandable(TreeNode item)
{
return (HasChildren(item) || ((item != null) && item.PopulateOnDemand));
}
private bool IsChildNodeSelected(TreeNode item)
{
bool bRet = false;
if ((item != null) && (item.ChildNodes != null))
{
bRet = IsChildNodeSelected(item.ChildNodes);
}
return bRet;
}
private void ClearSelectedNode(TreeNodeCollection nodes)
{
if (nodes != null)
{
foreach (TreeNode node in nodes)
{
if (node.Selected)
{
node.Selected = false;
}
if (node.ChildNodes != null)
{
ClearSelectedNode(node.ChildNodes);
}
}
}
}
private bool IsChildNodeSelected(TreeNodeCollection nodes)
{
bool bRet = false;
if (nodes != null)
{
foreach (TreeNode node in nodes)
{
if (node.Selected || IsChildNodeSelected(node.ChildNodes))
{
bRet = true;
break;
}
}
}
return bRet;
}
private bool IsParentNodeSelected(TreeNode item)
{
bool bRet = false;
if ((item != null) && (item.Parent != null))
{
if (item.Parent.Selected)
{
bRet = true;
}
else
{
bRet = IsParentNodeSelected(item.Parent);
}
}
return bRet;
}
private void ExpandToSelectedNode()
{
TreeView treeView = Control as TreeView;
if ((treeView != null) && (treeView.SelectedNode != null))
{
ExpandToNode(treeView.SelectedNode);
}
}
private void ExpandToNode(TreeNode item)
{
TreeView treeView = Control as TreeView;
if ((treeView != null) && (item != null))
{
treeView.CollapseAll();
TreeNode nodeToExpand = item.Parent;
while (nodeToExpand != null)
{
nodeToExpand.Expanded = true;
nodeToExpand = nodeToExpand.Parent;
}
}
}
static public void ExpandToDepth(TreeNodeCollection nodes, int expandDepth)
{
if (nodes != null)
{
foreach (TreeNode node in nodes)
{
if (node.Depth < expandDepth)
{
node.Expand();
ExpandToDepth(node.ChildNodes, expandDepth);
}
}
}
}
}
}