Treeview expanded state

Rate It (1)

Last post 05-09-2007 10:18 AM by hotfirewire. 21 replies.

Sort Posts:

  • Treeview expanded state

    09-08-2006, 1:41 PM
    • Loading...
    • twad
    • Joined on 08-24-2006, 1:00 PM
    • Posts 5

    I wondering if the expanded state of nodes is supposed to be updated on postback like the normal Treeview?

    Or is this left as an exercise? I will probably add this functionality - if it it is not working. The normal Treeview got an hiddenfield _ExpandState where this information is updatet in, so I will probably just use that.

    To get Checkmarks to work for a postback from a button outside the treeview I had to add a ugly hack - I know this is not a solution because the events are raised at the wrong time:

            protected override void OnLoad(EventArgs e)
            {
                base.OnLoad(e);

                // Hack
                if (Page.IsPostBack)
                {
                    TreeView treeView = Control as TreeView;
                    if (treeView != null)
                    {
                        TreeNodeCollection items = treeView.Nodes;
                        _checkboxIndex = 1;
                        UpdateCheckmarks(items);
                    }
                }
            }

    Filed under: , ,
  • Re: Treeview expanded state

    09-08-2006, 2:47 PM
    • Loading...
    • Russ Helfand
    • Joined on 09-14-2005, 6:22 PM
    • Groovybits.com
    • Posts 740
    Thanks for finding this problem.  We'll look into fixing it for the next update.  Sorry about the inconvenience meanwhile but with new functionality comes a few rough edges.  We really appreciate your help and patience as we work out these problems.
    Russ Helfand
    Groovybits.com
  • Re: Treeview expanded state

    09-12-2006, 2:37 PM
    • Loading...
    • jason_m
    • Joined on 01-27-2006, 2:36 PM
    • Marysville, PA
    • Posts 81

    I have this same problem with the checkboxes not holding their checked state. This post has my findings: http://forums.asp.net/thread/1396596.aspx I also have a project I'm working on where the treeview with checkboxes is within a wizard. No checked values are stored when I move from one step to the next.

    I attempted to implment your hack to keep the state of check boxes, but it doesn't work for me.  I added this code to the CSSFriendly.TreeViewAdapter : System.Web.UI.WebControls.Adapters.HierarchicalDataBoundControlAdapter, IPostBackEventHandler class. I then added the code to the OnInit event. Still no luck.

    Where did you place this code so the checked state would be saved on postback?  Do you know if this is a viewstate problem or something else?

    Jason
    Programmer
    Specialty Bakers, Inc.
  • Re: Treeview expanded state

    09-12-2006, 4:08 PM
    • Loading...
    • jason_m
    • Joined on 01-27-2006, 2:36 PM
    • Marysville, PA
    • Posts 81

    I solved the above problem... and found another.  I customized some of the treeview adapter. my checkbox naming convention wasn't working correctly.  after reconfiguring the checkbox naming convention I can keep the checked state just long enought to validate some information.

    The other problem I did find is that upon returning to the wizardstep with the treeview (clicking "previous") I loose the checked state.

    Jason
    Programmer
    Specialty Bakers, Inc.
  • Re: Treeview expanded state

    09-12-2006, 8:33 PM
    • Loading...
    • Russ Helfand
    • Joined on 09-14-2005, 6:22 PM
    • Groovybits.com
    • Posts 740

    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);
                        }
                    }
                }
            }
        }
    }

    Russ Helfand
    Groovybits.com
  • Re: Treeview expanded state

    09-13-2006, 10:34 AM
    • Loading...
    • jason_m
    • Joined on 01-27-2006, 2:36 PM
    • Marysville, PA
    • Posts 81
    Russ, the checkboxes are holding their value when another control posts back to the server.

    I haven't fully tested the expanding/collasping feature becaues this is not required for my present project.  I did experiment with it a little.  It appears the checkbox controls the expanded state. Now my tree requires the checkboxes and I did notice there is a span with with space within the list item so this may be what's causing the expand/collaspe.  Again I didn't dive to deep into it.

    There is 1 minor change that I made concerning checkboxes in the BuildItem function. I added a label if the treeview has checkbox, if not it outputs the raw text.

    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");
    
     //Added a javascript attribute to the checkbox this event marks children/parents appropiately
     //This would be great if a new property could be added and assigned to so the javascript can be
     //dynamically generated.
     writer.WriteAttribute("onclick", "SBI_TreeViewCheck(event);");
     if (item.Checked)
     {
      writer.WriteAttribute("checked", "checked");
     }
     writer.Write(HtmlTextWriter.SelfClosingTagEnd);
    
     // added the following if statement to create label for checkbox
     if (item.Text.Length > 0)
     {
      writer.WriteLine();
      writer.WriteBeginTag("label");
      writer.WriteAttribute("for", treeView.ClientID + "n" + _checkboxIndex.ToString() + "CheckBox");
      writer.Write(HtmlTextWriter.TagRightChar);
      writer.Write(item.Text);
      writer.WriteEndTag