File: UI\WebControls\TreeNode.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="TreeNode.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Web.UI.WebControls {
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Web.UI;
    using System.Web.Util;
 
 
    /// <devdoc>
    ///     Provides a tree node for use in the TreeView class
    /// </devdoc>
    [ParseChildren(true, "ChildNodes")]
    public class TreeNode : IStateManager, ICloneable {
        private bool _isTrackingViewState;
        private StateBag _viewState;
 
        private TreeNodeCollection _childNodes;
        private TreeView _owner;
        private TreeNode _parent;
 
        private bool _populateDesired;
        private int _selectDesired;
        private bool _modifyCheckedNodes;
 
        private string _parentIsLast;
        private string _toggleNodeAttributeValue;
 
        private object _dataItem;
 
        private int _index;
 
        private string _valuePath;
        private string _internalValuePath;
        private int _depth = -2;
 
        private bool _isRoot;
 
 
        /// <devdoc>
        ///     Constructs a new TreeNode without a text or value
        /// </devdoc>
        public TreeNode() {
            _selectDesired = 0;
        }
 
 
        /// <devdoc>
        ///     Constructs a new TreeNode with the specified owner TreeView
        /// </devdoc>
        protected internal TreeNode(TreeView owner, bool isRoot) : this() {
            _owner = owner;
            _isRoot = isRoot;
        }
 
 
        /// <devdoc>
        ///     Constructs a new TreeNode with the specified text
        /// </devdoc>
        public TreeNode(string text) : this(text, null, null, null, null) {
        }
 
 
        /// <devdoc>
        ///     Constructs a new TreeNode with the specified text, and value
        /// </devdoc>
        public TreeNode(string text, string value) : this(text, value, null, null, null) {
        }
 
 
        /// <devdoc>
        ///     Constructs a new TreeNode with the specified text, value, and image URL
        /// </devdoc>
        public TreeNode(string text, string value, string imageUrl) : this(text, value, imageUrl, null, null){
        }
 
 
        /// <devdoc>
        ///     Constructs a new TreeNode with the specified text, value, image URL, navigation URL, and target.
        /// </devdoc>
        public TreeNode(string text, string value, string imageUrl, string navigateUrl, string target) : this() {
            if (text != null) {
                Text = text;
            }
 
            if (value != null) {
                Value = value;
            }
 
            if (!String.IsNullOrEmpty(imageUrl)) {
                ImageUrl = imageUrl;
            }
 
            if (!String.IsNullOrEmpty(navigateUrl)) {
                NavigateUrl = navigateUrl;
            }
 
            if (!String.IsNullOrEmpty(target)) {
                Target = target;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the checked state
        /// </devdoc>
        [DefaultValue(false)]
        [WebSysDescription(SR.TreeNode_Checked)]
        public bool Checked {
            get {
                object o = ViewState["Checked"];
                if (o == null) {
                    return false;
                }
                return (bool)o;
            }
            set {
                ViewState["Checked"] = value;
                NotifyOwnerChecked();
            }
        }
 
        /// <devdov>
        /// </devdoc>
        internal bool CheckedSet {
            get {
                return (ViewState["Checked"] != null);
            }
        }
 
 
        /// <devdoc>
        ///     Gets whether this node was creating through databinding
        /// </devdoc>
        [Browsable(false)]
        [DefaultValue(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool DataBound {
            get {
                object o = ViewState["DataBound"];
                if (o == null) {
                    return false;
                }
                return (bool)o;
            }
        }
 
 
        /// <devdoc>
        ///     Gets the collection of children nodes parented to this TreeNode
        /// </devdoc>
        [Browsable(false)]
        [DefaultValue(null)]
        [MergableProperty(false)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public TreeNodeCollection ChildNodes {
            get {
                if (_childNodes == null) {
                    _childNodes = new TreeNodeCollection(this);
                }
                return _childNodes;
            }
        }
 
 
        /// <devdoc>
        ///     Gets path to the data to which this node is bound.
        /// </devdoc>
        [Browsable(false)]
        [DefaultValue("")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public string DataPath {
            get {
                string s = (string)ViewState["DataPath"];
                if (s == null) {
                    return String.Empty;
                }
                return s;
            }
        }
 
 
        /// <devdoc>
        ///     Gets the depth of the tree node.
        /// </devdoc>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Depth {
            get {
                if (_depth == -2) {
                    if (_isRoot) {
                        return -1;
                    }
 
                    if (Parent != null) {
                        _depth = Parent.Depth + 1;
                    }
                    else if (_owner != null) {
                        _depth = InternalValuePath.Split(TreeView.InternalPathSeparator).Length - 1;
                    }
                    else {
                        return 0;
                    }
                }
                return _depth;
            }
        }
 
        /// <devdoc>
        ///     Gets and sets the expand state
        /// </devdoc>
        [DefaultValue(typeof(Nullable<bool>), "")]
        [WebSysDescription(SR.TreeNode_Expanded)]
        public bool? Expanded {
            get {
                object o = ViewState["Expanded"];
                if (o == null) {
                    return null;
                }
                return (bool?)o;
            }
            set {
                bool? oldValue = Expanded;
                // We need to set the viewstate so that it wins over ExpandDepth on the get request (VSWhidbey 331936)
                // N.B. We don't want this to happen when restoring ViewState.
                ViewState["Expanded"] = value;
                if (value != oldValue) {
                    if (_owner != null && _owner.DesignMode) return;
 
                    if (value == true) {
                        if (PopulateOnDemand) {
                            // If the owner isn't set, remember to populate the node when the
                            // owner is determined
                            if (_owner == null) {
                                _populateDesired = true;
                            }
                            // Don't populate when the TreeView is restoring client expand state
                            else if (!_owner.LoadingNodeState) {
                                Populate();
                            }
                        }
 
                        if (_owner != null) {
                            _owner.RaiseTreeNodeExpanded(this);
                        }
                    }
                    else if ((value == false) && (oldValue == true) && (ChildNodes.Count > 0)) {
                        if (_owner != null) {
                            _owner.RaiseTreeNodeCollapsed(this);
                        }
                    }
                }
            }
        }
 
        /// <devdoc>
        ///     Gets the data item for the tree node.
        /// </devdoc>
        [Browsable(false)]
        [DefaultValue(null)]
        public object DataItem {
            get {
                return _dataItem;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the TreeNode ImageToolTip
        /// </devdoc>
        [DefaultValue("")]
        [Localizable(true)]
        [WebSysDescription(SR.TreeNode_ImageToolTip)]
        public string ImageToolTip {
            get {
                string s = (string)ViewState["ImageToolTip"];
 
                if (s == null) {
                    return String.Empty;
                }
 
                return s;
            }
            set {
                ViewState["ImageToolTip"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the image URl to be rendered for this node
        /// </devdoc>
        [DefaultValue("")]
        [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
        [UrlProperty()]
        [WebSysDescription(SR.TreeNode_ImageUrl)]
        public string ImageUrl {
            get {
                string s = (string)ViewState["ImageUrl"];
                if (s == null) {
                    return String.Empty;
                }
                return s;
            }
            set {
                ViewState["ImageUrl"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the unique index for the tree node
        /// </devdoc>
        internal int Index {
            get {
                return _index;
            }
            set {
                _index = value;
            }
        }
       
        // This is the value path that we use internally. It is different from ValuePath
        // because we always use the same separator character and escape it from values.
        internal string InternalValuePath {
            get {
                if (_internalValuePath != null ) {
                    return _internalValuePath;
                }
                if (_parent != null) {
                    // StringBuilder.Insert is expensive, but we need to build starting from the end.
                    // First build a list, then build the string starting from the end of the list.
                    List<string> pathParts = new List<string>();
                    pathParts.Add(TreeView.Escape(Value));
                    TreeNode parent = _parent;
                    while ((parent != null) && !parent._isRoot) {
                        if (parent._internalValuePath != null) {
                            pathParts.Add(parent._internalValuePath);
                            break;
                        }
                        else {
                            pathParts.Add(TreeView.Escape(parent.Value));
                        }
                        parent = parent._parent;
                    }
                    pathParts.Reverse();
                    _internalValuePath = String.Join(TreeView.InternalPathSeparator.ToString(), pathParts.ToArray());
                    return _internalValuePath;
                }
                else {
                    return String.Empty;
                }
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the URL to navigate to when the node is clicked
        /// </devdoc>
        [DefaultValue("")]
        [Editor("System.Web.UI.Design.UrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
        [UrlProperty()]
        [WebSysDescription(SR.TreeNode_NavigateUrl)]
        public string NavigateUrl {
            get {
                string s = (string)ViewState["NavigateUrl"];
                if (s == null) {
                    return String.Empty;
                }
                return s;
            }
            set {
                ViewState["NavigateUrl"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Gets the owner TreeView for this TreeNode, if there is one
        /// </devdoc>
        internal TreeView Owner {
            get {
                return _owner;
            }
        }
 
 
        /// <devdoc>
        ///     Gets the parent TreeNode
        /// </devdoc>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public TreeNode Parent {
            get {
                if ((_parent == null) || _parent._isRoot) {
                    return null;
                }
 
                return _parent;
            }
 
        }
 
 
        /// <devdoc>
        ///     Gets and sets whether node has been populated
        /// </devdoc>
        internal bool Populated {
            get {
                object o = ViewState["Populated"];
                if (o == null) {
                    return false;
                }
                return (bool)o;
            }
            set {
                ViewState["Populated"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Specifies whether the node should have its children populate immediately or
        ///     only when the node is expanded.
        /// </devdoc>
        [DefaultValue(false)]
        [WebSysDescription(SR.TreeNode_PopulateOnDemand)]
        public bool PopulateOnDemand {
            get {
                object o = ViewState["PopulateOnDemand"];
                if (o == null) {
                    return false;
                }
                return (bool)o;
            }
            set {
                ViewState["PopulateOnDemand"] = value;
                if (value && (Expanded == true)) {
                    Expanded = null;
                }
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the PreserveChecked state
        /// </devdoc>
        [DefaultValue(false)]
        internal bool PreserveChecked {
            get {
                object o = ViewState["PreserveChecked"];
                if (o == null) {
                    return false;
                }
                return (bool)o;
            }
            set {
                ViewState["PreserveChecked"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the action which the TreeNode will perform when selected
        /// </devdoc>
        [DefaultValue(TreeNodeSelectAction.Select)]
        [WebSysDescription(SR.TreeNode_SelectAction)]
        public TreeNodeSelectAction SelectAction {
            get {
                object o = ViewState["SelectAction"];
                if (o == null) {
                    return TreeNodeSelectAction.Select;
                }
                return (TreeNodeSelectAction)o;
            }
            set {
                ViewState["SelectAction"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the selected state
        /// </devdoc>
        [DefaultValue(false)]
        [WebSysDescription(SR.TreeNode_Selected)]
        public bool Selected {
            get {
                object o = ViewState["Selected"];
                if (o == null) {
                    return false;
                }
                return (bool)o;
            }
            set {
                SetSelected(value);
                if (_owner == null) {
                    _selectDesired = (value ? +1 : -1);
                    return;
                }
                else if (value) {
                    // Set the TreeView's selected node to this one
                    _owner.SetSelectedNode(this);
                }
                else if (this == _owner.SelectedNode) {
                    _owner.SetSelectedNode(null);
                }
            }
        }
        
        internal string SelectID {
            get {
                if (_owner.ShowExpandCollapse) {
                    return _owner.CreateNodeTextId(Index);
                }
                else {
                    return _owner.CreateNodeId(Index);
                }
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets whether the TreeNode has a CheckBox
        ///     See ShouldSerializeShowCheckBox remarks.
        /// </devdoc>
        [DefaultValue(typeof(Nullable<bool>), "")]
        [WebSysDescription(SR.TreeNode_ShowCheckBox)]
        public bool? ShowCheckBox {
            get {
                object o = ViewState["ShowCheckBox"];
                if (o == null) {
                    return null;
                }
                return (bool?)o;
            }
            set {
                ViewState["ShowCheckBox"] = value;
            }
        }
 
        /// <devdoc>
        ///     Gets and sets the target window that the TreeNode will browse to if selected
        /// </devdoc>
        [DefaultValue("")]
        [WebSysDescription(SR.TreeNode_Target)]
        public string Target {
            get {
                string s = (string)ViewState["Target"];
                if (s == null) {
                    return String.Empty;
                }
                return s;
            }
            set {
                ViewState["Target"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the display text
        /// </devdoc>
        [DefaultValue("")]
        [Localizable(true)]
        [WebSysDescription(SR.TreeNode_Text)]
        public string Text {
            get {
                string s = (string)ViewState["Text"];
                if (s == null) {
                    s = (string)ViewState["Value"];
                    if (s == null) {
                        return String.Empty;
                    }
                }
                return s;
            }
            set {
                ViewState["Text"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the TreeNode tooltip
        /// </devdoc>
        [DefaultValue("")]
        [Localizable(true)]
        [WebSysDescription(SR.TreeNode_ToolTip)]
        public string ToolTip {
            get {
                string s = (string)ViewState["ToolTip"];
                if (s == null) {
                    return String.Empty;
                }
                return s;
            }
            set {
                ViewState["ToolTip"] = value;
            }
        }
 
 
        /// <devdoc>
        ///     Gets and sets the value
        /// </devdoc>
        [DefaultValue("")]
        [Localizable(true)]
        [WebSysDescription(SR.TreeNode_Value)]
        public string Value {
            get {
                string s = (string)ViewState["Value"];
                if (s == null) {
                    s = (string)ViewState["Text"];
                    if (s == null) {
                        return String.Empty;
                    }
                }
                return s;
            }
            set {
                ViewState["Value"] = value;
                ResetValuePathRecursive();
            }
        }
 
 
        /// <devdoc>
        ///     Gets the full path of the TreeNode
        /// </devdoc>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public string ValuePath {
            get {
                if (_valuePath != null) {
                    return _valuePath;
                }
 
                if (_parent != null) {
                    string parentPath = _parent.ValuePath;
                    _valuePath = ((parentPath.Length == 0) && (_parent.Depth == -1)) ?
                        Value : parentPath + _owner.PathSeparator + Value;
                    return _valuePath;
                }
                else if ((Owner != null) && !String.IsNullOrEmpty(InternalValuePath)) {
                    // Reconstruct the value path from the internal value path (callback case, VSWhidbey 340121)
                    string[] splitValuePath = InternalValuePath.Split(TreeView.InternalPathSeparator);
                    for (int i = 0; i < splitValuePath.Length; i++) {
                        splitValuePath[i] = TreeView.UnEscape(splitValuePath[i]);
                    }
                    _valuePath = String.Join(Owner.PathSeparator.ToString(), splitValuePath);
                    return _valuePath;
                }
                else {
                    return String.Empty;
                }
            }
        }
 
 
        /// <devdoc>
        ///     The state for this TreeNode
        /// </devdoc>
        private StateBag ViewState {
            get {
                if (_viewState == null) {
                    _viewState = new StateBag();
                    if (_isTrackingViewState) {
                        ((IStateManager)_viewState).TrackViewState();
                    }
                }
                return _viewState;
            }
        }
 
        private void ApplyAttributeList(HtmlTextWriter writer, ArrayList list) {
            for (int i = 0; i < list.Count; i += 2) {
                object o = list[i];
                if (o is string) {
                    writer.AddAttribute((string)o, (string)list[i + 1]);
                }
                else {
                    writer.AddAttribute((HtmlTextWriterAttribute)o, (string)list[i + 1]);
                }
            }
        }
 
 
        /// <devdoc>
        ///     Collapses the current node
        /// </devdoc>
        public void Collapse() {
            Expanded = false;
        }
 
 
        /// <devdoc>
        ///     Collapse the current node and all children recursively
        /// </devdoc>
        public void CollapseAll() {
            SetExpandedRecursive(false);
        }
 
 
        /// <devdoc>
        ///     Expands the current node
        /// </devdoc>
        public void Expand() {
            Expanded = true;
        }
 
 
        /// <devdoc>
        ///     Expands the current node and all children recursively
        /// </devdoc>
        public void ExpandAll() {
            SetExpandedRecursive(true);
        }
 
        internal TreeNode GetParentInternal() {
            return _parent;
        }
 
 
        /// <devdoc>
        ///     Adds the href javascript for doing client-side node population
        /// </devdoc>
        private string GetPopulateNodeAttribute(HtmlTextWriter writer, string myId, string selectId, string selectImageId, string lineType, int depth, bool[] isLast) {
            string populateNodeAttributeValue = String.Empty;
 
            // In a callback population scenario, we need to know which node is last in
            // order to render lines or childNodesPadding properly
            if (_parentIsLast == null) {
                char[] parentIsLast = new char[depth + 1];
                for (int i = 0; i < depth + 1; i++) {
                    if (isLast[i]) {
                        parentIsLast[i] = 't';
                    }
                    else {
                        parentIsLast[i] = 'f';
                    }
                }
                _parentIsLast = new string(parentIsLast);
            }
 
            // function populateNode(data,index,node,lineType,text,path,databound,datapath)
            string indexString = Index.ToString(CultureInfo.InvariantCulture);
            if (_owner.IsNotIE) {
                populateNodeAttributeValue = "javascript:TreeView_PopulateNode(" +
                                             _owner.ClientDataObjectID + "," +
                                             indexString + "," +
                                             "document.getElementById('" + myId + "')," +
                                             "document.getElementById('" + selectId + "')," +
                                             ((selectImageId.Length == 0) ? "null" : ("document.getElementById('" + selectImageId + "')")) + "," +
                                             "'" + lineType + "'," +
                                             "'" + Util.QuoteJScriptString(Text, true) + "'," +
                                             "'" + Util.QuoteJScriptString(InternalValuePath, true) + "'," +
                                             "'" + (DataBound ? 't' : 'f') + "'," +
                                             "'" + Util.QuoteJScriptString(DataPath, true) + "','" +
                                             _parentIsLast + "')";
            }
            else {
                populateNodeAttributeValue = "javascript:TreeView_PopulateNode(" +
                                             _owner.ClientDataObjectID + "," +
                                             indexString + "," +
                                             myId + "," +
                                             selectId + "," +
                                             ((selectImageId.Length == 0) ? "null" : selectImageId) + "," +
                                             "'" + lineType + "'," +
                                             "'" + Util.QuoteJScriptString(Text, true) + "'," +
                                             "'" + Util.QuoteJScriptString(InternalValuePath, true) + "'," +
                                             "'" + (DataBound ? 't' : 'f') + "'," +
                                             "'" + Util.QuoteJScriptString(DataPath, true) + "','" +
                                             _parentIsLast + "')";
            }
            if (_owner.Page != null) {
                _owner.Page.ClientScript.RegisterForEventValidation(_owner.UniqueID,
                    String.Concat(indexString, Text, InternalValuePath, DataPath));
            }
 
            return populateNodeAttributeValue;
        }
 
        internal bool GetEffectiveShowCheckBox() {
            return GetEffectiveShowCheckBox(GetTreeNodeType());
        }
 
        private bool GetEffectiveShowCheckBox(TreeNodeTypes type) {
            if (ShowCheckBox == true) {
                return true;
            }
            if (ShowCheckBox == false) {
                return false;
            }
            return ((_owner.ShowCheckBoxes & type) != 0);
        }
 
        /// <devdoc>
        ///     Adds the href javascript for doing client-side node expand state toggling
        /// </devdoc>
        private string GetToggleNodeAttributeValue(string myId, string lineType) {
            Debug.Assert(ChildNodes.Count > 0, "No nodes for expansion, why are we rendering an expander?");
            if (_toggleNodeAttributeValue == null) {
                if (_owner.IsNotIE) {
                    _toggleNodeAttributeValue = "javascript:TreeView_ToggleNode(" +
                                                _owner.ClientDataObjectID + "," +
                                                Index.ToString(CultureInfo.InvariantCulture) + "," +
                                                "document.getElementById('" + myId + "')," +
                                                "'" + lineType + "'," +
                                                "document.getElementById('" + myId + "Nodes'))";
                }
                else {
                    _toggleNodeAttributeValue = "javascript:TreeView_ToggleNode(" +
                                                _owner.ClientDataObjectID + "," +
                                                Index.ToString(CultureInfo.InvariantCulture) + "," +
                                                myId + "," +
                                                "'" + lineType + "'," +
                                                myId + "Nodes)";
                }
            }
 
            return _toggleNodeAttributeValue;
        }
 
        private TreeNodeTypes GetTreeNodeType() {
            TreeNodeTypes type = TreeNodeTypes.Leaf;
            if ((Depth == 0) && (ChildNodes.Count > 0)) {
                type = TreeNodeTypes.Root;
            }
            else if ((ChildNodes.Count > 0) || PopulateOnDemand) {
                type = TreeNodeTypes.Parent;
            }
            return type;
        }
 
        private void NotifyOwnerChecked() {
            if (_owner == null) {
                _modifyCheckedNodes = true;
            }
            else {
                object o = ViewState["Checked"];
                if ((o != null) && ((bool)o == true)) {
                    TreeNodeCollection checkedNodes = _owner.CheckedNodes;
                    if (!checkedNodes.Contains(this)) {
                        _owner.CheckedNodes.Add(this);
                    }
                }
                else {
                    _owner.CheckedNodes.Remove(this);
                }
            }
        }
 
 
        /// <devdoc>
        ///     Fills in the children for the tree node.
        /// </devdoc>
        internal void Populate() {
            if (!Populated && (ChildNodes.Count == 0)) {
                if (_owner != null) {
                    _owner.PopulateNode(this);
                }
                // If the owner hasn't been determined yet, remember that we need to populate this node
                // when the owner actually does get set
                else {
                    _populateDesired = true;
                }
            }
        }
 
 
        /// <devdoc>
        ///     Renders the contents of the node and its children.  It uses the position and isLast parameters
        ///     to determine which lines and which kind of lines to render.
        /// </devdoc>
        internal void Render(HtmlTextWriter writer, int position, bool[] isLast, bool enabled) {
            string myId = String.Empty;
            Debug.Assert(Index != -1, "Didn't assign an index to a node.");
            myId = _owner.CreateNodeId(Index);
 
            int depth = Depth;
            bool last = false;
            if (depth > -1) {
                last = isLast[depth];
            }
 
            bool expanded = (Expanded == true);
 
            TreeNodeStyle mergedStyle = _owner.GetStyle(this);
 
            // <table cellpadding="0" cellspacing="0" border="0" height="nodespacing">
            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
            writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "0");
            writer.RenderBeginTag(HtmlTextWriterTag.Table);
 
            // Don't render the top spacing if this is the first root node
            if ((mergedStyle != null) && !mergedStyle.NodeSpacing.IsEmpty && ((depth != 0) || (position != 0))) {
                writer.AddStyleAttribute(HtmlTextWriterStyle.Height,
                    mergedStyle.NodeSpacing.ToString(CultureInfo.InvariantCulture));
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
                writer.RenderEndTag();
                writer.RenderEndTag();
            }
            // <tr>
            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
 
            // If this is not a root node, fill in the lines or indent space
            if (depth > 0) {
                for (int i = 0; i < depth; i++) {
                    if (writer is Html32TextWriter) {
                        // <td>
                        writer.RenderBeginTag(HtmlTextWriterTag.Td);
                        writer.AddStyleAttribute(HtmlTextWriterStyle.Width, _owner.NodeIndent.ToString(CultureInfo.InvariantCulture) + "px");
                        writer.RenderBeginTag(HtmlTextWriterTag.Table);
                        writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                        writer.RenderBeginTag(HtmlTextWriterTag.Td);
                        if (_owner.ShowLines && !isLast[i]) {
                            writer.AddAttribute(HtmlTextWriterAttribute.Src, _owner.GetImageUrl(TreeView.IImageIndex));
                            writer.AddAttribute(HtmlTextWriterAttribute.Alt, String.Empty);
                            writer.RenderBeginTag(HtmlTextWriterTag.Img);
                            writer.RenderEndTag();
                        }
                        writer.RenderEndTag();
                        writer.RenderEndTag();
                        writer.RenderEndTag();
                        // </td>
                        writer.RenderEndTag();
                    }
                    else {
                        // <td>
                        writer.RenderBeginTag(HtmlTextWriterTag.Td);
                        writer.Write("<div style=\"width:" + _owner.NodeIndent.ToString(CultureInfo.InvariantCulture) + "px;height:1px\">");
                        if (_owner.ShowLines && !isLast[i]) {
                            writer.AddAttribute(HtmlTextWriterAttribute.Src, _owner.GetImageUrl(TreeView.IImageIndex));
                            writer.AddAttribute(HtmlTextWriterAttribute.Alt, String.Empty);
                            writer.RenderBeginTag(HtmlTextWriterTag.Img);
                            writer.RenderEndTag();
                        }
                        writer.Write("</div>");
                        // </td>
                        writer.RenderEndTag();
                    }
                }
            }
 
            // A node can expand if its populate on demand or if it has children
            bool canExpand = (PopulateOnDemand || (ChildNodes.Count > 0)) && _owner.ShowExpandCollapse;
 
            string imageUrl = String.Empty;
            string lineType = " ";
            string imageToolTip = String.Empty;
            if (last) {
                if (canExpand) {
                    if (expanded) {
                        if (_owner.ShowLines) {
                            if (depth == 0) {
                                if (position == 0) {
                                    // The first and only root node
                                    lineType = "-";
                                    imageUrl = _owner.GetImageUrl(TreeView.DashMinusImageIndex);
                                    imageToolTip = _owner.CollapseImageToolTip;
                                }
                                else {
                                    // The last root node
                                    lineType = "l";
                                    imageUrl = _owner.GetImageUrl(TreeView.LMinusImageIndex);
                                    imageToolTip = _owner.CollapseImageToolTip;
                                }
                            }
                            else {
                                // The last node in a set of siblings
                                lineType = "l";
                                imageUrl = _owner.GetImageUrl(TreeView.LMinusImageIndex);
                                imageToolTip = _owner.CollapseImageToolTip;
                            }
                        }
                        else {
                            imageUrl = _owner.GetImageUrl(TreeView.MinusImageIndex);
                            imageToolTip = _owner.CollapseImageToolTip;
                        }
                    }
                    else {
                        if (_owner.ShowLines) {
                            if (depth == 0) {
                                if (position == 0) {
                                    // The first and only root node
                                    lineType = "-";
                                    imageUrl = _owner.GetImageUrl(TreeView.DashPlusImageIndex);
                                    imageToolTip = _owner.ExpandImageToolTip;
                                }
                                else {
                                    // The last root node
                                    lineType = "l";
                                    imageUrl = _owner.GetImageUrl(TreeView.LPlusImageIndex);
                                    imageToolTip = _owner.ExpandImageToolTip;
                                }
                            }
                            else {
                                // The last node in a set of sibling
                                lineType = "l";
                                imageUrl = _owner.GetImageUrl(TreeView.LPlusImageIndex);
                                imageToolTip = _owner.ExpandImageToolTip;
                            }
                        }
                        else {
                            imageUrl = _owner.GetImageUrl(TreeView.PlusImageIndex);
                            imageToolTip = _owner.ExpandImageToolTip;
                        }
                    }
                }
                else {
                    if (_owner.ShowLines) {
                        if (depth == 0) {
                            if (position == 0) {
                                // The first and only node, no children
                                lineType = "-";
                                imageUrl = _owner.GetImageUrl(TreeView.DashImageIndex);
                            }
                            else {
                                // The last root node, no children
                                lineType = "l";
                                imageUrl = _owner.GetImageUrl(TreeView.LImageIndex);
                            }
                        }
                        else {
                            // The last node in a set of siblings, no children
                            lineType = "l";
                            imageUrl = _owner.GetImageUrl(TreeView.LImageIndex);
                        }
                    }
                    else if (_owner.ShowExpandCollapse) {
                        imageUrl = _owner.GetImageUrl(TreeView.NoExpandImageIndex);
                    }
                }
            }
            else {
                if (canExpand) {
                    if (expanded) {
                        if (_owner.ShowLines) {
                            if (depth == 0) {
                                if (position == 0) {
                                    // The first root node
                                    lineType = "r";
                                    imageUrl = _owner.GetImageUrl(TreeView.RMinusImageIndex);
                                    imageToolTip = _owner.CollapseImageToolTip;
                                }
                                else {
                                    // A middle root node
                                    lineType = "t";
                                    imageUrl = _owner.GetImageUrl(TreeView.TMinusImageIndex);
                                    imageToolTip = _owner.CollapseImageToolTip;
                                }
                            }
                            else {
                                // A middle node
                                lineType = "t";
                                imageUrl = _owner.GetImageUrl(TreeView.TMinusImageIndex);
                                imageToolTip = _owner.CollapseImageToolTip;
                            }
                        }
                        else {
                            imageUrl = _owner.GetImageUrl(TreeView.MinusImageIndex);
                            imageToolTip = _owner.CollapseImageToolTip;
                        }
                    }
                    else {
                        if (_owner.ShowLines) {
                            if (depth == 0) {
                                if (position == 0) {
                                    // The first root node
                                    lineType = "r";
                                    imageUrl = _owner.GetImageUrl(TreeView.RPlusImageIndex);
                                    imageToolTip = _owner.ExpandImageToolTip;
                                }
                                else {
                                    // A middle root node
                                    lineType = "t";
                                    imageUrl = _owner.GetImageUrl(TreeView.TPlusImageIndex);
                                    imageToolTip = _owner.ExpandImageToolTip;
                                }
                            }
                            else {
                                // A middle node
                                lineType = "t";
                                imageUrl = _owner.GetImageUrl(TreeView.TPlusImageIndex);
                                imageToolTip = _owner.ExpandImageToolTip;
                            }
                        }
                        else {
                            imageUrl = _owner.GetImageUrl(TreeView.PlusImageIndex);
                            imageToolTip = _owner.ExpandImageToolTip;
                        }
                    }
                }
                else {
                    if (_owner.ShowLines) {
                        if (depth == 0) {
                            if (position == 0) {
                                // The first root node, no children
                                lineType = "r";
                                imageUrl = _owner.GetImageUrl(TreeView.RImageIndex);
                            }
                            else {
                                // A middle root node, no children
                                lineType = "t";
                                imageUrl = _owner.GetImageUrl(TreeView.TImageIndex);
                            }
                        }
                        else {
                            // A middle node, no children
                            lineType = "t";
                            imageUrl = _owner.GetImageUrl(TreeView.TImageIndex);
                        }
                    }
                    else if (_owner.ShowExpandCollapse) {
                        imageUrl = _owner.GetImageUrl(TreeView.NoExpandImageIndex);
                    }
                }
            }
 
            TreeNodeTypes type = GetTreeNodeType();
 
            // Figure out the proper node icon
            string nodeImageUrl = String.Empty;
 
            if (ImageUrl.Length > 0) {
                nodeImageUrl = _owner.ResolveClientUrl(ImageUrl);
            }
            else {
                if ((depth < _owner.LevelStyles.Count) && (_owner.LevelStyles[depth] != null) && mergedStyle.ImageUrl.Length > 0) {
                    nodeImageUrl = _owner.GetLevelImageUrl(depth);
                }
                else {
                    switch (type) {
                        case TreeNodeTypes.Root:
                            nodeImageUrl = _owner.GetImageUrl(TreeView.RootImageIndex);
                            break;
                        case TreeNodeTypes.Parent:
                            nodeImageUrl = _owner.GetImageUrl(TreeView.ParentImageIndex);
                            break;
                        case TreeNodeTypes.Leaf:
                            nodeImageUrl = _owner.GetImageUrl(TreeView.LeafImageIndex);
                            break;
                    }
                }
            }
 
            string selectImageId = String.Empty;
            if (nodeImageUrl.Length > 0) {
                selectImageId = SelectID + "i";
            }
 
            if (imageUrl.Length > 0) {
                // <td>
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
 
                if (canExpand) {
                    // If we are using client script and there aren't any expand/collapse handlers attached, do all the toggling/populating
                    if (_owner.RenderClientScript && !_owner.CustomExpandCollapseHandlerExists) {
                        writer.AddAttribute(HtmlTextWriterAttribute.Id, myId);
 
                        if (PopulateOnDemand) {
                            // If we are populating from the client, add all the needed attributes and call the client script needed to make the callback
                            if (_owner.PopulateNodesFromClient) {
                                if (ChildNodes.Count != 0) {
                                    throw new InvalidOperationException(SR.GetString(SR.TreeView_PopulateOnlyEmptyNodes, _owner.ID));
                                }
                                writer.AddAttribute(HtmlTextWriterAttribute.Href, GetPopulateNodeAttribute(writer, myId, SelectID, selectImageId, lineType, depth, isLast));
                            }
                            // If we aren't populating from the client, make a postback to do the population
                            else {
                                string href = "javascript:0";
                                if (_owner.Page != null) {
                                    href = _owner.Page.ClientScript.GetPostBackClientHyperlink(_owner,
                                        "t" + InternalValuePath, true, true);
                                }
                                writer.AddAttribute(HtmlTextWriterAttribute.Href, href);
                            }
                        }
                        else {
                            writer.AddAttribute(HtmlTextWriterAttribute.Href, GetToggleNodeAttributeValue(myId, lineType));
                        }
                    }
                    else {
                        string href = "javascript:0";
                        if (_owner.Page != null) {
                            href = _owner.Page.ClientScript.GetPostBackClientHyperlink(_owner, "t" + InternalValuePath, true);
                        }
                        // If we aren't using client script to perform expansions, get a postback reference
                        writer.AddAttribute(HtmlTextWriterAttribute.Href, href);
                    }
 
                    if (enabled == true) {
                        // <a href=href>
                        writer.RenderBeginTag(HtmlTextWriterTag.A);
                    }
 
                    writer.AddAttribute(HtmlTextWriterAttribute.Src, imageUrl);
                    writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "0");
                    if (imageToolTip.Length > 0) {
                        writer.AddAttribute(HtmlTextWriterAttribute.Alt,
                            String.Format(CultureInfo.CurrentCulture, imageToolTip, Text));
			            //fix bug 1197460, quirk it so the fix will only be enabled on projects on 4.6.1 or later version of framework
			            if (BinaryCompatibility.Current.TargetsAtLeastFramework461) {
                            writer.AddAttribute(HtmlTextWriterAttribute.Title,
                                String.Format(CultureInfo.CurrentCulture, imageToolTip, Text));
			            }
                    }
                    else {
                        writer.AddAttribute(HtmlTextWriterAttribute.Alt, String.Empty);
			            //fix bug 1197460, quirk it so the fix will only be enabled on projects on 4.6.1 or later version of framework
			            if (BinaryCompatibility.Current.TargetsAtLeastFramework461) {
                        	writer.AddAttribute(HtmlTextWriterAttribute.Title, String.Empty);
			            }
                    }
                    writer.RenderBeginTag(HtmlTextWriterTag.Img);
                    writer.RenderEndTag();
 
                    if (enabled == true) {
                        // </a>
                        writer.RenderEndTag();
                    }
                }
                else {
                    writer.AddAttribute(HtmlTextWriterAttribute.Src, imageUrl);
                    writer.AddAttribute(HtmlTextWriterAttribute.Alt, String.Empty);
                    writer.RenderBeginTag(HtmlTextWriterTag.Img);
                    writer.RenderEndTag();
                }
 
                // </td>
                writer.RenderEndTag();
            }
 
            // Since we need the same set of anchor attributes on both the image and the text
            // accumulate a list here first
            ArrayList anchorAttributes = new ArrayList();
            // If there is a navigation url on this node, set up the navigation stuff
            if (NavigateUrl.Length > 0) {
                // writer.AddAttribute(HtmlTextWriterAttribute.Href, _owner.ResolveClientUrl(NavigateUrl));
                anchorAttributes.Add(HtmlTextWriterAttribute.Href);
                anchorAttributes.Add(_owner.ResolveClientUrl(NavigateUrl));
 
                // Use the TreeNode Target if it has one, the TreeView's if it doesn't
                string target = ViewState["Target"] as string;
                if (target == null) {
                    target = _owner.Target;
                }
 
                if (target.Length > 0) {
                    // writer.AddAttribute(HtmlTextWriterAttribute.Target, Target);
                    anchorAttributes.Add(HtmlTextWriterAttribute.Target);
                    anchorAttributes.Add(target);
 
                    if (_owner.RenderClientScript) {
                        // Use client-script to merge in the selected node style
                        string onClick = String.Empty;
                        if (_owner.Page != null && _owner.Page.SupportsStyleSheets &&
                            (SelectAction == TreeNodeSelectAction.Select) || (SelectAction == TreeNodeSelectAction.SelectExpand)) {
 
                            onClick = Util.MergeScript(onClick,
                                "TreeView_SelectNode(" + _owner.ClientDataObjectID + 
                                    ", this,'" + SelectID + "');");
                        }
 
                        if ((SelectAction == TreeNodeSelectAction.Expand) || (SelectAction == TreeNodeSelectAction.SelectExpand)) {
                            if (PopulateOnDemand) {
                                // Postback to populate
                                // 
                                onClick = Util.MergeScript(onClick,
                                    _owner.Page.ClientScript.GetPostBackClientHyperlink(_owner, "t" + InternalValuePath, true, true));
                            }
                            else if (!_owner.CustomExpandCollapseHandlerExists && canExpand) {
                                onClick = Util.MergeScript(onClick, GetToggleNodeAttributeValue(myId, lineType));
                            }
                        }
 
                        // writer.AddAttribute("onclick", onClick);
                        if (onClick.Length != 0) {
                            anchorAttributes.Add("onclick");
                            anchorAttributes.Add(onClick);
                        }
                    }
                }
            }
            // Otherwise, write out a postback that will select the node
            else {
                // If client script is on, call the proper javascript and the select action is expand
                if (_owner.RenderClientScript && (SelectAction == TreeNodeSelectAction.Expand) && !_owner.CustomExpandCollapseHandlerExists) {
                    // and if the node is being populated on demand, and we are populating nodes from the client, call the populateNode javascript
                    if (PopulateOnDemand) {
                        if (_owner.PopulateNodesFromClient) {
                            // writer.AddAttribute(HtmlTextWriterAttribute.Href, GetPopulateNodeAttribute(writer, myId, SelectID, lineType, depth, isLast));
                            anchorAttributes.Add(HtmlTextWriterAttribute.Href);
                            anchorAttributes.Add(GetPopulateNodeAttribute(writer, myId, SelectID, selectImageId, lineType, depth, isLast));
                        }
                        else {
                            // If we're not populating from the client, postback to populate
                            // writer.AddAttribute(HtmlTextWriterAttribute.Href, _owner.Page.GetPostBackClientHyperlink(_owner, "t" + HttpUtility.HtmlEncode(InternalValuePath), true));
                            anchorAttributes.Add(HtmlTextWriterAttribute.Href);
 
                            string href = "javascript:0";
                            if (_owner.Page != null) {
                                href = _owner.Page.ClientScript.GetPostBackClientHyperlink(
                                    _owner, "t" + InternalValuePath, true, true);
                            }
                            anchorAttributes.Add(href);
                        }
                    }
                    else if (canExpand) {
                        // writer.AddAttribute(HtmlTextWriterAttribute.Href, GetToggleNodeAttributeValue(myId, lineType));
                        anchorAttributes.Add(HtmlTextWriterAttribute.Href);
                        anchorAttributes.Add(GetToggleNodeAttributeValue(myId, lineType));
                    }
                }
                // If not, just render an href for a postback
                else if (SelectAction != TreeNodeSelectAction.None) {
                    // writer.AddAttribute(HtmlTextWriterAttribute.Href, _owner.Page.GetPostBackClientHyperlink(_owner, "s" + HttpUtility.HtmlEncode(InternalValuePath), true));
                    anchorAttributes.Add(HtmlTextWriterAttribute.Href);
 
                    if (_owner.Page != null) {
                        string href = _owner.Page.ClientScript.GetPostBackClientHyperlink(
                            _owner, "s" + InternalValuePath, true, true);
                        anchorAttributes.Add(href);
 
                        if (_owner.RenderClientScript) {
                            anchorAttributes.Add("onclick");
                            anchorAttributes.Add("TreeView_SelectNode(" + _owner.ClientDataObjectID + 
                                ", this,'" + SelectID + "');");
                        }
                    }
                    else {
                        anchorAttributes.Add("javascript:0");
                    }
                }
            }
 
            if (ToolTip.Length > 0) {
                anchorAttributes.Add(HtmlTextWriterAttribute.Title);
                anchorAttributes.Add(ToolTip);
            }
 
            // Render out the node icon, if it is set
            if (nodeImageUrl.Length > 0) {
                // <td>
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
                // <a>
                ApplyAttributeList(writer, anchorAttributes);
 
                // Set the id of the text hyperlink
                writer.AddAttribute(HtmlTextWriterAttribute.Id, selectImageId);
 
                if (enabled == true && SelectAction != TreeNodeSelectAction.None) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Tabindex, "-1");
                    writer.RenderBeginTag(HtmlTextWriterTag.A);
                }
                writer.AddAttribute(HtmlTextWriterAttribute.Src, nodeImageUrl);
                writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "0");
                if (ImageToolTip.Length > 0) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Alt, ImageToolTip);
		            //fix bug 1197460, quirk it so the fix will only be enabled on projects on 4.6.1 or later version of framework
	                if (BinaryCompatibility.Current.TargetsAtLeastFramework461) {
                        writer.AddAttribute(HtmlTextWriterAttribute.Title, ImageToolTip);
		            }
                }
                else {
                    writer.AddAttribute(HtmlTextWriterAttribute.Alt, String.Empty);
		    	    //fix bug 1197460, quirk it so the fix will only be enabled on projects on 4.6.1 or later version of framework
			        if (BinaryCompatibility.Current.TargetsAtLeastFramework461) {
	                    writer.AddAttribute(HtmlTextWriterAttribute.Title, String.Empty);
			        }
                }
                writer.RenderBeginTag(HtmlTextWriterTag.Img);
                writer.RenderEndTag();
                if (enabled == true && SelectAction != TreeNodeSelectAction.None) {
                    // </a>
                    writer.RenderEndTag();
                }
                // </td>
                writer.RenderEndTag();
            }
 
            // <td nowrap="nowrap">
            if (!_owner.NodeWrap) {
                writer.AddStyleAttribute(HtmlTextWriterStyle.WhiteSpace, "nowrap");
            }
 
            if (_owner.Page != null && _owner.Page.SupportsStyleSheets) {
                string styleClass = _owner.GetCssClassName(this, false);
                if (styleClass.Trim().Length > 0) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Class, styleClass);
                }
            }
            else {
                if (mergedStyle != null) {
                    mergedStyle.AddAttributesToRender(writer);
                }
            }
 
            if (_owner.EnableHover && (SelectAction != TreeNodeSelectAction.None)) {
                writer.AddAttribute("onmouseover", "TreeView_HoverNode(" + _owner.ClientDataObjectID + ", this)");
                writer.AddAttribute("onmouseout", "TreeView_UnhoverNode(this)");
            }
 
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
 
            // Render a check box if we need to
            if (GetEffectiveShowCheckBox(type)) {
                writer.AddAttribute(HtmlTextWriterAttribute.Type, "checkbox");
                string id = myId + "CheckBox";
                writer.AddAttribute(HtmlTextWriterAttribute.Name, id);
                writer.AddAttribute(HtmlTextWriterAttribute.Id, id);  // VSWhidbey 497326: Render id for easier client-side scripting
                if (Checked) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Checked, "checked");
                }
                if (enabled == false) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled");
                    if (!_owner.Enabled && (_owner.RenderingCompatibility >= VersionUtil.Framework40) 
                        && !String.IsNullOrEmpty(WebControl.DisabledCssClass)) {
                        writer.AddAttribute(HtmlTextWriterAttribute.Class, WebControl.DisabledCssClass);
                    }
                }
 
                if (ToolTip.Length > 0) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Title, ToolTip);
                }
                writer.RenderBeginTag(HtmlTextWriterTag.Input);
                writer.RenderEndTag();
            }
 
            // Rendering hook for extended treeviews
            RenderPreText(writer);
            if (_owner.Page != null && _owner.Page.SupportsStyleSheets) {
                bool renderInlineBorder;
                string styleClass = _owner.GetCssClassName(this, true, out renderInlineBorder);
                if (styleClass.Trim().Length > 0) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Class, styleClass);
                    if (renderInlineBorder) {
                        // Add inline style to force the border to none to override any CssClass (VSWhidbey 336610)
                        writer.AddStyleAttribute(HtmlTextWriterStyle.BorderStyle, "none");
                        // And an inline font-size of 1em to avoid squaring relative font sizes by applying them twice
                        writer.AddStyleAttribute(HtmlTextWriterStyle.FontSize, "1em");
                    }
                }
            }
            else {
                if (mergedStyle != null) {
                    mergedStyle.HyperLinkStyle.AddAttributesToRender(writer);
                }
            }
 
            ApplyAttributeList(writer, anchorAttributes);
            writer.AddAttribute(HtmlTextWriterAttribute.Id, SelectID);
 
            if ((SelectAction == TreeNodeSelectAction.None) || !enabled) {
                // Render a span so that the styles get applied, with the ID so that POD works
                writer.RenderBeginTag(HtmlTextWriterTag.Span);
                // Render plain text if the select action was none
                writer.Write(Text);
                writer.RenderEndTag();
            }
            else {
                // AccessKey
                if (!_owner.AccessKeyRendered && _owner.AccessKey.Length != 0) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Accesskey, _owner.AccessKey, true);
                    _owner.AccessKeyRendered = true;
                }
                // <a href=href>
                writer.RenderBeginTag(HtmlTextWriterTag.A);
                writer.Write(Text);
                writer.RenderEndTag(); // </a>
            }
            // Rendering hook for extended treeviews
            RenderPostText(writer);
 
            // </td>
            writer.RenderEndTag();
 
            // </tr>
            writer.RenderEndTag();
 
            if ((mergedStyle != null) && !mergedStyle.NodeSpacing.IsEmpty) {
                writer.AddStyleAttribute(HtmlTextWriterStyle.Height, mergedStyle.NodeSpacing.ToString(CultureInfo.InvariantCulture));
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
                writer.RenderEndTag();
                writer.RenderEndTag();
            }
 
            // </table>
            writer.RenderEndTag();
 
            // If children exist, maybe render them
            if (ChildNodes.Count > 0) {
                if (isLast.Length < depth + 2) {
                    bool[] newIsLast = new bool[depth + 5];
                    Array.Copy(isLast, 0, newIsLast, 0, isLast.Length);
                    isLast = newIsLast;
                }
 
                // If client script is enabled, always render the child nodes, and also render a div around the child nodes, with a 'display' style
                if (_owner.RenderClientScript) {
                    if (!expanded) {
                        writer.AddStyleAttribute("display", "none");
                    }
                    else {
                        writer.AddStyleAttribute("display", "block");
                    }
                    writer.AddAttribute(HtmlTextWriterAttribute.Id, myId + "Nodes");
                    writer.RenderBeginTag(HtmlTextWriterTag.Div);
                    RenderChildNodes(writer, depth, isLast, enabled);
                    writer.RenderEndTag();
                }
                // If client script is not enabled, only render the children if this node is expanded
                else {
                    if (expanded) {
                        RenderChildNodes(writer, depth, isLast, enabled);
                    }
                }
            }
        }
 
 
        /// <devdoc>
        ///     Renders the children nodes of the TreeNode
        /// </devdoc>
        internal void RenderChildNodes(HtmlTextWriter writer, int depth, bool[] isLast, bool enabled) {
            TreeNodeStyle mergedStyle = _owner.GetStyle(this);
            // Render the child nodes padding
            if (!mergedStyle.ChildNodesPadding.IsEmpty) {
                writer.AddAttribute(HtmlTextWriterAttribute.Height, mergedStyle.ChildNodesPadding.ToString(CultureInfo.InvariantCulture));
                writer.RenderBeginTag(HtmlTextWriterTag.Table);
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
                writer.RenderEndTag();
                writer.RenderEndTag();
                writer.RenderEndTag();
            }
 
            for (int i = 0; i < ChildNodes.Count; i++) {
                TreeNode node = ChildNodes[i];
                isLast[depth + 1] = (i == ChildNodes.Count - 1);
                node.Render(writer, i, isLast, enabled);
            }
 
            // Render the child nodes padding only if there is another sibling node
            if (!isLast[depth] && !mergedStyle.ChildNodesPadding.IsEmpty) {
                writer.AddAttribute(HtmlTextWriterAttribute.Height, mergedStyle.ChildNodesPadding.ToString(CultureInfo.InvariantCulture));
                writer.RenderBeginTag(HtmlTextWriterTag.Table);
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
                writer.RenderEndTag();
                writer.RenderEndTag();
                writer.RenderEndTag();
            }
        }
 
        protected virtual void RenderPostText(HtmlTextWriter writer) {
        }
 
        protected virtual void RenderPreText(HtmlTextWriter writer) {
        }
 
        internal void ResetValuePathRecursive() {
            if (_valuePath != null) {
                _valuePath = null;
                foreach (TreeNode child in ChildNodes) {
                    child.ResetValuePathRecursive();
                }
            }
        }
 
 
        /// <devdoc>
        ///     Selects the TreeNode
        /// </devdoc>
        public void Select() {
            Selected = true;
        }
 
 
        /// <devdoc>
        ///     Marks this node as a databound node
        /// </devdoc>
        internal void SetDataBound(bool dataBound) {
            ViewState["DataBound"] = dataBound;
        }
 
 
        /// <devdoc>
        ///     Used by ExpandAll and CollapseAll to recusively set all nodes' Expanded property to the specified value
        /// </devdoc>
        private void SetExpandedRecursive(bool value) {
            Expanded = value;
            if (ChildNodes.Count > 0) {
                for (int i = 0; i < ChildNodes.Count; i++) {
                    ChildNodes[i].SetExpandedRecursive(value);
                }
            }
        }
 
 
        /// <devdoc>
        ///     Sets the data item for use by the user in databinding
        /// </devdoc>
        internal void SetDataItem(object dataItem) {
            _dataItem = dataItem;
        }
 
        /// <devdoc>
        ///     Sets the data path for use by the TreeView in databinding
        /// </devdoc>
        internal void SetDataPath(string dataPath) {
            ViewState["DataPath"] = dataPath;
        }
 
        internal void SetDirty() {
            ViewState.SetDirty(true);
 
            if (ChildNodes.Count > 0) {
                ChildNodes.SetDirty();
            }
        }
 
 
        /// <devdoc>
        ///     Sets the owner TreeView of this node.
        /// </devdoc>
        internal void SetOwner(TreeView owner) {
            _owner = owner;
 
            if (_selectDesired == +1) {
                _selectDesired = 0;
                Selected = true;
            }
            else if (_selectDesired == -1) {
                _selectDesired = 0;
                Selected = false;
            }
 
            if (_populateDesired) {
                _populateDesired = false;
                Populate();
            }
 
            if (_modifyCheckedNodes) {
                if (_owner != null) {
                    _modifyCheckedNodes = false;
 
                    if (Checked) {
                        TreeNodeCollection checkedNodes = _owner.CheckedNodes;
                        if (!checkedNodes.Contains(this)) {
                            _owner.CheckedNodes.Add(this);
                        }
                    }
                    else {
                        _owner.CheckedNodes.Remove(this);
                    }
                }
            }
 
            foreach (TreeNode node in ChildNodes) {
                node.SetOwner(_owner);
            }
        }
 
 
        /// <devdoc>
        ///     Sets the parent TreeNode of the node
        /// </devdoc>
        internal void SetParent(TreeNode parent) {
            _parent = parent;
            SetPath(null);
        }
 
 
        /// <devdoc>
        ///     Sets the path of the node (without reparenting).  Used in the PopulateNodesFromClient scenario.
        /// </devdoc>
        internal void SetPath(string newPath) {
            _internalValuePath = newPath;
            _depth = -2;
        }
 
        internal void SetSelected(bool value) {
            ViewState["Selected"] = value;
            // If the owner hasn't been set, remember that we want to select this node
            // when the owner is determined
            if (_owner == null) {
                _selectDesired = (value ? +1 : -1);
            }
        }
 
        /// <devdoc>
        ///     Switches the expand state of the node
        /// </devdoc>
        public void ToggleExpandState() {
            Expanded = !(Expanded == true);
        }
 
        #region IStateManager implementation
 
        /// <internalonly/>
        bool IStateManager.IsTrackingViewState {
            get {
                return IsTrackingViewState;
            }
        }
 
        protected bool IsTrackingViewState {
            get {
                return _isTrackingViewState;
            }
        }
 
 
        /// <internalonly/>
        void IStateManager.LoadViewState(object state) {
            LoadViewState(state);
        }
 
        protected virtual void LoadViewState(object state) {
            object[] nodeState = (object[])state;
 
            if (nodeState != null) {
                if (nodeState[0] != null) {
                    ((IStateManager)ViewState).LoadViewState(nodeState[0]);
                    NotifyOwnerChecked();
                }
 
                if (nodeState[1] != null) {
                    ((IStateManager)ChildNodes).LoadViewState(nodeState[1]);
                }
            }
        }
 
 
        /// <internalonly/>
        object IStateManager.SaveViewState() {
            return SaveViewState();
        }
 
        protected virtual object SaveViewState() {
            object[] state = new object[2];
            if (_viewState != null) {
                state[0] = ((IStateManager)_viewState).SaveViewState();
            }
 
            if (_childNodes != null) {
                state[1] = ((IStateManager)_childNodes).SaveViewState();
            }
 
            if ((state[0] == null) && (state[1] == null)) {
                return null;
            }
 
            return state;
        }
 
 
        /// <internalonly/>
        void IStateManager.TrackViewState() {
            TrackViewState();
        }
 
        protected void TrackViewState() {
            _isTrackingViewState = true;
 
            if (_viewState != null) {
                ((IStateManager)_viewState).TrackViewState();
            }
 
            if (_childNodes != null) {
                ((IStateManager)_childNodes).TrackViewState();
            }
        }
        #endregion
 
        #region ICloneable implementation
 
        /// <internalonly/>
        object ICloneable.Clone() {
            return Clone();
        }
 
        protected virtual object Clone() {
            TreeNode newNode = new TreeNode();
            newNode.Checked = Checked;
            newNode.Expanded = Expanded;
            newNode.ImageUrl = ImageUrl;
            newNode.ImageToolTip = ImageToolTip;
            newNode.NavigateUrl = NavigateUrl;
            newNode.PopulateOnDemand = PopulateOnDemand;
            newNode.SelectAction = SelectAction;
            newNode.Selected = Selected;
            if (ViewState["ShowCheckBox"] != null) {
                newNode.ShowCheckBox = ShowCheckBox;
            }
            newNode.Target = Target;
            newNode.Text = Text;
            newNode.ToolTip = ToolTip;
            newNode.Value = Value;
 
            return newNode;
        }
        #endregion
    }
}