Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / xsp / System / Web / UI / WebControls / TreeView.cs / 5 / 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; Listcache = 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; Listcache = 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; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // 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; Listcache = 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; Listcache = 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; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SiteMapSection.cs
- Select.cs
- CustomErrorsSection.cs
- SamlAuthorizationDecisionClaimResource.cs
- WebDisplayNameAttribute.cs
- ThreadSafeList.cs
- SecUtil.cs
- ParallelEnumerableWrapper.cs
- PreloadedPackages.cs
- AdvancedBindingPropertyDescriptor.cs
- OdbcErrorCollection.cs
- MenuItemBinding.cs
- DataServiceQuery.cs
- RepeatBehaviorConverter.cs
- DbDataSourceEnumerator.cs
- PropertyDescriptor.cs
- Lease.cs
- SmiTypedGetterSetter.cs
- Serializer.cs
- CroppedBitmap.cs
- Latin1Encoding.cs
- PackageRelationshipSelector.cs
- DiscoveryOperationContextExtension.cs
- SessionEndingEventArgs.cs
- SourceFilter.cs
- _HeaderInfo.cs
- ExceptionHandler.cs
- UpdateProgress.cs
- RegexNode.cs
- DataSourceSelectArguments.cs
- EnterpriseServicesHelper.cs
- DBConnection.cs
- DiscoveryDocumentSearchPattern.cs
- DbConnectionPoolIdentity.cs
- DataControlFieldCollection.cs
- Divide.cs
- GACMembershipCondition.cs
- LineServicesRun.cs
- SystemIcmpV6Statistics.cs
- UnknownBitmapEncoder.cs
- SAPICategories.cs
- PerformanceCountersElement.cs
- FixUpCollection.cs
- EndEvent.cs
- _NestedSingleAsyncResult.cs
- LicenseManager.cs
- Transform3D.cs
- ResourceWriter.cs
- NetDataContractSerializer.cs
- Section.cs
- DependencyObjectType.cs
- WebPageTraceListener.cs
- XmlSchemaAttribute.cs
- HebrewCalendar.cs
- ShimAsPublicXamlType.cs
- SerializationHelper.cs
- Separator.cs
- DirtyTextRange.cs
- storepermission.cs
- CustomSignedXml.cs
- DbParameterCollection.cs
- SqlDataReader.cs
- storepermission.cs
- CategoryState.cs
- SingleKeyFrameCollection.cs
- CqlErrorHelper.cs
- SourceSwitch.cs
- ActiveXHost.cs
- RowToParametersTransformer.cs
- SchemaElement.cs
- AutoResetEvent.cs
- ValueProviderWrapper.cs
- AnnotationResourceChangedEventArgs.cs
- CompiledIdentityConstraint.cs
- Variable.cs
- DirectoryLocalQuery.cs
- StrongNameMembershipCondition.cs
- XmlWriterTraceListener.cs
- PrivilegedConfigurationManager.cs
- JsonQueryStringConverter.cs
- MetadataPropertyCollection.cs
- DeviceFilterEditorDialog.cs
- ResourceContainer.cs
- HttpListener.cs
- PanelStyle.cs
- SmuggledIUnknown.cs
- InputScope.cs
- DataGridView.cs
- CompositeControl.cs
- SectionRecord.cs
- AutoGeneratedField.cs
- ThaiBuddhistCalendar.cs
- ProxySimple.cs
- FixedSOMLineRanges.cs
- ExpressionBuilderCollection.cs
- DesignerResources.cs
- SiteMapDataSourceView.cs
- Attributes.cs
- AttachedAnnotation.cs
- DataGridPagerStyle.cs