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
    • Member
      25 point Member
    • twad
    • Member since 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
    • Contributor
      3,298 point Contributor
    • Russ Helfand
    • Member since 09-14-2005, 6:22 PM
    • Groovybits.com
    • Posts 741
    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
    • Member
      283 point Member
    • jason_m
    • Member since 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
    • Member
      283 point Member
    • jason_m
    • Member since 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
    • Contributor
      3,298 point Contributor
    • Russ Helfand
    • Member since 09-14-2005, 6:22 PM
    • Groovybits.com
    • Posts 741

    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
    • Member
      283 point Member
    • jason_m
    • Member since 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("label");
     }
     //and added code
    
     _checkboxIndex++;
    }
    //added else to if-checkbox statment and moved the item.Text into it.
    //this allows the text to display within a label associated with the checkbox.
    //if there is no checkbox then the text is just raw text.
    else
     writer.Write(item.Text);

    I also added an onclick attribute to the checkbox.  This event recurses the tree and marks the childern/parents appropiately. here is the javascript:

    //globals
    var checkedState; //bool
    var parentState; //bool
    
    function SBI_TreeViewCheck(e)
    {
        // obj gives us the node on which check or uncheck operation has performed
        var element = e.srcElement || e.target; 
        parentState = true; //default true;
        
        //checking whether obj consists of checkbox to avoid exception
        if (isCheckBox(element))
        {
            checkedState = element.checked;
            
            //work our way back to the parent <li> element 
            while (!isListItem(element))
                element = element.parentNode;
            recurseThroughChildren(element.firstChild); //set child nodes to checkedState
            
            //from the <li> work our way back to the parent <ul> element 
            while (!isList(element))
                element = element.parentNode;
            recurseThroughParents(element); //set parent nodes accordingly
        }
    }
    
    function recurseThroughChildren(element)
    {
        var chkbox;
        while(!isNull(element))
        {
            //mark the child checkbox the same as the parent
            chkbox = getCheckBox(element);
            if (!isNull(chkbox))
                chkbox.checked = checkedState;
            //recurse through children of current element
            recurseThroughChildren(element.firstChild);
            element = element.nextSibling;
        }
    }
    
    function recurseThroughParents(parent)
    {
        //if it is an unorderd list, process children
        if(isList(parent))
        { 
            //get all child elements that are list items
            var item = parent.getElementsByTagName('LI')[0];
            var chkbox;
            
            //foreach list item...
            while(!isNull(item))
            {
                //whle the children are not check boxes (only 1 checkbox per list item)
                chkbox = getCheckBox(item.firstChild)
                if (!isNull(chkbox) && !chkbox.checked)
                    parentState = false;
                item = item.nextSibling;
            }
            var parentCheckBox = getCheckBox(parent.parentNode);
            if (!isNull(parentCheckBox))
                parentCheckBox.checked = parentState;
        }
        if (!isDiv(parent)) //if it is not a div tag countinue to the next parent
            recurseThroughParents(parent.parentNode);
    }
    
    //return the first checkbox found
    function getCheckBox(obj)
    {
        var ret = null;
        while (!isNull(obj))
        {
            if (isCheckBox(obj))
                ret = obj;
            else if (obj.childNodes.length > 0)
                ret = getCheckBox(obj.firstChild);
            if (isCheckBox(ret))
                break;
            obj = obj.nextSibling;
        }
        return ret;
    }
    
    
    //helper functions
    function isDiv(obj)
    {
        if (isNull(obj))
            return false;
        return (obj.tagName == 'DIV');
    }
    
    function isCheckBox(obj)
    {
        if (isNull(obj))
            return false;
        return (obj.tagName == 'INPUT' && obj.type == 'checkbox');
    }
    
    function isList(obj)
    {
        if (isNull(obj))
            return false;
        return (obj.tagName == 'UL');
    }
    
    function isListItem(obj)
    {
        if (isNull(obj))
            return false;
        return (obj.tagName == 'LI');
    }
    
    function isNull(obj)
    {
        return (obj == null);
    }
     
     
    Jason
    Programmer
    Specialty Bakers, Inc.
  • Re: Treeview expanded state

    09-13-2006, 10:50 AM
    • Contributor
      3,298 point Contributor
    • Russ Helfand
    • Member since 09-14-2005, 6:22 PM
    • Groovybits.com
    • Posts 741

    Nice Jason.  I like both of your modifications.  Do you might if I use them (or variants thereof) in the next update of the kit?

    Overall, it sounds like the problems you were having with checkbox state preservation are solved.  I just want to be certain that is the case.  Is that correct?

    Russ Helfand
    Groovybits.com
  • Re: Treeview expanded state

    09-13-2006, 10:54 AM
    • Contributor
      3,298 point Contributor
    • Russ Helfand
    • Member since 09-14-2005, 6:22 PM
    • Groovybits.com
    • Posts 741
    That last post was supposed to read: do you MIND if I use them...
    Russ Helfand
    Groovybits.com
  • Re: Treeview expanded state

    09-13-2006, 10:57 AM
    • Member
      283 point Member
    • jason_m
    • Member since 01-27-2006, 2:36 PM
    • Marysville, PA
    • Posts 81

    To the best of my knowledge the checkbox state is working correctly for my purposes.  Sometime next week I should be testing this control with Atlas update panels. I will post back to this thread with my findings.

    You are more than welcome to include the code into the next release. I posted it so others could use it.

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

    09-22-2006, 5:09 AM
    • Member
      25 point Member
    • twad
    • Member since 08-24-2006, 1:00 PM
    • Posts 5

    I do not know if this solves the problem with expanded state being preserved I had to write a ugly hack using a hidden input for each node + some javascript to update it.

    Since this is a dynamic treestructure you could have - on page render:

    - Parent 1
    - Parent 2

    on page postback - after expanding both nodes (without postback) and then having a postback through some other control

    +Parent 1 (expanded)
        -child 1
    +Parent 2 (expanded)
        -child 2

    In the original treeview Parent1.expanded and Parent2.expanded = true, but in the cssfriendly treeview the expanded state equals the original expanded state (false)

    the original treeview stores this information in a long string hiddenfield like this "eeeeeneeen" and have javascript to update it. The current code only expands nodes when it gets an event so if autopostback is disabled the expanded state of nodes is not updatet.
     

  • Re: Treeview expanded state

    09-22-2006, 1:01 PM
    • Contributor
      3,298 point Contributor
    • Russ Helfand
    • Member since 09-14-2005, 6:22 PM
    • Groovybits.com
    • Posts 741

    Hi Twad,

    The fix I posted above (and subsequently modified by Jason) should (I think) reexpand the tree to the selected node regardless of what control (the tree or another control) causes the postback for the page.  Have you tried that?  I'm curious to know if it works adequately for you.

    I know that you are also aiming to reexpand the tree to its exact state of expansion after postback.  The adapter doesn't aim for that.  It only aims to reexpand the tree to expose the selected node.  Neither heuristic is right or wrong, in my opinion.  They are simply different.  The adapter is able to reexpand to the selected node (only) without needing to retain a lot of state (context) info.  I think you discovered this, too.  Personally, I think its better in many cases to reexpand to the selected node after postback rather than retaining lots of state info and trying to completely restore the tree to its exact expansion state... but I'd like your opinions on the importance of these various options.

    Looking forward to hearing from you, best regards,

    Russ Helfand
    Groovybits.com
  • Re: Treeview expanded state

    09-24-2006, 8:13 AM
    • Member
      25 point Member
    • twad
    • Member since 08-24-2006, 1:00 PM
    • Posts 5

    I thought the point of the adapter was to have the same behavior as the original treeview control only with the added benefit of styling using css. So if the original treeview did not have javascript and hidden fields to keep track of expanded state - I would agree that the adapter did not need to implement this.

    Maybe I am misusing the expanded state, but this works in the original treeview control. What I am using it for is to let the user edit which nodes should be expanded and which nodes should not be expanded and save this state in the profile so the user is presented with the same workspace on next visit, and do not need to do the boring task of re-expanding nodes to suit the user on each visit.

    I might agree in principle that a lot of state is bad, but this gives my application a better user experience so I do not see anything wrong with that. If you decide to keep the current implementation of expanded state handling you should have a note in the treeview documentation clearly stating that expanded state is not stored on postback and that the adapter differs in behavior from the original treeview control.

    Anyway thanks for the implementation, it was easy to modify the adapter to create an adapter with css-styling and which also suited my needs for storing expanded state.

     

  • Re: Treeview expanded state

    09-24-2006, 1:52 PM
    • Contributor
      3,298 point Contributor
    • Russ Helfand
    • Member since 09-14-2005, 6:22 PM
    • Groovybits.com
    • Posts 741

    I can appreciate your confusion and frustration.  I can also appreciate the fact that there are plenty of cases where reexpanding the tree to its exact state prior to postback would be a better user experience. As a longer term goal, we can aim for that.

    In the nearer term, however, I tried to set a goal that feels like a fair compromise: the adapters should do the most important things that the unadapted controls do... but do them a way that is more CSS-friendly and uses nice markup. That is why the reexpansion-after-postback mechanism isn't as sophisticated or precise as the unadapted version of the TreeView.  I was trying to keep it simple but reasonably effective for many authors but not put so much emphasis on making it perfect that it would prevent me from working on other parts of the kit that need improvement too.

    These, of course, are decisions that could be debated endlessly.  For example, you may legitimately feel that it is really, really important that the reexpansion heuristic for TreeView mimic much more closely the unadapted version.  Others on this forum would argue that upgrading the kit to be fully compliant to XHTML 1.1 Strict is more important to them.  Others would have the immediate emphasis put on more documentation or more kinds of adapters (covering more controls).  I admit: it's incredibly hard to balance all of these divergent priorities.

    All of this comes out sounding like an excuse.  I guess it is.  I'm trying to explain that, for now, the adapters aren't really meant to be an exact mimic of the unadapted controls... though they are supposed to mimic the behavior in the unadapted controls that most folks find critically important. You've stated a real preference for having us improve and upgrade the precision of the reexpansion heuristic for the TreeView adapter.  That's fair.  I'll definitely add that to my to-do list of things we can research in the future. Meanwhile, perhaps someone else on the forum will contribute code that helps us move in the direction of an improved heuristic for reexpansion more immediately.

    The issue of not wanting to weigh down the page with lots and lots of state info is still valid... though perhaps I'm using it too much as an excuse for not properly reexpanding the tree to its original expansion state after postback! I'll continue to think about how all this might be done without too much extra load, complexity, etc.

    Are you OK with all this? I don't mean to shut you down or to make you feel like I'm not listening.  Mostly I'm trying to react to your statement that you thought that the adapters were supposed to be mimicing all behavior exactly.  That's not really the case at this moment.  It may be the longer-term goal but for now the adapters are meant to present a fair implementation of these controls, though admittedly not perfect.

    Russ Helfand
    Groovybits.com
  • Re: Treeview expanded state

    09-25-2006, 5:29 AM
    • Member
      25 point Member
    • twad
    • Member since 08-24-2006, 1:00 PM
    • Posts 5

    I am not very frustrated really Smile

    I took your adapter added the features I needed in the source and now I use this modified version.

    Trouble with my modified version is that I did not have any use for the event handling so I dropped it, same way you dropped expansion state - so really we are both happy. If I had not found this adapter I would have been forced to create my own control to solve my design problem, which would have been significantly more work.

    I understand your situation and respect your priorites.

  • Re: Treeview expanded state

    09-26-2006, 4:46 PM
    • Member
      60 point Member
    • markcarr
    • Member since 05-19-2006, 10:23 PM
    • Bainbridge Island, WA
    • Posts 12

    I like those changes too. Thanks Jason. I made one modification to your code which stops allows my users to click on the parent node's check box without it forcing an expand/collapse of it's children.  Also, Russ I added a bug fix to respect any tree nodes that are explicitly set as Checkbox=False when the rest of the tree has checkboxes.

     

    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)))
    {
    // added if statement to respect Checked=False attribute on tree with checked=All
    if (((item.ShowCheckBox == null) || (item.ShowCheckBox.Value == true)))
    {
    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");
    }
    // added javascript to check/uncheck children recursively and use cancelbubble to stop expand/collapse when checking box
    if (IsExpandable(item))
    writer.WriteAttribute("onclick", "TreeViewCheck(event);event.cancelBubble=true;");
    else
    writer.WriteAttribute("onclick", "TreeViewCheck(event);");

    writer.Write(HtmlTextWriter.SelfClosingTagEnd);
    _checkboxIndex++;
    }
    }
      
Page 1 of 2 (22 items) 1 2 Next >