TreeView.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / xsp / System / Web / UI / WebControls / TreeView.cs / 2 / TreeView.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.UI.WebControls { 
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Drawing; 
    using System.Drawing.Design;
    using System.Globalization; 
    using System.IO; 
    using System.Security.Permissions;
    using System.Text; 
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.Adapters;
    using System.Web.Util; 

 
    ///  
    ///     Provides a tree view control
    ///  
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [ControlValueProperty("SelectedValue")]
    [DefaultEvent("SelectedNodeChanged")]
    [Designer("System.Web.UI.Design.WebControls.TreeViewDesigner, " + AssemblyRef.SystemDesign)] 
    [SupportsEventValidation]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] 
    public class TreeView : HierarchicalDataBoundControl, IPostBackEventHandler, IPostBackDataHandler, ICallbackEventHandler { 
        private static string populateNodeScript = @"
    function TreeView_PopulateNodeDoCallBack(context,param) { 
        ";
        private static string populateNodeScriptEnd = @";
    }
"; 

        internal const int RootImageIndex = 0; 
        internal const int ParentImageIndex = 1; 
        internal const int LeafImageIndex = 2;
 
        internal const int NoExpandImageIndex = 3;
        internal const int PlusImageIndex = 4;
        internal const int MinusImageIndex = 5;
 
        internal const int IImageIndex = 6;
 
        internal const int RImageIndex = 7; 
        internal const int RPlusImageIndex = 8;
        internal const int RMinusImageIndex = 9; 

        internal const int TImageIndex = 10;
        internal const int TPlusImageIndex = 11;
        internal const int TMinusImageIndex = 12; 

        internal const int LImageIndex = 13; 
        internal const int LPlusImageIndex = 14; 
        internal const int LMinusImageIndex = 15;
 
        internal const int DashImageIndex = 16;
        internal const int DashPlusImageIndex = 17;
        internal const int DashMinusImageIndex = 18;
 
        internal const int ImageUrlsCount = 19;
 
        // Also used by Menu 
        internal const char InternalPathSeparator = '\\';
        private const char EscapeCharacter = '|'; 
        private const string EscapeSequenceForPathSeparator = "*|*";
        private const string EscapeSequenceForEscapeCharacter = "||";

        private string[] _imageUrls; 
        private string[] _levelImageUrls;
 
        private static readonly object CheckChangedEvent = new object(); 
        private static readonly object SelectedNodeChangedEvent = new object();
        private static readonly object TreeNodeCollapsedEvent = new object(); 
        private static readonly object TreeNodeExpandedEvent = new object();
        private static readonly object TreeNodePopulateEvent = new object();
        private static readonly object TreeNodeDataBoundEvent = new object();
 
        private TreeNodeStyle _nodeStyle;
        private TreeNodeStyle _rootNodeStyle; 
        private TreeNodeStyle _parentNodeStyle; 
        private TreeNodeStyle _leafNodeStyle;
        private TreeNodeStyle _selectedNodeStyle; 
        private Style _hoverNodeStyle;
        private HyperLinkStyle _hoverNodeHyperLinkStyle;

        private Style _baseNodeStyle; 

        // Cached styles. In the current implementation, the styles are the same for all items 
        // and submenus at a given depth. 
        private List _cachedParentNodeStyles;
        private List _cachedParentNodeClassNames; 
        private List _cachedParentNodeHyperLinkClassNames;
        private List _cachedLeafNodeStyles;
        private List _cachedLeafNodeClassNames;
        private List _cachedLeafNodeHyperLinkClassNames; 
        private Collection _cachedLevelsContainingCssClass;
 
        private TreeNode _rootNode; 
        private TreeNode _selectedNode;
 
        private TreeNodeCollection _checkedNodes;

        private TreeNodeStyleCollection _levelStyles;
 
        private ArrayList _checkedChangedNodes;
 
        private TreeNodeBindingCollection _bindings; 

        private int _cssStyleIndex; 

        //
        private bool _loadingNodeState;
        private bool _dataBound; 
        private bool _accessKeyRendered;
 
        private bool _isNotIE; 
        private bool _renderClientScript;
 
        private bool _fireSelectedNodeChanged;

        private string _cachedExpandImageUrl;
        private string _cachedCollapseImageUrl; 
        private string _cachedNoExpandImageUrl;
        private string _cachedClientDataObjectID; 
        private string _cachedExpandStateID; 
        private string _cachedImageArrayID;
        private string _cachedPopulateLogID; 
        private string _cachedSelectedNodeFieldID;

        private string _currentSiteMapNodeDataPath;
 
        private string _callbackEventArgument;
 
        internal bool AccessKeyRendered { 
            get {
                return _accessKeyRendered; 
            }
            set {
                _accessKeyRendered = value;
            } 
        }
 
        private List CachedLeafNodeClassNames { 
            get {
                if (_cachedLeafNodeClassNames == null) { 
                    _cachedLeafNodeClassNames = new List();
                }
                return _cachedLeafNodeClassNames;
            } 
        }
 
        private List CachedLeafNodeStyles { 
            get {
                if (_cachedLeafNodeStyles == null) { 
                    _cachedLeafNodeStyles = new List();
                }
                return _cachedLeafNodeStyles;
            } 
        }
 
        private List CachedLeafNodeHyperLinkClassNames { 
            get {
                if (_cachedLeafNodeHyperLinkClassNames == null) { 
                    _cachedLeafNodeHyperLinkClassNames = new List();
                }
                return _cachedLeafNodeHyperLinkClassNames;
            } 
        }
 
        private Collection CachedLevelsContainingCssClass { 
            get {
                if (_cachedLevelsContainingCssClass == null) { 
                    _cachedLevelsContainingCssClass = new Collection();
                }
                return _cachedLevelsContainingCssClass;
            } 
        }
 
        private List CachedParentNodeStyles { 
            get {
                if (_cachedParentNodeStyles == null) { 
                    _cachedParentNodeStyles = new List();
                }
                return _cachedParentNodeStyles;
            } 
        }
 
        private List CachedParentNodeClassNames { 
            get {
                if (_cachedParentNodeClassNames == null) { 
                    _cachedParentNodeClassNames = new List();
                }
                return _cachedParentNodeClassNames;
            } 
        }
 
        private List CachedParentNodeHyperLinkClassNames { 
            get {
                if (_cachedParentNodeHyperLinkClassNames == null) { 
                    _cachedParentNodeHyperLinkClassNames = new List();
                }
                return _cachedParentNodeHyperLinkClassNames;
            } 
        }
 
 
        /// 
        /// Gets and sets whether the tree view will automatically bind to data 
        /// 
        [
        DefaultValue(true),
        WebCategory("Behavior"), 
        WebSysDescription(SR.TreeView_AutoGenerateDataBindings)
        ] 
        public bool AutoGenerateDataBindings { 
            get {
                object o = ViewState["AutoGenerateDataBindings"]; 
                if (o == null) {
                    return true;
                }
                return (bool)o; 
            }
            set { 
                ViewState["AutoGenerateDataBindings"] = value; 
            }
        } 


        /// 
        ///     Gets the tree level data mappings 
        /// 
        [ 
        DefaultValue(null), 
        MergableProperty(false),
        Editor("System.Web.UI.Design.WebControls.TreeViewBindingsEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), 
        PersistenceMode(PersistenceMode.InnerProperty),
        WebCategory("Data"),
        WebSysDescription(SR.TreeView_DataBindings)
        ] 
        public TreeNodeBindingCollection DataBindings {
            get { 
                if (_bindings == null) { 
                    _bindings = new TreeNodeBindingCollection();
                    if (IsTrackingViewState) { 
                        ((IStateManager)_bindings).TrackViewState();
                    }
                }
                return _bindings; 
            }
        } 
 

        ///  
        ///     Gets the currently checked nodes in tree
        /// 
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
        public TreeNodeCollection CheckedNodes {
            get { 
                if (_checkedNodes == null) { 
                    _checkedNodes = new TreeNodeCollection(null, false);
                } 
                return _checkedNodes;
            }
        }
 
        private ArrayList CheckedChangedNodes {
            get { 
                if (_checkedChangedNodes == null) { 
                    _checkedChangedNodes = new ArrayList();
                } 
                return _checkedChangedNodes;
            }
        }
 
        /// 
        ///     Gets the hidden field ID for the expand state of this TreeView 
        ///  
        internal string ClientDataObjectID {
            get { 
                if (_cachedClientDataObjectID == null) {
                    _cachedClientDataObjectID = ClientID + "_Data";
                }
                return _cachedClientDataObjectID; 
            }
        } 
 

        ///  
        ///     Gets and sets the image ToolTip for the collapse node icon (minus).
        /// 
        [Localizable(true)]
        [WebSysDefaultValue(SR.TreeView_CollapseImageToolTipDefaultValue)] 
        [WebCategory("Appearance")]
        [WebSysDescription(SR.TreeView_CollapseImageToolTip)] 
        public string CollapseImageToolTip { 
            get {
                string s = (string)ViewState["CollapseImageToolTip"]; 

                if (s == null) {
                    return SR.GetString(SR.TreeView_CollapseImageToolTipDefaultValue);
                } 

                return s; 
            } 
            set {
                ViewState["CollapseImageToolTip"] = value; 
            }
        }

 
        /// 
        ///     Gets and sets the image url for the collapse node icon (minus). 
        ///  
        [DefaultValue("")]
        [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))] 
        [UrlProperty()]
        [WebCategory("Appearance")]
        [WebSysDescription(SR.TreeView_CollapseImageUrl)]
        public string CollapseImageUrl { 
            get {
                string s = (string)ViewState["CollapseImageUrl"]; 
                if (s == null) { 
                    return String.Empty;
                } 
                return s;
            }
            set {
                ViewState["CollapseImageUrl"] = value; 
            }
        } 
 
        internal string CollapseImageUrlInternal {
            get { 
                if (_cachedCollapseImageUrl == null) {
                    switch (ImageSet) {
                        case TreeViewImageSet.Arrows: {
                                _cachedCollapseImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Arrows_Collapse.gif"); 
                                break;
                            } 
                        case TreeViewImageSet.Contacts: { 
                                _cachedCollapseImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Contacts_Collapse.gif");
                                break; 
                            }
                        case TreeViewImageSet.XPFileExplorer: {
                                _cachedCollapseImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_XP_Explorer_Collapse.gif");
                                break; 
                            }
                        case TreeViewImageSet.Msdn: { 
                                _cachedCollapseImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_MSDN_Collapse.gif"); 
                                break;
                            } 
                        case TreeViewImageSet.WindowsHelp: {
                                _cachedCollapseImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Windows_Help_Collapse.gif");
                                break;
                            } 
                        case TreeViewImageSet.Custom: {
                                _cachedCollapseImageUrl = CollapseImageUrl; 
                                break; 
                            }
                        default: { 
                                _cachedCollapseImageUrl = String.Empty;
                                break;
                            }
                    } 
                }
                return _cachedCollapseImageUrl; 
            } 
        }
 
        internal bool CustomExpandCollapseHandlerExists {
            get {
                TreeNodeEventHandler collapseHandler = (TreeNodeEventHandler)Events[TreeNodeCollapsedEvent];
                TreeNodeEventHandler expandHandler = (TreeNodeEventHandler)Events[TreeNodeExpandedEvent]; 
                return ((collapseHandler != null) || (expandHandler != null));
            } 
        } 

 
        /// 
        ///     Gets and sets whether the control should try to use client script, if the browser is capable.
        /// 
        [DefaultValue(true)] 
        [WebCategory("Behavior")]
        [Themeable(false)] 
        [WebSysDescription(SR.TreeView_EnableClientScript)] 
        public bool EnableClientScript {
            get { 
                object o = ViewState["EnableClientScript"];
                if (o == null) {
                    return true;
                } 

                return (bool)o; 
            } 
            set {
                ViewState["EnableClientScript"] = value; 
            }
        }

        ///  
        ///     Gets whether hover styles have been enabled (set)
        ///  
        internal bool EnableHover { 
            get {
                return (Page != null && 
                    (Page.SupportsStyleSheets || Page.IsCallback ||
                    (Page.ScriptManager != null && Page.ScriptManager.IsInAsyncPostBack)) &&
                    RenderClientScript &&
                    (_hoverNodeStyle != null)); 
            }
        } 
 
        [DefaultValue(-1)]
        [TypeConverter(typeof(TreeViewExpandDepthConverter))] 
        [WebCategory("Behavior")]
        [WebSysDescription(SR.TreeView_ExpandDepth)]
        public int ExpandDepth {
            get { 
                object o = ViewState["ExpandDepth"];
                if (o == null) { 
                    return -1; 
                }
                return (int)o; 
            }
            set {
                ViewState["ExpandDepth"] = value;
            } 
        }
 
 
        /// 
        ///     Gets and sets the image ToolTip for the Expand node icon (minus). 
        /// 
        [Localizable(true)]
        [WebSysDefaultValue(SR.TreeView_ExpandImageToolTipDefaultValue)]
        [WebCategory("Appearance")] 
        [WebSysDescription(SR.TreeView_ExpandImageToolTip)]
        public string ExpandImageToolTip { 
            get { 
                string s = (string)ViewState["ExpandImageToolTip"];
 
                if (s == null) {
                    return SR.GetString(SR.TreeView_ExpandImageToolTipDefaultValue);
                }
 
                return s;
            } 
            set { 
                ViewState["ExpandImageToolTip"] = value;
            } 
        }


        ///  
        ///     Gets and sets the image url for the expand node icon (plus).
        ///  
        [DefaultValue("")] 
        [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
        [UrlProperty()] 
        [WebCategory("Appearance")]
        [WebSysDescription(SR.TreeView_ExpandImageUrl)]
        public string ExpandImageUrl {
            get { 
                string s = (string)ViewState["ExpandImageUrl"];
                if (s == null) { 
                    return String.Empty; 
                }
                return s; 
            }
            set {
                ViewState["ExpandImageUrl"] = value;
            } 
        }
 
        internal string ExpandImageUrlInternal { 
            get {
                if (_cachedExpandImageUrl == null) { 
                    switch (ImageSet) {
                        case TreeViewImageSet.Arrows: {
                                _cachedExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Arrows_Expand.gif");
                                break; 
                            }
                        case TreeViewImageSet.Contacts: { 
                                _cachedExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Contacts_Expand.gif"); 
                                break;
                            } 
                        case TreeViewImageSet.XPFileExplorer: {
                                _cachedExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_XP_Explorer_Expand.gif");
                                break;
                            } 
                        case TreeViewImageSet.Msdn: {
                                _cachedExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_MSDN_Expand.gif"); 
                                break; 
                            }
                        case TreeViewImageSet.WindowsHelp: { 
                                _cachedExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Windows_Help_Expand.gif");
                                break;
                            }
                        case TreeViewImageSet.Custom: { 
                                _cachedExpandImageUrl = ExpandImageUrl;
                                break; 
                            } 
                        default: {
                                _cachedExpandImageUrl = String.Empty; 
                                break;
                            }
                    }
                } 
                return _cachedExpandImageUrl;
            } 
        } 

 
        /// 
        ///     Gets the hidden field ID for the expand state of this TreeView
        /// 
        internal string ExpandStateID { 
            get {
                if (_cachedExpandStateID == null) { 
                    _cachedExpandStateID = ClientID + "_ExpandState"; 
                }
                return _cachedExpandStateID; 
            }
        }

 
        /// 
        ///     Gets the hover style properties for nodes. 
        ///  
        [
        DefaultValue(null), 
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty),
        WebCategory("Styles"), 
        WebSysDescription(SR.TreeView_HoverNodeStyle)
        ] 
        public Style HoverNodeStyle { 
            get {
                if (_hoverNodeStyle == null) { 
                    _hoverNodeStyle = new Style();
                    if (IsTrackingViewState) {
                        ((IStateManager)_hoverNodeStyle).TrackViewState();
                    } 
                }
                return _hoverNodeStyle; 
            } 
        }
 
        /// 
        /// ID of the client-side array of images (expand, collapse, lines, etc.)
        /// 
        internal string ImageArrayID { 
            get {
                if (_cachedImageArrayID == null) { 
                    _cachedImageArrayID = ClientID + "_ImageArray"; 
                }
                return _cachedImageArrayID; 
            }
        }

 
        [DefaultValue(TreeViewImageSet.Custom)]
        [WebCategory("Appearance")] 
        [WebSysDescription(SR.TreeView_ImageSet)] 
        public TreeViewImageSet ImageSet {
            get { 
                object o = ViewState["ImageSet"];
                if (o == null) {
                    return TreeViewImageSet.Custom;
                } 
                return (TreeViewImageSet)o;
            } 
            set { 
                if (value < TreeViewImageSet.Custom || value > TreeViewImageSet.Faq) {
                    throw new ArgumentOutOfRangeException("value"); 
                }
                ViewState["ImageSet"] = value;
            }
        } 

 
        ///  
        ///     An cache of urls for the line and node type images.
        ///  
        private string[] ImageUrls {
            get {
                if (_imageUrls == null) {
                    _imageUrls = new string[ImageUrlsCount]; 
                }
                return _imageUrls; 
            } 
        }
 

        /// 
        ///     Gets whether the current browser is IE
        ///  
        internal bool IsNotIE {
            get { 
                return _isNotIE; 
            }
        } 


        /// 
        ///     Gets the style properties of leaf nodes in the tree. 
        /// 
        [ 
        WebCategory("Styles"), 
        DefaultValue(null),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty),
        WebSysDescription(SR.TreeView_LeafNodeStyle)
        ] 
        public TreeNodeStyle LeafNodeStyle {
            get { 
                if (_leafNodeStyle == null) { 
                    _leafNodeStyle = new TreeNodeStyle();
                    if (IsTrackingViewState) { 
                        ((IStateManager)_leafNodeStyle).TrackViewState();
                    }
                }
                return _leafNodeStyle; 
            }
        } 
 
        private string[] LevelImageUrls {
            get { 
                if (_levelImageUrls == null) {
                    _levelImageUrls = new string[LevelStyles.Count];
                }
                return _levelImageUrls; 
            }
        } 
 

 
        /// 
        ///     Gets the collection of TreeNodeStyles corresponding to the each level
        /// 
        [ 
        DefaultValue(null),
        Editor("System.Web.UI.Design.WebControls.TreeNodeStyleCollectionEditor," + AssemblyRef.SystemDesign, typeof(UITypeEditor)), 
        PersistenceMode(PersistenceMode.InnerProperty), 
        WebCategory("Styles"),
        WebSysDescription(SR.TreeView_LevelStyles), 
        ]
        public TreeNodeStyleCollection LevelStyles {
            get {
                if (_levelStyles == null) { 
                    _levelStyles = new TreeNodeStyleCollection();
                    if (IsTrackingViewState) { 
                        ((IStateManager)_levelStyles).TrackViewState(); 
                    }
                } 

                return _levelStyles;
            }
        } 

 
        ///  
        ///     Gets and sets the url pointing to a folder containing TreeView line images.
        ///  
        [DefaultValue("")]
        [WebCategory("Appearance")]
        [WebSysDescription(SR.TreeView_LineImagesFolderUrl)]
        public string LineImagesFolder { 
            get {
                string s = (string)ViewState["LineImagesFolder"]; 
                if (s == null) { 
                    return String.Empty;
                } 
                return s;
            }
            set {
                ViewState["LineImagesFolder"] = value; 
            }
        } 
 

        ///  
        ///     True is we are loading the state of a node that has changed on the client, specifically,
        ///     this is used by TreeNode.Expand to check if it needs to trigger a populate or not
        /// 
        internal bool LoadingNodeState { 
            get {
                return _loadingNodeState; 
            } 
        }
 

        /// 
        ///     The maximum depth to which the TreeView will bind.
        ///  
        [WebCategory("Behavior")]
        [DefaultValue(-1)] 
        [WebSysDescription(SR.TreeView_MaxDataBindDepth)] 
        public int MaxDataBindDepth {
            get { 
                object o = ViewState["MaxDataBindDepth"];
                if (o == null) {
                    return -1;
                } 
                return (int)o;
            } 
            set { 
                if (value < -1) {
                    throw new ArgumentOutOfRangeException("value"); 
                }
                ViewState["MaxDataBindDepth"] = value;
            }
        } 

 
        ///  
        ///     Gets and sets the image url for the non-expandable node indicator icon.
        ///  
        [DefaultValue("")]
        [Editor("System.Web.UI.Design.ImageUrlEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor))]
        [UrlProperty()]
        [WebCategory("Appearance")] 
        [WebSysDescription(SR.TreeView_NoExpandImageUrl)]
        public string NoExpandImageUrl { 
            get { 
                string s = (string)ViewState["NoExpandImageUrl"];
                if (s == null) { 
                    return String.Empty;
                }
                return s;
            } 
            set {
                ViewState["NoExpandImageUrl"] = value; 
            } 
        }
 
        internal string NoExpandImageUrlInternal {
            get {
                if (_cachedNoExpandImageUrl == null) {
                    switch (ImageSet) { 
                        case TreeViewImageSet.Simple: {
                                _cachedNoExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Simple_NoExpand.gif"); 
                                break; 
                            }
                        case TreeViewImageSet.Simple2: { 
                                _cachedNoExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Simple2_NoExpand.gif");
                                break;
                            }
                        case TreeViewImageSet.Arrows: { 
                                _cachedNoExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Arrows_NoExpand.gif");
                                break; 
                            } 
                        case TreeViewImageSet.Contacts: {
                                _cachedNoExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Contacts_NoExpand.gif"); 
                                break;
                            }
                        case TreeViewImageSet.XPFileExplorer: {
                                _cachedNoExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_XP_Explorer_NoExpand.gif"); 
                                break;
                            } 
                        case TreeViewImageSet.Msdn: { 
                                _cachedNoExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_MSDN_NoExpand.gif");
                                break; 
                            }
                        case TreeViewImageSet.WindowsHelp: {
                                _cachedNoExpandImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Windows_Help_NoExpand.gif");
                                break; 
                            }
                        case TreeViewImageSet.Custom: { 
                                _cachedNoExpandImageUrl = NoExpandImageUrl; 
                                break;
                            } 
                        default: {
                                _cachedNoExpandImageUrl = String.Empty;
                                break;
                            } 
                    }
                } 
                return _cachedNoExpandImageUrl; 
            }
        } 


        /// 
        ///     Gets and sets the indent width of each node 
        /// 
        [DefaultValue(20)] 
        [WebCategory("Appearance")] 
        [WebSysDescription(SR.TreeView_NodeIndent)]
        public int NodeIndent { 
            get {
                object o = ViewState["NodeIndent"];
                if (o == null) {
                    return 20; 
                }
                return (int)o; 
            } 
            set {
                ViewState["NodeIndent"] = value; 
            }
        }

 
        /// 
        ///     Gets and sets whether the text of the nodes should be wrapped 
        ///  
        [DefaultValue(false)]
        [WebCategory("Appearance")] 
        [WebSysDescription(SR.TreeView_NodeWrap)]
        public bool NodeWrap {
            get {
                object o = ViewState["NodeWrap"]; 
                if (o == null) {
                    return false; 
                } 
                return (bool)o;
            } 
            set {
                ViewState["NodeWrap"] = value;
            }
        } 

 
        ///  
        ///     Gets the collection of top-level nodes.
        ///  
        [
        DefaultValue(null),
        MergableProperty(false),
        Editor("System.Web.UI.Design.WebControls.TreeNodeCollectionEditor," + AssemblyRef.SystemDesign, typeof(UITypeEditor)), 
        PersistenceMode(PersistenceMode.InnerProperty),
        WebSysDescription(SR.TreeView_Nodes) 
        ] 
        public TreeNodeCollection Nodes {
            get { 
                return RootNode.ChildNodes;
            }
        }
 

 
        ///  
        ///     Gets the style properties of nodes in the tree.
        ///  
        [
        WebCategory("Styles"),
        DefaultValue(null),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty), 
        WebSysDescription(SR.TreeView_NodeStyle) 
        ]
        public TreeNodeStyle NodeStyle { 
            get {
                if (_nodeStyle == null) {
                    _nodeStyle = new TreeNodeStyle();
                    if (IsTrackingViewState) { 
                        ((IStateManager)_nodeStyle).TrackViewState();
                    } 
                } 
                return _nodeStyle;
            } 
        }


        ///  
        ///     Gets the style properties of parent nodes in the tree.
        ///  
        [ 
        WebCategory("Styles"),
        DefaultValue(null), 
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty),
        WebSysDescription(SR.TreeView_ParentNodeStyle) 
        ]
        public TreeNodeStyle ParentNodeStyle { 
            get { 
                if (_parentNodeStyle == null) {
                    _parentNodeStyle = new TreeNodeStyle(); 
                    if (IsTrackingViewState) {
                        ((IStateManager)_parentNodeStyle).TrackViewState();
                    }
                } 
                return _parentNodeStyle;
            } 
        } 

 
        /// 
        ///     Gets and sets the character used to delimit paths.
        /// 
        [DefaultValue('/')] 
        [WebSysDescription(SR.TreeView_PathSeparator)]
        public char PathSeparator { 
            get { 
                object o = ViewState["PathSeparator"];
                if (o == null) { 
                    return '/';
                }
                return (char)o;
            } 
            set {
                if (value == '\0') { 
                    ViewState["PathSeparator"] = null; 
                }
                else { 
                    ViewState["PathSeparator"] = value;
                }
                foreach (TreeNode node in Nodes) {
                    node.ResetValuePathRecursive(); 
                }
            } 
        } 

 
        /// 
        ///     Gets the hidden field ID for the expand state of this TreeView
        /// 
        internal string PopulateLogID { 
            get {
                if (_cachedPopulateLogID == null) { 
                    _cachedPopulateLogID = ClientID + "_PopulateLog"; 
                }
                return _cachedPopulateLogID; 
            }
        }

 
        /// 
        ///     Gets and sets whether the tree view should populate nodes from the client (if supported) 
        ///  
        [DefaultValue(true)]
        [WebCategory("Behavior")] 
        [WebSysDescription(SR.TreeView_PopulateNodesFromClient)]
        public bool PopulateNodesFromClient {
            get {
                if (!DesignMode && 
                    (Page != null && !Page.Request.Browser.SupportsCallback)) {
                    return false; 
                } 
                object o = ViewState["PopulateNodesFromClient"];
                if (o == null) { 
                    return true;
                }
                return (bool)o;
            } 
            set {
                ViewState["PopulateNodesFromClient"] = value; 
            } 
        }
 
        /// 
        ///     Gets whether we should be rendering client script or not
        /// 
        internal bool RenderClientScript { 
            get {
                return _renderClientScript; 
            } 
        }
 

        /// 
        ///     The 'virtual' root node of the tree
        ///  
        internal TreeNode RootNode {
            get { 
                if (_rootNode == null) { 
                    // Using the constructor only here. Other places should use CreateNode.
                    _rootNode = new TreeNode(this, true); 
                }
                return _rootNode;
            }
        } 

        // BaseTreeNodeStyle is roughly equivalent to ControlStyle.HyperLinkStyle if it existed. 
        internal Style BaseTreeNodeStyle { 
            get {
                if (_baseNodeStyle == null) { 
                    _baseNodeStyle = new Style();
                    _baseNodeStyle.Font.CopyFrom(Font);
                    if (!ForeColor.IsEmpty) {
                        _baseNodeStyle.ForeColor = ForeColor; 
                    }
                    // Not defaulting to black anymore for not entirely satisfying but reasonable reasons (VSWhidbey 356729) 
                    if (!ControlStyle.IsSet(System.Web.UI.WebControls.Style.PROP_FONT_UNDERLINE)) { 
                        _baseNodeStyle.Font.Underline = false;
                    } 
                }
                return _baseNodeStyle;
            }
        } 

 
        ///  
        ///     Gets the style properties of root nodes in the tree.
        ///  
        [
        WebCategory("Styles"),
        DefaultValue(null),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty), 
        WebSysDescription(SR.TreeView_RootNodeStyle) 
        ]
        public TreeNodeStyle RootNodeStyle { 
            get {
                if (_rootNodeStyle == null) {
                    _rootNodeStyle = new TreeNodeStyle();
                    if (IsTrackingViewState) { 
                        ((IStateManager)_rootNodeStyle).TrackViewState();
                    } 
                } 
                return _rootNodeStyle;
            } 
        }


        ///  
        ///     Gets and sets the TreeView's selected node.
        ///  
        [ 
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 
        ]
        public TreeNode SelectedNode {
            get {
                return _selectedNode; 
            }
        } 
 

        ///  
        ///     Gets the tag ID for hidden field containing the id of the selected node of this TreeView
        /// 
        internal string SelectedNodeFieldID {
            get { 
                if (_cachedSelectedNodeFieldID == null) {
                    _cachedSelectedNodeFieldID = ClientID + "_SelectedNode"; 
                } 
                return _cachedSelectedNodeFieldID;
            } 
        }


        ///  
        ///     Gets the style properties of the selected node in the tree.
        ///  
        [ 
        WebCategory("Styles"),
        DefaultValue(null), 
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        NotifyParentProperty(true),
        PersistenceMode(PersistenceMode.InnerProperty),
        WebSysDescription(SR.TreeView_SelectedNodeStyle) 
        ]
        public TreeNodeStyle SelectedNodeStyle { 
            get { 
                if (_selectedNodeStyle == null) {
                    _selectedNodeStyle = new TreeNodeStyle(); 
                    if (IsTrackingViewState) {
                        ((IStateManager)_selectedNodeStyle).TrackViewState();
                    }
                } 
                return _selectedNodeStyle;
            } 
        } 

 
        [Browsable(false)]
        [DefaultValue("")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public string SelectedValue { 
            get {
                if (SelectedNode != null) { 
                    return SelectedNode.Value; 
                }
 
                return String.Empty;
            }
        }
 

        ///  
        ///     Gets and sets whether to show check boxes next to specific types of nodes in the tree 
        /// 
        [DefaultValue(TreeNodeTypes.None)] 
        [WebCategory("Behavior")]
        [WebSysDescription(SR.TreeView_ShowCheckBoxes)]
        public TreeNodeTypes ShowCheckBoxes {
            get { 
                object o = ViewState["ShowCheckBoxes"];
                if (o == null) { 
                    return TreeNodeTypes.None; 
                }
                return (TreeNodeTypes)o; 
            }
            set {
                if ((value < TreeNodeTypes.None) || (value > TreeNodeTypes.All)) {
                    throw new ArgumentOutOfRangeException("value"); 
                }
                ViewState["ShowCheckBoxes"] = value; 
            } 
        }
 

        /// 
        ///     Gets and sets whether to show the expander icon next to nodes in the tree
        ///  
        [DefaultValue(true)]
        [WebCategory("Appearance")] 
        [WebSysDescription(SR.TreeView_ShowExpandCollapse)] 
        public bool ShowExpandCollapse {
            get { 
                object o = ViewState["ShowExpandCollapse"];
                if (o == null) {
                    return true;
                } 
                return (bool)o;
            } 
            set { 
                ViewState["ShowExpandCollapse"] = value;
            } 
        }


        ///  
        ///     Gets and sets whether the TreeView should show lines.
        ///  
        [DefaultValue(false)] 
        [WebCategory("Appearance")]
        [WebSysDescription(SR.TreeView_ShowLines)] 
        public bool ShowLines {
            get {
                object o = ViewState["ShowLines"];
                if (o == null) { 
                    return false;
                } 
                return (bool)o; 
            }
            set { 
                ViewState["ShowLines"] = value;
            }
        }
 
        [
        Localizable(true), 
        WebCategory("Accessibility"), 
        WebSysDefaultValue(SR.TreeView_Default_SkipLinkText),
        WebSysDescription(SR.TreeView_SkipLinkText) 
        ]
        public String SkipLinkText {
            get {
                string s = ViewState["SkipLinkText"] as String; 
                return s == null ? SR.GetString(SR.TreeView_Default_SkipLinkText) : s;
            } 
            set { 
                ViewState["SkipLinkText"] = value;
            } 
        }


        ///  
        ///     Gets and sets the target window that the TreeNodes will browse to if selected
        ///  
        [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; 
            }
        }

 
        protected override HtmlTextWriterTag TagKey {
            get { 
                return DesignMode ? HtmlTextWriterTag.Table : HtmlTextWriterTag.Div; 
            }
        } 

        public override bool Visible {
            get {
                return base.Visible; 
            }
            set { 
                // Remember that the tree was initially invisible and thus never expanded (VSWhidbey 349279) 
                // See SaveViewState to see the code that sets this flag.
                if ((value == true) && (Page != null) && Page.IsPostBack && 
                    (ViewState["NeverExpanded"] != null) &&
                    ((bool)ViewState["NeverExpanded"] == true)) {

                    // This will reset the viewstate flag and expand the tree 
                    ExpandToDepth(Nodes, ExpandDepth);
                } 
                base.Visible = value; 
            }
        } 

        [WebCategory("Behavior")]
        [WebSysDescription(SR.TreeView_CheckChanged)]
        public event TreeNodeEventHandler TreeNodeCheckChanged { 
            add {
                Events.AddHandler(CheckChangedEvent, value); 
            } 
            remove {
                Events.RemoveHandler(CheckChangedEvent, value); 
            }
        }

 
        /// 
        ///     Triggered when the TreeView's selected node has changed. 
        ///  
        [WebCategory("Behavior")]
        [WebSysDescription(SR.TreeView_SelectedNodeChanged)] 
        public event EventHandler SelectedNodeChanged {
            add {
                Events.AddHandler(SelectedNodeChangedEvent, value);
            } 
            remove {
                Events.RemoveHandler(SelectedNodeChangedEvent, value); 
            } 
        }
 

        /// 
        ///     Triggered when a TreeNode has collapsed its children.
        ///  
        [WebCategory("Behavior")]
        [WebSysDescription(SR.TreeView_TreeNodeCollapsed)] 
        public event TreeNodeEventHandler TreeNodeCollapsed { 
            add {
                Events.AddHandler(TreeNodeCollapsedEvent, value); 
            }
            remove {
                Events.RemoveHandler(TreeNodeCollapsedEvent, value);
            } 
        }
 
 
        /// 
        ///     Triggered when a TreeNode has been databound. 
        /// 
        [WebCategory("Behavior")]
        [WebSysDescription(SR.TreeView_TreeNodeDataBound)]
        public event TreeNodeEventHandler TreeNodeDataBound { 
            add {
                Events.AddHandler(TreeNodeDataBoundEvent, value); 
            } 
            remove {
                Events.RemoveHandler(TreeNodeDataBoundEvent, value); 
            }
        }

 
        /// 
        ///     Triggered when a TreeNode has expanded its children. 
        ///  
        [WebCategory("Behavior")]
        [WebSysDescription(SR.TreeView_TreeNodeExpanded)] 
        public event TreeNodeEventHandler TreeNodeExpanded {
            add {
                Events.AddHandler(TreeNodeExpandedEvent, value);
            } 
            remove {
                Events.RemoveHandler(TreeNodeExpandedEvent, value); 
            } 
        }
 

        /// 
        ///     Triggered when a TreeNode is populating its children.
        ///  
        [WebCategory("Behavior")]
        [WebSysDescription(SR.TreeView_TreeNodePopulate)] 
        public event TreeNodeEventHandler TreeNodePopulate { 
            add {
                Events.AddHandler(TreeNodePopulateEvent, value); 
            }
            remove {
                Events.RemoveHandler(TreeNodePopulateEvent, value);
            } 
        }
 
        protected override void AddAttributesToRender(HtmlTextWriter writer) { 

            // Make sure we are in a form tag with runat=server. 
            if (Page != null) {
                Page.VerifyRenderingInServerForm(this);
            }
 
            string oldAccessKey = AccessKey;
            if (!String.IsNullOrEmpty(oldAccessKey)) { 
                AccessKey = String.Empty; 
                base.AddAttributesToRender(writer);
                AccessKey = oldAccessKey; 
            }
            else {
                base.AddAttributesToRender(writer);
            } 
        }
 
        // returns true if the style contains a class name 
        private static bool AppendCssClassName(StringBuilder builder, TreeNodeStyle style, bool hyperlink) {
            bool containsClassName = false; 
            if (style != null) {
                // We have to merge with any CssClass specified on the Style itself
                if (style.CssClass.Length != 0) {
                    builder.Append(style.CssClass); 
                    builder.Append(' ');
                    containsClassName = true; 
                } 

                string className = (hyperlink ? 
                    style.HyperLinkStyle.RegisteredCssClass :
                    style.RegisteredCssClass);
                if (className.Length > 0) {
                    builder.Append(className); 
                    builder.Append(' ');
                } 
            } 
            return containsClassName;
        } 

        private static T CacheGetItem(List cacheList, int index) where T : class {
            Debug.Assert(cacheList != null);
            if (index < cacheList.Count) return cacheList[index]; 
            return null;
        } 
 
        private static void CacheSetItem(List cacheList, int index, T item) where T : class {
            if (cacheList.Count > index) { 
                cacheList[index] = item;
            }
            else {
                for (int i = cacheList.Count; i < index; i++) { 
                    cacheList.Add(null);
                } 
                cacheList.Add(item); 
            }
        } 


        /// 
        ///     Fully collapses all nodes in the tree 
        /// 
        public void CollapseAll() { 
            foreach (TreeNode node in Nodes) { 
                node.CollapseAll();
            } 
        }


        ///  
        ///     Overridden to disallow adding controls
        ///  
        protected override ControlCollection CreateControlCollection() { 
            return new EmptyControlCollection(this);
        } 

        protected virtual internal TreeNode CreateNode() {
            return new TreeNode(this, false);
        } 

 
        ///  
        ///     Creates a tree node ID based on an index
        ///  
        internal string CreateNodeId(int index) {
            return ClientID + "n" + index;
        }
 

        ///  
        ///     Creates a tree node text ID based on an index 
        /// 
        internal string CreateNodeTextId(int index) { 
            return ClientID + "t" + index;
        }

        /// Data bound controls should override PerformDataBinding instead 
        /// of DataBind.  If DataBind if overridden, the OnDataBinding and OnDataBound events will
        /// fire in the wrong order.  However, for backwards compat on ListControl and AdRotator, we 
        /// can't seal this method.  It is sealed on all new BaseDataBoundControl-derived controls. 
        public override sealed void DataBind() {
            base.DataBind(); 
        }


        ///  
        ///     Databinds the specified node to the datasource
        ///  
        private void DataBindNode(TreeNode node) { 
            if (node.PopulateOnDemand && !IsBoundUsingDataSourceID && !DesignMode) {
                throw new InvalidOperationException(SR.GetString(SR.TreeView_PopulateOnlyForDataSourceControls, ID)); 
            }

            HierarchicalDataSourceView view = GetData(node.DataPath);
            // Do nothing if no datasource was set 
            if (!IsBoundUsingDataSourceID && (DataSource == null)) {
                return; 
            } 

            if (view == null) { 
                throw new InvalidOperationException(SR.GetString(SR.TreeView_DataSourceReturnedNullView, ID));
            }
            IHierarchicalEnumerable enumerable = view.Select();
            node.ChildNodes.Clear(); 
            if (enumerable != null) {
                // If we're bound to a SiteMapDataSource, automatically select the node 
                if (IsBoundUsingDataSourceID) { 
                    SiteMapDataSource siteMapDataSource = GetDataSource() as SiteMapDataSource;
                    if (siteMapDataSource != null) { 
                        if (_currentSiteMapNodeDataPath == null) {
                            IHierarchyData currentNodeData = (IHierarchyData)siteMapDataSource.Provider.CurrentNode;
                            if (currentNodeData != null) {
                                _currentSiteMapNodeDataPath = currentNodeData.Path; 
                            }
                            else { 
                                _currentSiteMapNodeDataPath = String.Empty; 
                            }
                        } 
                    }
                }

                DataBindRecursive(node, enumerable, true); 
            }
        } 
 

        ///  
        ///     Databinds recursively, using the TreeView's Bindings collection, until it reaches a TreeNodeBinding
        ///     that is PopulateOnDemand or there is no more data.  Optionally ignores the first level's PopulateOnDemand
        ///     to facilitate populating that level
        ///  
        private void DataBindRecursive(TreeNode node, IHierarchicalEnumerable enumerable, bool ignorePopulateOnDemand) {
            // Since we are binding children, get the level below the current node's depth 
            int depth = checked(node.Depth + 1); 

            // Don't databind beyond the maximum specified depth 
            if ((MaxDataBindDepth != -1) && (depth > MaxDataBindDepth)) {
                return;
            }
 
            foreach (object item in enumerable) {
                IHierarchyData data = enumerable.GetHierarchyData(item); 
 
                string text = null;
                string value = null; 
                string navigateUrl = String.Empty;
                string imageUrl = String.Empty;
                string target = String.Empty;
 
                string toolTip = String.Empty;
                string imageToolTip = String.Empty; 
                TreeNodeSelectAction selectAction = TreeNodeSelectAction.Select; 
                bool? showCheckBox = null;
 
                string dataMember = String.Empty;
                bool populateOnDemand = false;

                dataMember = data.Type; 

                TreeNodeBinding level = DataBindings.GetBinding(dataMember, depth); 
 
                if (level != null) {
                    populateOnDemand = level.PopulateOnDemand; 

                    PropertyDescriptorCollection props = TypeDescriptor.GetProperties(item);

                    // Bind Text, using the static value if necessary 
                    string textField = level.TextField;
                    if (textField.Length > 0) { 
                        PropertyDescriptor desc = props.Find(textField, true); 
                        if (desc != null) {
                            object objData = desc.GetValue(item); 
                            if (objData != null) {
                                if (!String.IsNullOrEmpty(level.FormatString)) {
                                    text = string.Format(CultureInfo.CurrentCulture, level.FormatString, objData);
                                } 
                                else {
                                    text = objData.ToString(); 
                                } 
                            }
                        } 
                        else {
                            throw new InvalidOperationException(SR.GetString(SR.TreeView_InvalidDataBinding, textField, "TextField"));
                        }
                    } 

                    if (String.IsNullOrEmpty(text)) { 
                        text = level.Text; 
                    }
 
                    // Bind Value, using the static value if necessary
                    string valueField = level.ValueField;
                    if (valueField.Length > 0) {
                        PropertyDescriptor desc = props.Find(valueField, true); 
                        if (desc != null) {
                            object objData = desc.GetValue(item); 
                            if (objData != null) { 
                                value = objData.ToString();
                            } 
                        }
                        else {
                            throw new InvalidOperationException(SR.GetString(SR.TreeView_InvalidDataBinding, valueField, "ValueField"));
                        } 
                    }
 
                    if (String.IsNullOrEmpty(value)) { 
                        value = level.Value;
                    } 

                    // Bind ImageUrl, using the static value if necessary
                    string imageUrlField = level.ImageUrlField;
                    if (imageUrlField.Length > 0) { 
                        PropertyDescriptor desc = props.Find(imageUrlField, true);
                        if (desc != null) { 
                            object objData = desc.GetValue(item); 
                            if (objData != null) {
                                imageUrl = objData.ToString(); 
                            }
                        }
                        else {
                            throw new InvalidOperationException(SR.GetString(SR.TreeView_InvalidDataBinding, imageUrlField, "ImageUrlField")); 
                        }
                    } 
 
                    if (imageUrl.Length == 0) {
                        imageUrl = level.ImageUrl; 
                    }

                    // Bind NavigateUrl, using the static value if necessary
                    string navigateUrlField = level.NavigateUrlField; 
                    if (navigateUrlField.Length > 0) {
                        PropertyDescriptor desc = props.Find(navigateUrlField, true); 
                        if (desc != null) { 
                            object objData = desc.GetValue(item);
                            if (objData != null) { 
                                navigateUrl = objData.ToString();
                            }
                        }
                        else { 
                            throw new InvalidOperationException(SR.GetString(SR.TreeView_InvalidDataBinding, navigateUrlField, "NavigateUrlField"));
                        } 
                    } 

                    if (navigateUrl.Length == 0) { 
                        navigateUrl = level.NavigateUrl;
                    }

                    // Bind Target, using the static value if necessary 
                    string targetField = level.TargetField;
                    if (targetField.Length > 0) { 
                        PropertyDescriptor desc = props.Find(targetField, true); 
                        if (desc != null) {
                            object objData = desc.GetValue(item); 
                            if (objData != null) {
                                target = objData.ToString();
                            }
                        } 
                        else {
                            throw new InvalidOperationException(SR.GetString(SR.TreeView_InvalidDataBinding, targetField, "TargetField")); 
                        } 
                    }
 
                    if (String.IsNullOrEmpty(target)) {
                        target = level.Target;
                    }
 
                    // Bind ToolTip, using the static value if necessary
                    string toolTipField = level.ToolTipField; 
                    if (toolTipField.Length > 0) { 
                        PropertyDescriptor desc = props.Find(toolTipField, true);
                        if (desc != null) { 
                            object objData = desc.GetValue(item);
                            if (objData != null) {
                                toolTip = objData.ToString();
                            } 
                        }
                        else { 
                            throw new InvalidOperationException(SR.GetString(SR.TreeView_InvalidDataBinding, toolTipField, "ToolTipField")); 
                        }
                    } 

                    if (toolTip.Length == 0) {
                        toolTip = level.ToolTip;
                    } 

                    // Bind ImageToolTip, using the static value if necessary 
                    string imageToolTipField = level.ImageToolTipField; 

                    if (imageToolTipField.Length > 0) { 
                        PropertyDescriptor desc = props.Find(imageToolTipField, true);

                        if (desc != null) {
                            object objData = desc.GetValue(item); 

                            if (objData != null) { 
                                imageToolTip = objData.ToString(); 
                            }
                        } 
                        else {
                            throw new InvalidOperationException(SR.GetString(SR.TreeView_InvalidDataBinding, imageToolTipField, "imageToolTipField"));
                        }
                    } 

                    if (imageToolTip.Length == 0) { 
                        imageToolTip = level.ImageToolTip; 
                    }
 
                    // Set the other static properties
                    selectAction = level.SelectAction;
                    showCheckBox = level.ShowCheckBox;
                } 
                else {
                    if (item is INavigateUIData) { 
                        INavigateUIData navigateUIData = (INavigateUIData)item; 
                        text = navigateUIData.Name;
                        value = navigateUIData.Value; 
                        navigateUrl = navigateUIData.NavigateUrl;
                        if (String.IsNullOrEmpty(navigateUrl)) {
                            selectAction = TreeNodeSelectAction.None;
                        } 
                        toolTip = navigateUIData.Description;
                    } 
                    if (IsBoundUsingDataSourceID) { 
                        populateOnDemand = PopulateNodesFromClient;
                    } 
                }

                if (AutoGenerateDataBindings && (text == null)) {
                    text = item.ToString(); 
                }
 
                TreeNode newNode = null; 
                // Allow String.Empty for the text, but not null
                if ((text != null) || (value != null)) { 
                    newNode = CreateNode();
                    if (!String.IsNullOrEmpty(text)) {
                        newNode.Text = text;
                    } 
                    if (!String.IsNullOrEmpty(value)) {
                        newNode.Value = value; 
                    } 
                    if (!String.IsNullOrEmpty(imageUrl)) {
                        newNode.ImageUrl = imageUrl; 
                    }
                    if (!String.IsNullOrEmpty(navigateUrl)) {
                        newNode.NavigateUrl = navigateUrl;
                    } 
                    if (!String.IsNullOrEmpty(target)) {
                        newNode.Target = target; 
                    } 
                }
 
                if (newNode != null) {
                    if (!String.IsNullOrEmpty(toolTip)) {
                        newNode.ToolTip = toolTip;
                    } 

                    if (!String.IsNullOrEmpty(imageToolTip)) { 
                        newNode.ImageToolTip = imageToolTip; 
                    }
 
                    if (selectAction != newNode.SelectAction) {
                        newNode.SelectAction = selectAction;
                    }
 
                    if (showCheckBox != null) {
                        newNode.ShowCheckBox = showCheckBox; 
                    } 

                    newNode.SetDataPath(data.Path); 
                    newNode.SetDataBound(true);

                    node.ChildNodes.Add(newNode);
 
                    if (String.Equals(data.Path, _currentSiteMapNodeDataPath, StringComparison.OrdinalIgnoreCase)) {
                        newNode.Selected = true; 
 
                        // Make sure the newly selected node's parents are expanded
                        if ((Page == null) || !Page.IsCallback) { 
                            TreeNode newNodeParent = newNode.Parent;
                            while (newNodeParent != null) {
                                if (newNodeParent.Expanded != true) {
                                    newNodeParent.Expanded = true; 
                                }
 
                                newNodeParent = newNodeParent.Parent; 
                            }
                        } 
                    }

                    // Make sure we call user code if they've hooked the populate event
                    newNode.SetDataItem(data.Item); 
                    OnTreeNodeDataBound(new TreeNodeEventArgs(newNode));
                    newNode.SetDataItem(null); 
 
                    if ((data.HasChildren) && ((MaxDataBindDepth == -1) || (depth < MaxDataBindDepth))) {
                        if (populateOnDemand && !DesignMode) { 
                            newNode.PopulateOnDemand = true;
                        }
                        else {
                            IHierarchicalEnumerable newEnumerable = data.GetChildren(); 
                            if (newEnumerable != null) {
                                DataBindRecursive(newNode, newEnumerable, false); 
                            } 
                        }
                    } 
                }
            }
        }
 
        /// 
        ///     Make sure we are set up to render 
        ///  
        private void EnsureRenderSettings() {
            HttpBrowserCapabilities caps = Page.Request.Browser; 
            _isNotIE = (Page.Request.Browser.MSDomVersion.Major < 4);
            _renderClientScript = GetRenderClientScript(caps);

            if (_hoverNodeStyle != null && Page != null && Page.Header == null) { 
                throw new InvalidOperationException(SR.GetString(SR.NeedHeader, "TreeView.HoverStyle"));
            } 
 
            if (Page != null && (Page.SupportsStyleSheets ||
                Page.IsCallback || (Page.ScriptManager != null && Page.ScriptManager.IsInAsyncPostBack))) { 
                // Register the styles. NB the order here is important: later wins over earlier
                RegisterStyle(BaseTreeNodeStyle);

                // It's also vitally important to register hyperlinkstyles BEFORE 
                // their associated styles as we need to copy the data from this style
                // and a registered style appears empty except for RegisteredClassName 
                if (_nodeStyle != null) { 
                    _nodeStyle.HyperLinkStyle.DoNotRenderDefaults = true;
                    RegisterStyle(_nodeStyle.HyperLinkStyle); 
                    RegisterStyle(_nodeStyle);
                }

                if (_rootNodeStyle != null) { 
                    _rootNodeStyle.HyperLinkStyle.DoNotRenderDefaults = true;
                    RegisterStyle(_rootNodeStyle.HyperLinkStyle); 
                    RegisterStyle(_rootNodeStyle); 
                }
 
                if (_parentNodeStyle != null) {
                    _parentNodeStyle.HyperLinkStyle.DoNotRenderDefaults = true;
                    RegisterStyle(_parentNodeStyle.HyperLinkStyle);
                    RegisterStyle(_parentNodeStyle); 
                }
 
                if (_leafNodeStyle != null) { 
                    _leafNodeStyle.HyperLinkStyle.DoNotRenderDefaults = true;
                    RegisterStyle(_leafNodeStyle.HyperLinkStyle); 
                    RegisterStyle(_leafNodeStyle);
                }

                foreach (TreeNodeStyle style in LevelStyles) { 
                    style.HyperLinkStyle.DoNotRenderDefaults = true;
                    RegisterStyle(style.HyperLinkStyle); 
                    RegisterStyle(style); 
                }
 
                if (_selectedNodeStyle != null) {
                    _selectedNodeStyle.HyperLinkStyle.DoNotRenderDefaults = true;
                    RegisterStyle(_selectedNodeStyle.HyperLinkStyle);
                    RegisterStyle(_selectedNodeStyle); 
                }
 
                if (_hoverNodeStyle != null) { 
                    _hoverNodeHyperLinkStyle = new HyperLinkStyle(_hoverNodeStyle);
                    _hoverNodeHyperLinkStyle.DoNotRenderDefaults = true; 
                    RegisterStyle(_hoverNodeHyperLinkStyle);
                    RegisterStyle(_hoverNodeStyle);
                }
            } 
        }
 
 
        /// 
        ///     Fully expands all nodes in the tree 
        /// 
        public void ExpandAll() {
            foreach (TreeNode node in Nodes) {
                node.ExpandAll(); 
            }
        } 
 
        private void ExpandToDepth(TreeNodeCollection nodes, int depth) {
            // Reset the memory that the tree was never expanded (VSWhidbey 349279) 
            ViewState["NeverExpanded"] = null;

            foreach (TreeNode node in nodes) {
                if ((depth == -1) || (node.Depth < depth)) { 
                    // Only expanding nodes that have not been set, not those that have explicit Expanded=False.
                    if (node.Expanded == null) { 
                        node.Expanded = true; 
                        // No need to populate as setting Expanded to true already does the job.
                    } 
                    ExpandToDepth(node.ChildNodes, depth);
                }
            }
        } 

 
        public TreeNode FindNode(string valuePath) { 
            if (valuePath == null) {
                return null; 
            }
            return Nodes.FindNode(valuePath.Split(PathSeparator), 0);
        }
 
        internal string GetCssClassName(TreeNode node, bool hyperLink) {
            bool discarded; 
            return GetCssClassName(node, hyperLink, out discarded); 
        }
 
        internal string GetCssClassName(TreeNode node, bool hyperLink, out bool containsClassName) {
            if (node == null) {
                throw new ArgumentNullException("node");
            } 

            containsClassName = false; 
            int depth = node.Depth; 
            bool parent = node.ChildNodes.Count != 0 || node.PopulateOnDemand;
            List cache = parent ? 
                (hyperLink ? CachedParentNodeHyperLinkClassNames : CachedParentNodeClassNames) :
                (hyperLink ? CachedLeafNodeHyperLinkClassNames : CachedLeafNodeClassNames);
            string baseClassName = CacheGetItem(cache, depth);
            if (CachedLevelsContainingCssClass.Contains(depth)) { 
                containsClassName = true;
            } 
 
            bool needsSelectedStyle = node.Selected && _selectedNodeStyle != null;
 
            if (!needsSelectedStyle && (baseClassName != null)) {
                return baseClassName;
            }
 
            StringBuilder builder = new StringBuilder();
            if (baseClassName != null) { 
                builder.Append(baseClassName); 
                builder.Append(' ');
            } 
            else {
                // No cached style, so build it
                if (hyperLink) {
                    builder.Append(BaseTreeNodeStyle.RegisteredCssClass); 
                    builder.Append(' ');
                } 
 
                containsClassName |= AppendCssClassName(builder, _nodeStyle, hyperLink);
 
                if (depth < LevelStyles.Count && LevelStyles[depth] != null) {
                    containsClassName |= AppendCssClassName(builder, (TreeNodeStyle)LevelStyles[depth], hyperLink);
                }
                if (depth == 0 && parent) { 
                    containsClassName |= AppendCssClassName(builder, _rootNodeStyle, hyperLink);
                } 
                else if (parent) { 
                    containsClassName |= AppendCssClassName(builder, _parentNodeStyle, hyperLink);
                } 
                else {
                    containsClassName |= AppendCssClassName(builder, _leafNodeStyle, hyperLink);
                }
 
                baseClassName = builder.ToString().Trim();
                CacheSetItem(cache, depth, baseClassName); 
                if (containsClassName && !CachedLevelsContainingCssClass.Contains(depth)) { 
                    CachedLevelsContainingCssClass.Add(depth);
                } 
            }

            if (needsSelectedStyle) {
                containsClassName |= AppendCssClassName(builder, _selectedNodeStyle, hyperLink); 
                return builder.ToString().Trim(); ;
            } 
            return baseClassName; 
        }
 

        /// 
        ///     Gets the URL for the specified image, properly pathing the image filename depending on which image it is
        ///  
        internal string GetImageUrl(int index) {
            if (ImageUrls[index] == null) { 
                switch (index) { 
                    case RootImageIndex:
                        string rootNodeImageUrl = RootNodeStyle.ImageUrl; 
                        if (rootNodeImageUrl.Length == 0) {
                            rootNodeImageUrl = NodeStyle.ImageUrl;
                        }
                        if (rootNodeImageUrl.Length == 0) { 
                            switch (ImageSet) {
                                case TreeViewImageSet.BulletedList: { 
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList_RootNode.gif"); 
                                        break;
                                    } 
                                case TreeViewImageSet.BulletedList2: {
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList2_RootNode.gif");
                                        break;
                                    } 
                                case TreeViewImageSet.BulletedList3: {
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList3_RootNode.gif"); 
                                        break; 
                                    }
                                case TreeViewImageSet.BulletedList4: { 
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList4_RootNode.gif");
                                        break;
                                    }
                                case TreeViewImageSet.News: { 
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_News_RootNode.gif");
                                        break; 
                                    } 
                                case TreeViewImageSet.Inbox: {
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Inbox_RootNode.gif"); 
                                        break;
                                    }
                                case TreeViewImageSet.Events: {
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Events_RootNode.gif"); 
                                        break;
                                    } 
                                case TreeViewImageSet.Faq: { 
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_FAQ_RootNode.gif");
                                        break; 
                                    }
                                case TreeViewImageSet.XPFileExplorer: {
                                        rootNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_XP_Explorer_RootNode.gif");
                                        break; 
                                    }
                            } 
                        } 

                        if (rootNodeImageUrl.Length != 0) { 
                            rootNodeImageUrl = ResolveClientUrl(rootNodeImageUrl);
                        }
                        ImageUrls[index] = rootNodeImageUrl;
                        break; 
                    case ParentImageIndex:
                        string parentNodeImageUrl = ParentNodeStyle.ImageUrl; 
                        if (parentNodeImageUrl.Length == 0) { 
                            parentNodeImageUrl = NodeStyle.ImageUrl;
                        } 
                        if (parentNodeImageUrl.Length == 0) {
                            switch (ImageSet) {
                                case TreeViewImageSet.BulletedList: {
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList_ParentNode.gif"); 
                                        break;
                                    } 
                                case TreeViewImageSet.BulletedList2: { 
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList2_ParentNode.gif");
                                        break; 
                                    }
                                case TreeViewImageSet.BulletedList3: {
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList3_ParentNode.gif");
                                        break; 
                                    }
                                case TreeViewImageSet.BulletedList4: { 
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList4_ParentNode.gif"); 
                                        break;
                                    } 
                                case TreeViewImageSet.News: {
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_News_ParentNode.gif");
                                        break;
                                    } 
                                case TreeViewImageSet.Inbox: {
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Inbox_ParentNode.gif"); 
                                        break; 
                                    }
                                case TreeViewImageSet.Events: { 
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Events_ParentNode.gif");
                                        break;
                                    }
                                case TreeViewImageSet.Faq: { 
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_FAQ_ParentNode.gif");
                                        break; 
                                    } 
                                case TreeViewImageSet.XPFileExplorer: {
                                        parentNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_XP_Explorer_ParentNode.gif"); 
                                        break;
                                    }
                            }
                        } 

 
                        if (parentNodeImageUrl.Length != 0) { 
                            parentNodeImageUrl = ResolveClientUrl(parentNodeImageUrl);
                        } 
                        ImageUrls[index] = parentNodeImageUrl;
                        break;
                    case LeafImageIndex:
                        string leafNodeImageUrl = LeafNodeStyle.ImageUrl; 
                        if (leafNodeImageUrl.Length == 0) {
                            leafNodeImageUrl = NodeStyle.ImageUrl; 
                        } 
                        if (leafNodeImageUrl.Length == 0) {
                            switch (ImageSet) { 
                                case TreeViewImageSet.BulletedList: {
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList_LeafNode.gif");
                                        break;
                                    } 
                                case TreeViewImageSet.BulletedList2: {
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList2_LeafNode.gif"); 
                                        break; 
                                    }
                                case TreeViewImageSet.BulletedList3: { 
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList3_LeafNode.gif");
                                        break;
                                    }
                                case TreeViewImageSet.BulletedList4: { 
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_BulletedList4_LeafNode.gif");
                                        break; 
                                    } 
                                case TreeViewImageSet.News: {
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_News_LeafNode.gif"); 
                                        break;
                                    }
                                case TreeViewImageSet.Inbox: {
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Inbox_LeafNode.gif"); 
                                        break;
                                    } 
                                case TreeViewImageSet.Events: { 
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Events_LeafNode.gif");
                                        break; 
                                    }
                                case TreeViewImageSet.Faq: {
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_FAQ_LeafNode.gif");
                                        break; 
                                    }
                                case TreeViewImageSet.XPFileExplorer: { 
                                        leafNodeImageUrl = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_XP_Explorer_LeafNode.gif"); 
                                        break;
                                    } 
                            }
                        }

                        if (leafNodeImageUrl.Length != 0) { 
                            leafNodeImageUrl = ResolveClientUrl(leafNodeImageUrl);
                        } 
                        ImageUrls[index] = leafNodeImageUrl; 
                        break;
                    case NoExpandImageIndex: 
                        if (ShowLines) {
                            if (LineImagesFolder.Length == 0) {
                                ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_NoExpand.gif");
                            } 
                            else {
                                ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "noexpand.gif")); 
                            } 
                        }
                        else { 
                            if (NoExpandImageUrlInternal.Length > 0) {
                                ImageUrls[index] = ResolveClientUrl(NoExpandImageUrlInternal);
                            }
                            else if (LineImagesFolder.Length > 0) { 
                                ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "noexpand.gif"));
                            } 
                            else { 
                                ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_NoExpand.gif");
                            } 
                        }
                        break;

                    case PlusImageIndex: 
                        if (ShowLines) {
                            if (LineImagesFolder.Length == 0) { 
                                ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_Expand.gif"); 
                            }
                            else { 
                                ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "plus.gif"));
                            }
                        }
                        else { 
                            if (ExpandImageUrlInternal.Length > 0) {
                                ImageUrls[index] = ResolveClientUrl(ExpandImageUrlInternal); 
                            } 
                            else if (LineImagesFolder.Length > 0) {
                                ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "plus.gif")); 
                            }
                            else {
                                ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_Expand.gif");
                            } 
                        }
                        break; 
                    case MinusImageIndex: 
                        if (ShowLines) {
                            if (LineImagesFolder.Length == 0) { 
                                ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_Collapse.gif");
                            }
                            else {
                                ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "minus.gif")); 
                            }
                        } 
                        else { 
                            if (CollapseImageUrlInternal.Length > 0) {
                                ImageUrls[index] = ResolveClientUrl(CollapseImageUrlInternal); 
                            }
                            else if (LineImagesFolder.Length > 0) {
                                ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "minus.gif"));
                            } 
                            else {
                                ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_Collapse.gif"); 
                            } 
                        }
                        break; 
                    case IImageIndex:
                        if (LineImagesFolder.Length == 0) {
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_I.gif");
                        } 
                        else {
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "i.gif")); 
                        } 
                        break;
                    case RImageIndex: 
                        if (LineImagesFolder.Length == 0) {
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_R.gif");
                        }
                        else { 
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "r.gif"));
                        } 
                        break; 
                    case RPlusImageIndex:
                        if (LineImagesFolder.Length == 0) { 
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_RExpand.gif");
                        }
                        else {
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "rplus.gif")); 
                        }
                        break; 
                    case RMinusImageIndex: 
                        if (LineImagesFolder.Length == 0) {
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_RCollapse.gif"); 
                        }
                        else {
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "rminus.gif"));
                        } 
                        break;
                    case TImageIndex: 
                        if (LineImagesFolder.Length == 0) { 
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_T.gif");
                        } 
                        else {
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "t.gif"));
                        }
                        break; 
                    case TPlusImageIndex:
                        if (LineImagesFolder.Length == 0) { 
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_TExpand.gif"); 
                        }
                        else { 
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "tplus.gif"));
                        }
                        break;
                    case TMinusImageIndex: 
                        if (LineImagesFolder.Length == 0) {
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_TCollapse.gif"); 
                        } 
                        else {
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "tminus.gif")); 
                        }
                        break;
                    case LImageIndex:
                        if (LineImagesFolder.Length == 0) { 
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_L.gif");
                        } 
                        else { 
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "l.gif"));
                        } 
                        break;
                    case LPlusImageIndex:
                        if (LineImagesFolder.Length == 0) {
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_LExpand.gif"); 
                        }
                        else { 
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "lplus.gif")); 
                        }
                        break; 
                    case LMinusImageIndex:
                        if (LineImagesFolder.Length == 0) {
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_LCollapse.gif");
                        } 
                        else {
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "lminus.gif")); 
                        } 
                        break;
                    case DashImageIndex: 
                        if (LineImagesFolder.Length == 0) {
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_Dash.gif");
                        }
                        else { 
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "dash.gif"));
                        } 
                        break; 
                    case DashPlusImageIndex:
                        if (LineImagesFolder.Length == 0) { 
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_DashExpand.gif");
                        }
                        else {
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "dashplus.gif")); 
                        }
                        break; 
                    case DashMinusImageIndex: 
                        if (LineImagesFolder.Length == 0) {
                            ImageUrls[index] = Page.ClientScript.GetWebResourceUrl(typeof(TreeView), "TreeView_Default_DashCollapse.gif"); 
                        }
                        else {
                            ImageUrls[index] = ResolveClientUrl(UrlPath.SimpleCombine(LineImagesFolder, "dashminus.gif"));
                        } 
                        break;
                } 
            } 

            return ImageUrls[index]; 
        }

        internal string GetLevelImageUrl(int index) {
            if (LevelImageUrls[index] == null) { 
                string imageUrl = ((TreeNodeStyle)LevelStyles[index]).ImageUrl;
                if (imageUrl.Length > 0) { 
                    LevelImageUrls[index] = ResolveClientUrl(imageUrl); 
                }
                else { 
                    LevelImageUrls[index] = String.Empty;
                }
            }
 
            return LevelImageUrls[index];
        } 
 
        // After calling this, style1 has a merged class name,
        // and all properties explicitly set on style2 replace those on style1. 
        // Also used by Menu
        internal static void GetMergedStyle(Style style1, Style style2) {
            string oldClass = style1.CssClass;
            style1.CopyFrom(style2); 
            if (oldClass.Length != 0 && style2.CssClass.Length != 0) {
                style1.CssClass += ' ' + oldClass; 
            } 
        }
 
        private bool GetRenderClientScript(HttpBrowserCapabilities caps) {
            return (EnableClientScript &&
                    Enabled &&
                    (caps.EcmaScriptVersion.Major > 0) && 
                    (caps.W3CDomVersion.Major > 0) &&
#if SHIPPINGADAPTERS 
                    !(Page.PageAdapter is Html32PageAdapter) && 
#endif
 !StringUtil.EqualsIgnoreCase(caps["tagwriter"], typeof(Html32TextWriter).FullName)); 
        }

        internal TreeNodeStyle GetStyle(TreeNode node) {
            if (node == null) { 
                throw new ArgumentNullException("node");
            } 
 
            bool parent = node.ChildNodes.Count != 0 || node.PopulateOnDemand;
            List cache = parent ? CachedParentNodeStyles : CachedLeafNodeStyles; 
            bool needsSelectedStyle = node.Selected && _selectedNodeStyle != null;

            int depth = node.Depth;
            TreeNodeStyle typedStyle = CacheGetItem(cache, depth); 

            if (!needsSelectedStyle && typedStyle != null) return typedStyle; 
 
            if (typedStyle == null) {
                typedStyle = new TreeNodeStyle(); 
                typedStyle.CopyFrom(BaseTreeNodeStyle);

                if (_nodeStyle != null) {
                    GetMergedStyle(typedStyle, _nodeStyle); 
                }
 
                if (depth == 0 && parent) { 
                    if (_rootNodeStyle != null) {
                        GetMergedStyle(typedStyle, _rootNodeStyle); 
                    }
                }
                else if (parent) {
                    if (_parentNodeStyle != null) { 
                        GetMergedStyle(typedStyle, _parentNodeStyle);
                    } 
                } 
                else if (_leafNodeStyle != null) {
                    GetMergedStyle(typedStyle, _leafNodeStyle); 
                }

                if (depth < LevelStyles.Count && LevelStyles[depth] != null) {
                    GetMergedStyle(typedStyle, LevelStyles[depth]); 
                }
 
                CacheSetItem(cache, depth, typedStyle); 
            }
 

            if (needsSelectedStyle) {
                TreeNodeStyle selectedStyle = new TreeNodeStyle();
                selectedStyle.CopyFrom(typedStyle); 
                GetMergedStyle(selectedStyle, _selectedNodeStyle);
                return selectedStyle; 
            } 
            return typedStyle;
        } 

        private int GetTrailingIndex(string s) {
            int i = s.Length - 1;
            while (i > 0) { 
                if (!Char.IsDigit(s[i])) {
                    break; 
                } 
                i--;
            } 

            if ((i > -1) && (i < (s.Length - 1)) && ((s.Length - i) < 11)) {
                return Int32.Parse(s.Substring(i + 1), CultureInfo.InvariantCulture);
            } 

            return -1; 
        } 

        internal static string Escape(string value) { 
            // This function escapes \ and | to avoid collisions with the internal path separator.
            // Also used by Menu
            StringBuilder b = null;
 
            if (String.IsNullOrEmpty(value)) {
                return String.Empty; 
            } 

            int startIndex = 0; 
            int count = 0;
            for (int i = 0; i < value.Length; i++) {
                switch (value[i]) {
                    case InternalPathSeparator: 
                        if (b == null) {
                            b = new StringBuilder(value.Length + 5); 
                        } 

                        if (count > 0) { 
                            b.Append(value, startIndex, count);
                        }

                        b.Append(EscapeSequenceForPathSeparator); 

                        startIndex = i + 1; 
                        count = 0; 
                        break;
                    case EscapeCharacter: 
                        if (b == null) {
                            b = new StringBuilder(value.Length + 5);
                        }
 
                        if (count > 0) {
                            b.Append(value, startIndex, count); 
                        } 

                        b.Append(EscapeSequenceForEscapeCharacter); 

                        startIndex = i + 1;
                        count = 0;
                        break; 
                    default:
                        count++; 
                        break; 
                }
            } 

            if (b == null) {
                return value;
            } 

            if (count > 0) { 
                b.Append(value, startIndex, count); 
            }
 
            return b.ToString();
        }

        internal static string UnEscape(string value) { 
            // Also used by Menu
            return value.Replace( 
                EscapeSequenceForPathSeparator, InternalPathSeparator.ToString()).Replace( 
                EscapeSequenceForEscapeCharacter, EscapeCharacter.ToString());
        } 

        /// 
        ///     Loads a nodes state from the postback data.  Basically, there are expand state (which may have changed on the client) and
        ///     check state.  It also fills a dictionary of nodes that were populated on the client (and need to be populated on the server). 
        /// 
        private void LoadNodeState(TreeNode node, ref int index, string expandState, IDictionary populatedNodes, int selectedNodeIndex) { 
            // If our populatedNodes dictionary contains the index for the current node, that means 
            // it was populated on the client-side and needs to have it's child node states also updated
            if (PopulateNodesFromClient && (populatedNodes != null)) { 
                if (populatedNodes.Contains(index)) {
                    populatedNodes[index] = node;
                }
            } 

            // If nothing was posted, selectedNodeIndex will be -1 
            if (selectedNodeIndex != -1) { 
                // When something was posted, update to the new selected node
                if (node.Selected && (index != selectedNodeIndex)) { 
                    node.Selected = false;
                }

                if ((index == selectedNodeIndex) && 
                    ((node.SelectAction == TreeNodeSelectAction.Select) ||
                    (node.SelectAction == TreeNodeSelectAction.SelectExpand))) { 
 
                    bool oldSelected = node.Selected;
 
                    node.Selected = true;

                    if (!oldSelected) {
                        _fireSelectedNodeChanged = true; 
                    }
                } 
            } 
            else if (node.Selected) {
                // Otherwise, just reselect the old selected node 
                SetSelectedNode(node);
            }

            // Check if the node's checked state has changed since the last postback 
            // But only if the node has checkbox UI (VSWhidbey 421233)
            if (node.GetEffectiveShowCheckBox()) { 
                bool originalChecked = node.Checked; 
                string checkBoxFieldID = CreateNodeId(index) + "CheckBox";
                if ((Context.Request.Form[checkBoxFieldID] != null) || 
                    (Context.Request.QueryString[checkBoxFieldID] != null)) {
                    if (!node.Checked) {
                        node.Checked = true;
                    } 
                    if (originalChecked != node.Checked) {
                        CheckedChangedNodes.Add(node); 
                    } 
                }
                else { 
                    if (originalChecked && !node.PreserveChecked) {
                        if (node.Checked) {
                            node.Checked = false;
                        } 
                    }
 
                    if (originalChecked != node.Checked) { 
                        CheckedChangedNodes.Add(node);
                    } 
                }
            }

            // Get the client-side expand state of the current node 
            if ((Page != null) && (Page.RequestInternal != null) &&
                (expandState != null) && 
                (expandState.Length > index) && 
                (ShowExpandCollapse ||
                (node.SelectAction == TreeNodeSelectAction.Expand) || 
                (node.SelectAction == TreeNodeSelectAction.SelectExpand))) {

                char c = expandState[index];
                switch (c) { 
                    case 'e':
                        node.Expanded = true; 
                        break; 
                    case 'c':
                        node.Expanded = false; 
                        break;
                    //case 'n': case 'u':
                    //    break;
                } 
            }
 
            index++; 

            // If there were children for this node, load their states too 
            TreeNodeCollection nodes = node.ChildNodes;
            if (nodes.Count > 0) {
                for (int i = 0; i < nodes.Count; i++) {
                    LoadNodeState(nodes[i], ref index, expandState, populatedNodes, selectedNodeIndex); 
                }
            } 
        } 

 
        /// 
        /// 
        /// Loads a saved state of the .
        ///  
        protected override void LoadViewState(object state) {
            if (state != null) { 
                object[] savedState = (object[])state; 

                if (savedState[0] != null) { 
                    base.LoadViewState(savedState[0]);
                }

                if (savedState[1] != null) { 
                    ((IStateManager)NodeStyle).LoadViewState(savedState[1]);
                } 
 
                if (savedState[2] != null) {
                    ((IStateManager)RootNodeStyle).LoadViewState(savedState[2]); 
                }

                if (savedState[3] != null) {
                    ((IStateManager)ParentNodeStyle).LoadViewState(savedState[3]); 
                }
 
                if (savedState[4] != null) { 
                    ((IStateManager)LeafNodeStyle).LoadViewState(savedState[4]);
                } 

                if (savedState[5] != null) {
                    ((IStateManager)SelectedNodeStyle).LoadViewState(savedState[5]);
                } 

                if (savedState[6] != null) { 
                    ((IStateManager)HoverNodeStyle).LoadViewState(savedState[6]); 
                }
 
                if (savedState[7] != null) {
                    ((IStateManager)LevelStyles).LoadViewState(savedState[7]);
                }
 
                if (savedState[8] != null) {
                    ((IStateManager)Nodes).LoadViewState(savedState[8]); 
                } 
            }
        } 

        protected internal override void OnInit(EventArgs e) {
            ChildControlsCreated = true;
            base.OnInit(e); 
        }
 
        protected virtual void OnTreeNodeCheckChanged(TreeNodeEventArgs e) { 
            TreeNodeEventHandler handler = (TreeNodeEventHandler)Events[CheckChangedEvent];
            if (handler != null) { 
                handler(this, e);
            }
        }
 

        ///  
        ///     Overridden to register for postback, and if client script is enabled, renders out 
        ///     the necessary script and hidden field to function.
        ///  
        protected internal override void OnPreRender(EventArgs e) {
            base.OnPreRender(e);

            EnsureRenderSettings(); 

            if (Page != null) { 
                if (!Page.IsPostBack && !_dataBound) { 
                    ExpandToDepth(Nodes, ExpandDepth);
                } 

                Page.RegisterRequiresPostBack(this);

                // Build up a hidden field of the expand state of all nodes 
                StringBuilder expandState = new StringBuilder();
 
                // We need to number all of the nodes, so call save node state. 
                int index = 0;
                for (int i = 0; i < Nodes.Count; i++) { 
                    SaveNodeState(Nodes[i], ref index, expandState, true);
                }

                if (RenderClientScript) { 
                    ClientScriptManager scriptOM = Page.ClientScript;
                    scriptOM.RegisterHiddenField(this, ExpandStateID, expandState.ToString()); 
 
                    // Register all the images (including lines if necessary)
                    int imageCount = 6; 
                    if (ShowLines) {
                        imageCount = 19;
                    }
                    for (int i = 0; i < imageCount; i++) { 
                        string imageUrl = GetImageUrl(i);
                        if (imageUrl.Length > 0) { 
                            imageUrl = Util.QuoteJScriptString(imageUrl); 
                        }
                        scriptOM.RegisterArrayDeclaration(this, ImageArrayID, "'" + imageUrl + "'"); 
                    }

                    // Register a hidden field for tracking the selected node and save it in viewstate so we can fire changed events on postback
                    string selectedNodeID = String.Empty; 
                    if (SelectedNode != null) {
                        // Validate that the selected node has not been removed 
                        TreeNode node = SelectedNode; 
                        while ((node != null) && (node != RootNode)) {
                            node = node.GetParentInternal(); 
                        }

                        if (node == RootNode) {
                            selectedNodeID = SelectedNode.SelectID; 
                            ViewState["SelectedNode"] = SelectedNode.SelectID;
                        } 
                        else { 
                            ViewState["SelectedNode"] = null;
                        } 
                    }
                    else {
                        ViewState["SelectedNode"] = null;
                    } 

                    scriptOM.RegisterHiddenField(this, SelectedNodeFieldID, selectedNodeID); 
 
                    // TreeView.js depends on WebForms.js so register that too.
                    Page.RegisterWebFormsScript(); 
                    // Register the external TreeView javascript file.
                    scriptOM.RegisterClientScriptResource(this, typeof(TreeView), "TreeView.js");

                    string clientDataObjectID = ClientDataObjectID; 

                    string populateStartupScript = String.Empty; 
                    if (PopulateNodesFromClient) { 
                        // Remember the max index of the nodes, so we can properly restore client-populated nodes on postback
                        ViewState["LastIndex"] = index; 

                        // Register a log for client-populated nodes
                        scriptOM.RegisterHiddenField(this, PopulateLogID, String.Empty);
 
                        populateStartupScript = clientDataObjectID + ".lastIndex = " + index + ";\r\n" +
                                                clientDataObjectID + ".populateLog = theForm.elements['" + PopulateLogID + "'];\r\n" + 
                                                clientDataObjectID + ".treeViewID = '" + UniqueID + "';\r\n" + 
                                                clientDataObjectID + ".name = '" + clientDataObjectID + "';\r\n";
                        // Using GetType() here instead of typeof because derived TreeViews might conflict 
                        if (!scriptOM.IsClientScriptBlockRegistered(GetType(), "PopulateNode")) {
                            //
                            scriptOM.RegisterClientScriptBlock(this, GetType(), "PopulateNode",
                            populateNodeScript + 
                            scriptOM.GetCallbackEventReference("context.data.treeViewID", "param", "TreeView_ProcessNodeData",
                                                               "context", "TreeView_ProcessNodeData", false) + 
                            populateNodeScriptEnd, 
                            true /* add script tags */);
                        } 
                    }
                    string selectedInfo = String.Empty;
                    if (_selectedNodeStyle != null) {
                        string className = _selectedNodeStyle.RegisteredCssClass; 
                        if (className.Length > 0) {
                            className += " "; 
                        } 
                        string hyperLinkClassName = _selectedNodeStyle.HyperLinkStyle.RegisteredCssClass;
                        if (hyperLinkClassName.Length > 0) { 
                            hyperLinkClassName += " ";
                        }
                        if (!String.IsNullOrEmpty(_selectedNodeStyle.CssClass)) {
                            string cssClass = _selectedNodeStyle.CssClass + " "; 
                            className += cssClass;
                            hyperLinkClassName += cssClass; 
                        } 
                        selectedInfo = clientDataObjectID + ".selectedClass = '" + className + "';\r\n" +
                                       clientDataObjectID + ".selectedHyperLinkClass = '" + hyperLinkClassName + "';\r\n"; 
                    }

                    string hoverInfo = String.Empty;
                    if (EnableHover) { 
                        string className = _hoverNodeStyle.RegisteredCssClass;
                        string hyperLinkClassName = _hoverNodeHyperLinkStyle.RegisteredCssClass; 
                        if (!String.IsNullOrEmpty(_hoverNodeStyle.CssClass)) { 
                            string cssClass = _hoverNodeStyle.CssClass;
                            if (!String.IsNullOrEmpty(className)) { 
                                className += " ";
                            }
                            if (!String.IsNullOrEmpty(hyperLinkClassName)) {
                                hyperLinkClassName += " "; 
                            }
                            className += cssClass; 
                            hyperLinkClassName += cssClass; 
                        }
 
                        selectedInfo = clientDataObjectID + ".hoverClass = '" + className + "';\r\n" +
                                       clientDataObjectID + ".hoverHyperLinkClass = '" + hyperLinkClassName + "';\r\n";
                    }
 
                    string createDataObjectScript = "var " + clientDataObjectID + " = new Object();\r\n" +
                                                    clientDataObjectID + ".images = " + ImageArrayID + ";\r\n" + 
                                                    clientDataObjectID + ".collapseToolTip = \"" 
                                                        + Util.QuoteJScriptString(CollapseImageToolTip) + "\";\r\n" +
                                                    clientDataObjectID + ".expandToolTip = \"" 
                                                        + Util.QuoteJScriptString(ExpandImageToolTip) + "\";\r\n" +
                                                    clientDataObjectID + ".expandState = theForm.elements['" + ExpandStateID + "'];\r\n" +
                                                    clientDataObjectID + ".selectedNodeID = theForm.elements['" + SelectedNodeFieldID + "'];\r\n" +
                                                    selectedInfo + 
                                                    hoverInfo +
                                                    "for (var i=0;i<" + imageCount + ";i++) {\r\n" + 
                                                    "var preLoad = new Image();\r\n" + 
                                                    "if (" + ImageArrayID + "[i].length > 0)\r\n" +
                                                    "preLoad.src = " + ImageArrayID + "[i];\r\n" + 
                                                    "}\r\n" + populateStartupScript;

                    // Register a startup script that creates a tree data object
                    // Note: the first line is to prevent Firefox warnings on undeclared identifiers, needed if a user event occurs 
                    // before all startup scripts have run.
                    scriptOM.RegisterClientScriptBlock(this, GetType(), ClientID + "_CreateDataObject1", "var " + clientDataObjectID + " = null;", true); 
                    scriptOM.RegisterStartupScript(this, GetType(), ClientID + "_CreateDataObject2", createDataObjectScript, true); 

                    // DevDiv 95670: Delete circular reference to prevent IE memory leaks during partial update 
                    IScriptManager scriptManager = Page.ScriptManager;
                    if ((scriptManager != null) && scriptManager.SupportsPartialRendering) {
                        scriptManager.RegisterDispose(this, ImageArrayID + ".length = 0;\r\n" + clientDataObjectID + " = null;");
                    } 
                }
            } 
        } 

        protected virtual void OnSelectedNodeChanged(EventArgs e) { 
            EventHandler handler = (EventHandler)Events[SelectedNodeChangedEvent];
            if (handler != null) {
                handler(this, e);
            } 
        }
 
        protected virtual void OnTreeNodeCollapsed(TreeNodeEventArgs e) { 
            TreeNodeEventHandler handler = (TreeNodeEventHandler)Events[TreeNodeCollapsedEvent];
            if (handler != null) { 
                handler(this, e);
            }
        }
 
        protected virtual void OnTreeNodeExpanded(TreeNodeEventArgs e) {
            TreeNodeEventHandler handler = (TreeNodeEventHandler)Events[TreeNodeExpandedEvent]; 
            if (handler != null) { 
                handler(this, e);
            } 
        }

        protected virtual void OnTreeNodeDataBound(TreeNodeEventArgs e) {
            TreeNodeEventHandler handler = (TreeNodeEventHandler)Events[TreeNodeDataBoundEvent]; 
            if (handler != null) {
                handler(this, e); 
            } 
        }
 
        protected virtual void OnTreeNodePopulate(TreeNodeEventArgs e) {
            TreeNodeEventHandler handler = (TreeNodeEventHandler)Events[TreeNodePopulateEvent];
            if (handler != null) {
                handler(this, e); 
            }
        } 
 

        ///  
        ///     Overridden to create all the tree nodes based on the datasource provided
        /// 
        protected internal override void PerformDataBinding() {
            base.PerformDataBinding(); 

            // This is to treat the case where the tree has already been bound 
            // but the data source was removed and we're rebinding (we want to get an emty tree) 
            if (!DesignMode && _dataBound &&
                String.IsNullOrEmpty(DataSourceID) && DataSource == null) { 

                Nodes.Clear();
                return;
            } 

            DataBindNode(RootNode); 
 
            if (!String.IsNullOrEmpty(DataSourceID) || DataSource != null) {
                _dataBound = true; 
            }

            // Always expand depth if data is changed
            ExpandToDepth(Nodes, ExpandDepth); 
        }
 
 
        /// 
        ///     Triggers a populate event for the specified node 
        /// 
        internal void PopulateNode(TreeNode node) {
            if (node.DataBound) {
                DataBindNode(node); 
            }
            else { 
                OnTreeNodePopulate(new TreeNodeEventArgs(node)); 
            }
            node.Populated = true; 
            node.PopulateOnDemand = false;
        }

        internal void RaiseSelectedNodeChanged() { 
            OnSelectedNodeChanged(EventArgs.Empty);
        } 
 

        internal void RaiseTreeNodeCollapsed(TreeNode node) { 
            OnTreeNodeCollapsed(new TreeNodeEventArgs(node));
        }

 
        internal void RaiseTreeNodeExpanded(TreeNode node) {
            OnTreeNodeExpanded(new TreeNodeEventArgs(node)); 
        } 

        private void RegisterStyle(Style style) { 
            if (style.IsEmpty) {
                return;
            }
 
            if (Page != null && Page.SupportsStyleSheets) {
                string name = ClientID + "_" + _cssStyleIndex++.ToString(NumberFormatInfo.InvariantInfo); 
                Page.Header.StyleSheet.CreateStyleRule(style, this, "." + name); 
                style.SetRegisteredCssClass(name);
            } 
        }

        public override void RenderBeginTag(HtmlTextWriter writer) {
            // skip link 
            if (SkipLinkText.Length != 0 && !DesignMode) {
                writer.AddAttribute(HtmlTextWriterAttribute.Href, '#' + ClientID + "_SkipLink"); 
                writer.RenderBeginTag(HtmlTextWriterTag.A); 
                writer.AddAttribute(HtmlTextWriterAttribute.Alt, SkipLinkText);
                writer.AddAttribute(HtmlTextWriterAttribute.Src, SpacerImageUrl); 
                writer.AddStyleAttribute(HtmlTextWriterStyle.BorderWidth, "0px");
                writer.AddAttribute(HtmlTextWriterAttribute.Width, "0");
                writer.AddAttribute(HtmlTextWriterAttribute.Height, "0");
                writer.RenderBeginTag(HtmlTextWriterTag.Img); 
                writer.RenderEndTag(); //Img
                writer.RenderEndTag(); //A 
            } 
            base.RenderBeginTag(writer);
            if (DesignMode) { 
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);
            }
        } 

 
        ///  
        ///     Overridden to render all the tree nodes
        ///  
        protected internal override void RenderContents(HtmlTextWriter writer) {
            base.RenderContents(writer);

            // Make sure we are in a form tag with runat=server. 
            if (Page != null) {
                Page.VerifyRenderingInServerForm(this); 
            } 

            bool enabled = IsEnabled; 

            // Render all the root nodes and have them render their children recursively
            for (int i = 0; i < Nodes.Count; i++) {
                TreeNode node = Nodes[i]; 
                bool[] isLast = new bool[10];
                isLast[0] = (i == (Nodes.Count - 1)); 
                node.Render(writer, i, isLast, enabled); 
            }
 
            // Reset all these cached values so things can pick up changes in the designer
            if (DesignMode) {
                // Reset all these cached values so things can pick up changes in the designer
                if (_nodeStyle != null) { 
                    _nodeStyle.ResetCachedStyles();
                } 
                if (_leafNodeStyle != null) { 
                    _leafNodeStyle.ResetCachedStyles();
                } 
                if (_parentNodeStyle != null) {
                    _parentNodeStyle.ResetCachedStyles();
                }
                if (_rootNodeStyle != null) { 
                    _rootNodeStyle.ResetCachedStyles();
                } 
                if (_selectedNodeStyle != null) { 
                    _selectedNodeStyle.ResetCachedStyles();
                } 
                if (_hoverNodeStyle != null) {
                    _hoverNodeHyperLinkStyle = new HyperLinkStyle(_hoverNodeStyle);
                }
 
                foreach (TreeNodeStyle style in LevelStyles) {
                    style.ResetCachedStyles(); 
                } 

                if (_imageUrls != null) { 
                    for (int i = 0; i < _imageUrls.Length; i++) {
                        _imageUrls[i] = null;
                    }
                } 

                _cachedExpandImageUrl = null; 
                _cachedCollapseImageUrl = null; 
                _cachedNoExpandImageUrl = null;
                _cachedLeafNodeClassNames = null; 
                _cachedLeafNodeHyperLinkClassNames = null;
                _cachedLeafNodeStyles = null;
                _cachedLevelsContainingCssClass = null;
                _cachedParentNodeClassNames = null; 
                _cachedParentNodeHyperLinkClassNames = null;
                _cachedParentNodeStyles = null; 
            } 
        }
 
        public override void RenderEndTag(HtmlTextWriter writer) {
            if (DesignMode) {
                writer.RenderEndTag();
                writer.RenderEndTag(); 
            }
            base.RenderEndTag(writer); 
            // skip link 
            if (SkipLinkText.Length != 0 && !DesignMode) {
                writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID + "_SkipLink"); // XHTML 1.1 needs id instead of name 
                writer.RenderBeginTag(HtmlTextWriterTag.A);
                writer.RenderEndTag();
            }
        } 

 
        ///  
        ///     Saves the expand state of nodes.  The value is placed into a hidden field on the page
        ///     which gets updated on the client as nodes are expanded and collapsed.  This also 
        ///     numbers the nodes, which provides IDs for the nodes.
        /// 
        private void SaveNodeState(TreeNode node, ref int index, StringBuilder expandState, bool rendered) {
            // Set the index for the current node 
            node.Index = index++;
 
            // If we aren't using client script, some checked nodes might not get rendered, and hence, 
            // won't postback their checked state.  We need to store some viewstate for those.
            if (node.CheckedSet) { 
                if (!Enabled || (!RenderClientScript && !rendered && node.Checked)) {
                    node.PreserveChecked = true;
                }
                else { 
                    node.PreserveChecked = false;
                } 
            } 

            if (node.PopulateOnDemand) { 
                if ((node.ChildNodes.Count == 0) || (node.Expanded != true)) {
                    // If the node is to be populated on the client and there are no children or it
                    // has children and is not expanded, it's a collpased node ('c')
                    expandState.Append('c'); 
                }
                else { 
                    // Otherwise, it's an expanded node 
                    expandState.Append('e');
                } 
            }
            else if (node.ChildNodes.Count == 0) {
                // If there aren't any child nodes, then it's a normal node
                expandState.Append('n'); 
            }
            else { 
                if (node.Expanded == null) { 
                    expandState.Append('u');
                } 
                else if (node.Expanded == true) {
                    // If it has children and it's expanded, it's expanded
                    expandState.Append('e');
                } 
                else {
                    // If it has children and it isn't expanded, it's collapsed 
                    expandState.Append('c'); 
                }
            } 

            // If there are children, save their state too
            if (node.ChildNodes.Count > 0) {
                TreeNodeCollection nodes = node.ChildNodes; 
                for (int i = 0; i < nodes.Count; i++) {
                    SaveNodeState(nodes[i], ref index, expandState, (node.Expanded == true) && rendered); 
                } 
            }
        } 


        /// 
        ///  
        ///  Saves the state of the .
        ///  
        protected override object SaveViewState() { 
            // If the tree is invisible (or one of its parents is) and we're in the GET request, we have to remember (VSWhidbey 349279)
            // 


            if (!Visible && (Page != null) && !Page.IsPostBack) {
                ViewState["NeverExpanded"] = true; 
            }
 
            object[] state = new object[9]; 

            state[0] = base.SaveViewState(); 

            bool hasViewState = (state[0] != null);

            if (_nodeStyle != null) { 
                state[1] = ((IStateManager)_nodeStyle).SaveViewState();
                hasViewState |= (state[1] != null); 
            } 

            if (_rootNodeStyle != null) { 
                state[2] = ((IStateManager)_rootNodeStyle).SaveViewState();
                hasViewState |= (state[2] != null);
            }
 
            if (_parentNodeStyle != null) {
                state[3] = ((IStateManager)_parentNodeStyle).SaveViewState(); 
                hasViewState |= (state[3] != null); 
            }
 
            if (_leafNodeStyle != null) {
                state[4] = ((IStateManager)_leafNodeStyle).SaveViewState();
                hasViewState |= (state[4] != null);
            } 

            if (_selectedNodeStyle != null) { 
                state[5] = ((IStateManager)_selectedNodeStyle).SaveViewState(); 
                hasViewState |= (state[5] != null);
            } 

            if (_hoverNodeStyle != null) {
                state[6] = ((IStateManager)_hoverNodeStyle).SaveViewState();
                hasViewState |= (state[6] != null); 
            }
 
            if (_levelStyles != null) { 
                state[7] = ((IStateManager)_levelStyles).SaveViewState();
                hasViewState |= (state[7] != null); 
            }

            state[8] = ((IStateManager)Nodes).SaveViewState();
            hasViewState |= (state[8] != null); 

            if (hasViewState) { 
                return state; 
            }
            else { 
                return null;
            }
        }
 

        ///  
        /// Allows a derived TreeView to set the DataBound proprety on a node 
        /// 
        protected void SetNodeDataBound(TreeNode node, bool dataBound) { 
            node.SetDataBound(dataBound);
        }

 
        /// 
        /// Allows a derived TreeView to set the DataItem on a node 
        ///  
        protected void SetNodeDataItem(TreeNode node, object dataItem) {
            node.SetDataItem(dataItem); 
        }


        ///  
        /// Allows a derived TreeView to set the DataPath on a node
        ///  
        protected void SetNodeDataPath(TreeNode node, string dataPath) { 
            node.SetDataPath(dataPath);
        } 

        internal void SetSelectedNode(TreeNode node) {
            Debug.Assert(node == null || node.Owner == this);
 
            if (_selectedNode != node) {
                // Unselect the previously selected node 
                if ((_selectedNode != null) && (_selectedNode.Selected)) { 
                    _selectedNode.SetSelected(false);
                } 
                _selectedNode = node;
                // Notify the new selected node that it's now selected
                if ((_selectedNode != null) && !_selectedNode.Selected) {
                    _selectedNode.SetSelected(true); 
                }
            } 
        } 

 
        /// 
        /// 
        ///    Marks the starting point to begin tracking and saving changes to the
        ///    control as part of the control viewstate. 
        /// 
        protected override void TrackViewState() { 
            base.TrackViewState(); 
            if (_nodeStyle != null) {
                ((IStateManager)_nodeStyle).TrackViewState(); 
            }

            if (_rootNodeStyle != null) {
                ((IStateManager)_rootNodeStyle).TrackViewState(); 
            }
 
            if (_parentNodeStyle != null) { 
                ((IStateManager)_parentNodeStyle).TrackViewState();
            } 

            if (_leafNodeStyle != null) {
                ((IStateManager)_leafNodeStyle).TrackViewState();
            } 

            if (_selectedNodeStyle != null) { 
                ((IStateManager)_selectedNodeStyle).TrackViewState(); 
            }
 
            if (_hoverNodeStyle != null) {
                ((IStateManager)_hoverNodeStyle).TrackViewState();
            }
 
            if (_levelStyles != null) {
                ((IStateManager)_levelStyles).TrackViewState(); 
            } 

            if (_bindings != null) { 
                ((IStateManager)_bindings).TrackViewState();
            }

            ((IStateManager)Nodes).TrackViewState(); 
        }
 
        #region IPostBackEventHandler implementation 

        ///  
        void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
            RaisePostBackEvent(eventArgument);
        }
 
        protected virtual void RaisePostBackEvent(string eventArgument) {
            ValidateEvent(UniqueID, eventArgument); 
 
            // Do not take any postback into account if the tree is disabled.
            if (!IsEnabled) return; 

            if (_adapter != null) {
                IPostBackEventHandler pbeh = _adapter as IPostBackEventHandler;
                if (pbeh != null) { 
                    pbeh.RaisePostBackEvent(eventArgument);
                } 
            } 
            else {
                if (eventArgument.Length == 0) { 
                    return;
                }

                // On postback, see what kind of event we received by checking the first character 
                char eventType = eventArgument[0];
                // Get the path of the node specified in the eventArgument 
                string nodePath = HttpUtility.HtmlDecode(eventArgument.Substring(1)); 
                // Find that node in the tree
                TreeNode node = Nodes.FindNode(nodePath.Split(InternalPathSeparator), 0); 

                if (node != null) {
                    switch (eventType) {
                        case 't': { 
                                // 't' means that we're toggling the expand state of the node
                                if (ShowExpandCollapse || 
                                    (node.SelectAction == TreeNodeSelectAction.Expand) || 
                                    (node.SelectAction == TreeNodeSelectAction.SelectExpand)) {
 
                                    node.ToggleExpandState();
                                }
                                break;
                            } 
                        case 's': {
                                // 's' means that the node has been selected 
                                if ((node.SelectAction == TreeNodeSelectAction.Expand) || (node.SelectAction == TreeNodeSelectAction.SelectExpand)) { 
                                    if (node.Expanded != true) {
                                        node.Expanded = true; 
                                    }
                                    else if (node.SelectAction == TreeNodeSelectAction.Expand) {
                                        // Expand is really just toggle expand state (while SelectExpand is just expand)
                                        node.Expanded = false; 
                                    }
                                } 
 
                                if ((node.SelectAction == TreeNodeSelectAction.Select) || (node.SelectAction == TreeNodeSelectAction.SelectExpand)) {
                                    bool selectedChanged = false; 
                                    if (!node.Selected) {
                                        selectedChanged = true;
                                    }
 
                                    node.Selected = true;
 
                                    if (selectedChanged) { 
                                        _fireSelectedNodeChanged = true;
                                    } 
                                }
                                break;
                            }
                    } 
                }
 
                if (_fireSelectedNodeChanged) { 
                    try {
                        RaiseSelectedNodeChanged(); 
                    }
                    finally {
                        _fireSelectedNodeChanged = false;
                    } 
                }
            } 
        } 
        #endregion
 
        #region ICallbackEventHandler implementation

        /// 
        void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) { 
            RaiseCallbackEvent(eventArgument);
        } 
 
        string ICallbackEventHandler.GetCallbackResult() {
            return GetCallbackResult(); 
        }

        protected virtual void RaiseCallbackEvent(string eventArgument) {
            _callbackEventArgument = eventArgument; 
        }
 
        protected virtual string GetCallbackResult() { 
            // Do not take any callback into account if the tree is disabled.
            if (!IsEnabled) return String.Empty; 

            // Split the eventArgument into pieces
            // The format is (without the spaces):
            // nodeIndex | lastIndex | databound | parentIsLast | text.length | text datapath.Length | datapath path 

            // The first piece is always the node index 
            int startIndex = 0; 
            int endIndex = _callbackEventArgument.IndexOf('|');
            string nodeIndexString = _callbackEventArgument.Substring(startIndex, endIndex); 
            int nodeIndex = Int32.Parse(nodeIndexString, CultureInfo.InvariantCulture);

            // The second piece is always the last index
            startIndex = endIndex + 1; 
            endIndex = _callbackEventArgument.IndexOf('|', startIndex);
            int lastIndex = Int32.Parse(_callbackEventArgument.Substring(startIndex, endIndex - startIndex), CultureInfo.InvariantCulture); 
 
            // The third piece is always the last databound bool followed by the checked bool
            bool dataBound = (_callbackEventArgument[endIndex + 1] == 't'); 
            bool nodeChecked = (_callbackEventArgument[endIndex + 2] == 't');

            // Fourth is the parentIsLast array
            startIndex = endIndex + 3; 
            endIndex = _callbackEventArgument.IndexOf('|', startIndex);
            string parentIsLast = _callbackEventArgument.Substring(startIndex, endIndex - startIndex); 
 
            // Fifth is the node text
            startIndex = endIndex + 1; 
            endIndex = _callbackEventArgument.IndexOf('|', startIndex);
            int nodeTextLength = Int32.Parse(_callbackEventArgument.Substring(startIndex, endIndex - startIndex), CultureInfo.InvariantCulture);
            startIndex = endIndex + 1;
            endIndex = startIndex + nodeTextLength; 
            string nodeText = _callbackEventArgument.Substring(startIndex, endIndex - startIndex);
 
            // Sixth is the data path 
            startIndex = endIndex;
            endIndex = _callbackEventArgument.IndexOf('|', startIndex); 
            int dataPathLength = Int32.Parse(_callbackEventArgument.Substring(startIndex, endIndex - startIndex), CultureInfo.InvariantCulture);
            startIndex = endIndex + 1;
            endIndex = startIndex + dataPathLength;
            string dataPath = _callbackEventArgument.Substring(startIndex, endIndex - startIndex); 

            // Last piece is the value path 
            startIndex = endIndex; 
            string valuePath = _callbackEventArgument.Substring(startIndex);
 
            // Last piece of the value path is the node value
            startIndex = valuePath.LastIndexOf(InternalPathSeparator);
            string nodeValue = TreeView.UnEscape(valuePath.Substring(startIndex + 1));
 
            // Validate that input for forged callbacks
            ValidateEvent(UniqueID, 
                String.Concat(nodeIndexString, nodeText, valuePath, dataPath)); 

            TreeNode node = CreateNode(); 
            node.PopulateOnDemand = true;
            if (nodeText != null && nodeText.Length != 0) {
                node.Text = nodeText;
            } 
            if (nodeValue != null && nodeValue.Length != 0) {
                node.Value = nodeValue; 
            } 
            node.SetDataBound(dataBound);
            node.Checked = nodeChecked; 
            node.SetPath(valuePath);
            node.SetDataPath(dataPath);
            PopulateNode(node);
 
            string result = String.Empty;
            if (node.ChildNodes.Count > 0) { 
                // Get the expand state for all the nodes (like we do in OnPreRender) 
                StringBuilder expandState = new StringBuilder();
                for (int i = 0; i < node.ChildNodes.Count; i++) { 
                    SaveNodeState(node.ChildNodes[i], ref lastIndex, expandState, true);
                }

                StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture); 
                //
                HtmlTextWriter writer = new HtmlTextWriter(stringWriter); 
 
                int depth = node.Depth;
                bool[] isLast = new bool[depth + 5]; 
                if (parentIsLast.Length > 0) {
                    // Restore the isLast bool array so we can properly draw the lines
                    for (int i = 0; i < parentIsLast.Length; i++) {
                        if (parentIsLast[i] == 't') { 
                            isLast[i] = true;
                        } 
                    } 
                }
 
                EnsureRenderSettings();

                // Render out the child nodes
                if (node.Expanded != true) { 
                    writer.AddStyleAttribute("display", "none");
                } 
                writer.AddAttribute(HtmlTextWriterAttribute.Id, CreateNodeId(nodeIndex) + "Nodes"); 
                writer.RenderBeginTag(HtmlTextWriterTag.Div);
                node.RenderChildNodes(writer, depth, isLast, true); 
                writer.RenderEndTag();
                writer.Flush();
                writer.Close();
 
                result = lastIndex.ToString(CultureInfo.InvariantCulture) + "|" + expandState.ToString() + "|" + stringWriter.ToString();
            } 
 
            _callbackEventArgument = String.Empty;
            return result; 
        }
        #endregion

        #region IPostBackDataHandler implementation 

        ///  
        bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) { 
            return LoadPostData(postDataKey, postCollection);
        } 

        protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
            // Do not take any postback into account if the tree is disabled.
            if (!IsEnabled) return false; 

            int selectedNodeIndex = -1; 
 
            string postedSelectedNodeID = postCollection[SelectedNodeFieldID];
            if (!String.IsNullOrEmpty(postedSelectedNodeID)) { 
                selectedNodeIndex = GetTrailingIndex(postedSelectedNodeID);
            }

            _loadingNodeState = true; 
            try {
                Dictionary populatedNodes = null; 
                int[] logList = null; 
                int logLength = -1;
                // If we're populating on the client, we need to repopulate the nodes that were 
                // populated on the client, so add all the node indexes that were populated on the client
                if (PopulateNodesFromClient) {
                    string log = postCollection[PopulateLogID];
                    if (log != null) { 
                        string[] logParts = log.Split(',');
                        logLength = logParts.Length; 
                        populatedNodes = new Dictionary(logLength); 
                        logList = new int[logLength];
                        for (int i = 0; i < logLength; i++) { 
                            if (logParts[i].Length > 0) {
                                int populateIndex = Int32.Parse(logParts[i], NumberStyles.Integer, CultureInfo.InvariantCulture);
                                if (!populatedNodes.ContainsKey(populateIndex)) {
                                    logList[i] = populateIndex; 
                                    // Putting null, which will be replaced during LoadNodeState
                                    populatedNodes.Add(populateIndex, null); 
                                } 
                                else {
                                    logList[i] = -1; 
                                }
                            }
                            else {
                                logList[i] = -1; 
                            }
                        } 
                    } 
                }
 
                // Make sure all the nodes that were checked on the client get checked
                // and restore the expand state of all those nodes.  Also, fill in the populatedNodes dictionary
                // with the actual TreeNode instances
                string expandState = postCollection[ExpandStateID]; 
                int index = 0;
                for (int i = 0; i < Nodes.Count; i++) { 
                    LoadNodeState(Nodes[i], ref index, expandState, populatedNodes, selectedNodeIndex); 
                }
 
                // Now that the populatedNodes dictionary is filled in with TreeNode objects, we need
                // to call populate on those nodes.
                if (PopulateNodesFromClient && (logLength > 0)) {
                    object oLastIndex = ViewState["LastIndex"]; 
                    int lastIndex = (oLastIndex != null) ? (int)oLastIndex : -1;
                    for (int i = 0; i < logLength; i++) { 
                        index = logList[i]; 
                        if ((index >= 0) && populatedNodes.ContainsKey(index)) {
                            TreeNode node = populatedNodes[index]; 
                            if (node != null) {
                                PopulateNode(node);

                                // Since the just-populated nodes could have been expanded and populated on the client as well, 
                                // we need to load the node state of those nodes (filling in the populatedNodes dictionary with
                                // those TreeNode instances 
                                if ((node.ChildNodes.Count > 0) && (lastIndex != -1)) { 
                                    TreeNodeCollection nodes = node.ChildNodes;
                                    for (int j = 0; j < nodes.Count; j++) { 
                                        LoadNodeState(nodes[j], ref lastIndex, expandState, populatedNodes, selectedNodeIndex);
                                    }
                                }
                            } 
                        }
                    } 
                } 
            }
            finally { 
                _loadingNodeState = false;
            }

            return (_checkedChangedNodes != null); 
        }
 
 
        /// 
        void IPostBackDataHandler.RaisePostDataChangedEvent() { 
            RaisePostDataChangedEvent();
        }

        protected virtual void RaisePostDataChangedEvent() { 
            // If there were nodes whose check state has changed, fire events for each one
            if (_checkedChangedNodes != null) { 
                foreach (TreeNode node in _checkedChangedNodes) { 
                    OnTreeNodeCheckChanged(new TreeNodeEventArgs(node));
                } 
            }
        }
        #endregion
 
        private class TreeViewExpandDepthConverter : Int32Converter {
            private const string fullyExpandedString = "FullyExpand"; 
            private static object[] expandDepthValues = { -1, 
                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 
                21, 22, 23, 24, 25, 26, 27, 28, 29, 30};

            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
                if (sourceType == typeof(string)) { 
                    return true;
                } 
 
                return base.CanConvertFrom(context, sourceType);
            } 

            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
                if (destinationType == typeof(int)) {
                    return true; 
                }
                else if (destinationType == typeof(string)) { 
                    return true; 
                }
 
                return base.CanConvertTo(context, destinationType);
            }

 
            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) {
                string strValue = value as string; 
                if (strValue != null) { 
                    if (String.Equals(strValue, fullyExpandedString, StringComparison.OrdinalIgnoreCase)) {
                        return -1; 
                    }
                }

                return base.ConvertFrom(context, culture, value); 
            }
 
            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { 
                if (destinationType == typeof(string)) {
                    if ((value is int) && ((int)value == -1)) { 
                        return fullyExpandedString;
                    }

                    string strValue = value as string; 
                    if (strValue != null) {
                        if (String.Equals(strValue, fullyExpandedString, StringComparison.OrdinalIgnoreCase)) { 
                            return value; 
                        }
                    } 
                }
                else if (destinationType == typeof(int)) {
                    string strValue = value as string;
                    if (strValue != null) { 
                        if (String.Equals(strValue, fullyExpandedString, StringComparison.OrdinalIgnoreCase)) {
                            return -1; 
                        } 
                    }
                } 

                return base.ConvertTo(context, culture, value, destinationType);
            }
 
            public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
                return new StandardValuesCollection(expandDepthValues); 
            } 

            public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { 
                return true;
            }
        }
    } 
}


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK