|
//------------------------------------------------------------------------------
// <copyright file="TreeView.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.UI.WebControls {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing.Design;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.Util;
/// <devdoc>
/// Provides a tree view control
/// </devdoc>
[ControlValueProperty("SelectedValue")]
[DefaultEvent("SelectedNodeChanged")]
[Designer("System.Web.UI.Design.WebControls.TreeViewDesigner, " + AssemblyRef.SystemDesign)]
[SupportsEventValidation]
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<TreeNodeStyle> _cachedParentNodeStyles;
private List<string> _cachedParentNodeClassNames;
private List<string> _cachedParentNodeHyperLinkClassNames;
private List<TreeNodeStyle> _cachedLeafNodeStyles;
private List<string> _cachedLeafNodeClassNames;
private List<string> _cachedLeafNodeHyperLinkClassNames;
private Collection<int> _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<string> CachedLeafNodeClassNames {
get {
if (_cachedLeafNodeClassNames == null) {
_cachedLeafNodeClassNames = new List<string>();
}
return _cachedLeafNodeClassNames;
}
}
private List<TreeNodeStyle> CachedLeafNodeStyles {
get {
if (_cachedLeafNodeStyles == null) {
_cachedLeafNodeStyles = new List<TreeNodeStyle>();
}
return _cachedLeafNodeStyles;
}
}
private List<string> CachedLeafNodeHyperLinkClassNames {
get {
if (_cachedLeafNodeHyperLinkClassNames == null) {
_cachedLeafNodeHyperLinkClassNames = new List<string>();
}
return _cachedLeafNodeHyperLinkClassNames;
}
}
private Collection<int> CachedLevelsContainingCssClass {
get {
if (_cachedLevelsContainingCssClass == null) {
_cachedLevelsContainingCssClass = new Collection<int>();
}
return _cachedLevelsContainingCssClass;
}
}
private List<TreeNodeStyle> CachedParentNodeStyles {
get {
if (_cachedParentNodeStyles == null) {
_cachedParentNodeStyles = new List<TreeNodeStyle>();
}
return _cachedParentNodeStyles;
}
}
private List<string> CachedParentNodeClassNames {
get {
if (_cachedParentNodeClassNames == null) {
_cachedParentNodeClassNames = new List<string>();
}
return _cachedParentNodeClassNames;
}
}
private List<string> CachedParentNodeHyperLinkClassNames {
get {
if (_cachedParentNodeHyperLinkClassNames == null) {
_cachedParentNodeHyperLinkClassNames = new List<string>();
}
return _cachedParentNodeHyperLinkClassNames;
}
}
/// <devdoc>
/// Gets and sets whether the tree view will automatically bind to data
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets the tree level data mappings
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets the currently checked nodes in tree
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets the hidden field ID for the expand state of this TreeView
/// </devdoc>
internal string ClientDataObjectID {
get {
if (_cachedClientDataObjectID == null) {
_cachedClientDataObjectID = ClientID + "_Data";
}
return _cachedClientDataObjectID;
}
}
/// <devdoc>
/// Gets and sets the image ToolTip for the collapse node icon (minus).
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets and sets the image url for the collapse node icon (minus).
/// </devdoc>
[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));
}
}
/// <devdoc>
/// Gets and sets whether the control should try to use client script, if the browser is capable.
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets whether hover styles have been enabled (set)
/// </devdoc>
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;
}
}
/// <devdoc>
/// Gets and sets the image ToolTip for the Expand node icon (minus).
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets and sets the image url for the expand node icon (plus).
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets the hidden field ID for the expand state of this TreeView
/// </devdoc>
internal string ExpandStateID {
get {
if (_cachedExpandStateID == null) {
_cachedExpandStateID = ClientID + "_ExpandState";
}
return _cachedExpandStateID;
}
}
/// <devdoc>
/// Gets the hover style properties for nodes.
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// ID of the client-side array of images (expand, collapse, lines, etc.)
/// </devdoc>
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;
}
}
/// <devdoc>
/// An cache of urls for the line and node type images.
/// </devdoc>
private string[] ImageUrls {
get {
if (_imageUrls == null) {
_imageUrls = new string[ImageUrlsCount];
}
return _imageUrls;
}
}
/// <devdoc>
/// Gets whether the current browser is IE
/// </devdoc>
internal bool IsNotIE {
get {
return _isNotIE;
}
}
/// <devdoc>
/// Gets the style properties of leaf nodes in the tree.
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets the collection of TreeNodeStyles corresponding to the each level
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets and sets the url pointing to a folder containing TreeView line images.
/// </devdoc>
[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;
}
}
/// <devdoc>
/// 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
/// </devdoc>
internal bool LoadingNodeState {
get {
return _loadingNodeState;
}
}
/// <devdoc>
/// The maximum depth to which the TreeView will bind.
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets and sets the image url for the non-expandable node indicator icon.
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets and sets the indent width of each node
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets and sets whether the text of the nodes should be wrapped
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets the collection of top-level nodes.
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets the style properties of nodes in the tree.
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets the style properties of parent nodes in the tree.
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets and sets the character used to delimit paths.
/// </devdoc>
[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();
}
}
}
/// <devdoc>
/// Gets the hidden field ID for the expand state of this TreeView
/// </devdoc>
internal string PopulateLogID {
get {
if (_cachedPopulateLogID == null) {
_cachedPopulateLogID = ClientID + "_PopulateLog";
}
return _cachedPopulateLogID;
}
}
/// <devdoc>
/// Gets and sets whether the tree view should populate nodes from the client (if supported)
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets whether we should be rendering client script or not
/// </devdoc>
internal bool RenderClientScript {
get {
return _renderClientScript;
}
}
/// <devdoc>
/// The 'virtual' root node of the tree
/// </devdoc>
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;
}
}
/// <devdoc>
/// Gets the style properties of root nodes in the tree.
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets and sets the TreeView's selected node.
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
]
public TreeNode SelectedNode {
get {
return _selectedNode;
}
}
/// <devdoc>
/// Gets the tag ID for hidden field containing the id of the selected node of this TreeView
/// </devdoc>
internal string SelectedNodeFieldID {
get {
if (_cachedSelectedNodeFieldID == null) {
_cachedSelectedNodeFieldID = ClientID + "_SelectedNode";
}
return _cachedSelectedNodeFieldID;
}
}
/// <devdoc>
/// Gets the style properties of the selected node in the tree.
/// </devdoc>
[
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;
}
}
/// <devdoc>
/// Gets and sets whether to show check boxes next to specific types of nodes in the tree
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets and sets whether to show the expander icon next to nodes in the tree
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets and sets whether the TreeView should show lines.
/// </devdoc>
[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;
}
}
/// <devdoc>
/// Gets and sets the target window that the TreeNodes will browse to if selected
/// </devdoc>
[DefaultValue("")]
[WebSysDescription(SR.TreeNode_Target)]
public string Target {
get {
string s = (string)ViewState["Target"];
if (s == null) {
return String.Empty;
}
return s;
}
set {
ViewState["Target"] = value;
}
}
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);
}
}
/// <devdoc>
/// Triggered when the TreeView's selected node has changed.
/// </devdoc>
[WebCategory("Behavior")]
[WebSysDescription(SR.TreeView_SelectedNodeChanged)]
public event EventHandler SelectedNodeChanged {
add {
Events.AddHandler(SelectedNodeChangedEvent, value);
}
remove {
Events.RemoveHandler(SelectedNodeChangedEvent, value);
}
}
/// <devdoc>
/// Triggered when a TreeNode has collapsed its children.
/// </devdoc>
[WebCategory("Behavior")]
[WebSysDescription(SR.TreeView_TreeNodeCollapsed)]
public event TreeNodeEventHandler TreeNodeCollapsed {
add {
Events.AddHandler(TreeNodeCollapsedEvent, value);
}
remove {
Events.RemoveHandler(TreeNodeCollapsedEvent, value);
}
}
/// <devdoc>
/// Triggered when a TreeNode has been databound.
/// </devdoc>
[WebCategory("Behavior")]
[WebSysDescription(SR.TreeView_TreeNodeDataBound)]
public event TreeNodeEventHandler TreeNodeDataBound {
add {
Events.AddHandler(TreeNodeDataBoundEvent, value);
}
remove {
Events.RemoveHandler(TreeNodeDataBoundEvent, value);
}
}
/// <devdoc>
/// Triggered when a TreeNode has expanded its children.
/// </devdoc>
[WebCategory("Behavior")]
[WebSysDescription(SR.TreeView_TreeNodeExpanded)]
public event TreeNodeEventHandler TreeNodeExpanded {
add {
Events.AddHandler(TreeNodeExpandedEvent, value);
}
remove {
Events.RemoveHandler(TreeNodeExpandedEvent, value);
}
}
/// <devdoc>
/// Triggered when a TreeNode is populating its children.
/// </devdoc>
[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<T>(List<T> cacheList, int index) where T : class {
Debug.Assert(cacheList != null);
if (index < cacheList.Count) return cacheList[index];
return null;
}
private static void CacheSetItem<T>(List<T> 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);
}
}
/// <devdoc>
/// Fully collapses all nodes in the tree
/// </devdoc>
public void CollapseAll() {
foreach (TreeNode node in Nodes) {
node.CollapseAll();
}
}
/// <devdoc>
/// Overridden to disallow adding controls
/// </devdoc>
protected override ControlCollection CreateControlCollection() {
return new EmptyControlCollection(this);
}
protected virtual internal TreeNode CreateNode() {
return new TreeNode(this, false);
}
/// <devdoc>
/// Creates a tree node ID based on an index
/// </devdoc>
internal string CreateNodeId(int index) {
return ClientID + "n" + index;
}
/// <devdoc>
/// Creates a tree node text ID based on an index
/// </devdoc>
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();
}
/// <devdoc>
/// Databinds the specified node to the datasource
/// </devdoc>
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);
}
}
/// <devdoc>
/// 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
/// </devdoc>
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);
}
}
}
}
}
}
/// <devdoc>
/// Make sure we are set up to render
/// </devdoc>
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);
}
}
}
/// <devdoc>
/// Fully expands all nodes in the tree
/// </devdoc>
public void ExpandAll() {
foreach (TreeNode node in Nodes) {
node.ExpandAll();
}
}
private void ExpandToDepth(TreeNodeCollection nodes, int depth) {
// Reset the memory that the tree was never expanded (VSWhidbey 349279)
ViewState["NeverExpanded"] = null;
foreach (TreeNode node in nodes) {
if ((depth == -1) || (node.Depth < depth)) {
// Only expanding nodes that have not been set, not those that have explicit Expanded=False.
if (node.Expanded == null) {
node.Expanded = true;
// No need to populate as setting Expanded to true already does the job.
}
ExpandToDepth(node.ChildNodes, depth);
}
}
}
public TreeNode FindNode(string valuePath) {
if (valuePath == null) {
return null;
}
return Nodes.FindNode(valuePath.Split(PathSeparator), 0);
}
internal string GetCssClassName(TreeNode node, bool hyperLink) {
bool discarded;
return GetCssClassName(node, hyperLink, out discarded);
}
internal string GetCssClassName(TreeNode node, bool hyperLink, out bool containsClassName) {
if (node == null) {
throw new ArgumentNullException("node");
}
containsClassName = false;
int depth = node.Depth;
bool parent = node.ChildNodes.Count != 0 || node.PopulateOnDemand;
List<string> cache = parent ?
(hyperLink ? CachedParentNodeHyperLinkClassNames : CachedParentNodeClassNames) :
(hyperLink ? CachedLeafNodeHyperLinkClassNames : CachedLeafNodeClassNames);
string baseClassName = CacheGetItem<string>(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<string>(cache, depth, baseClassName);
if (containsClassName && !CachedLevelsContainingCssClass.Contains(depth)) {
CachedLevelsContainingCssClass.Add(depth);
}
}
if (needsSelectedStyle) {
containsClassName |= AppendCssClassName(builder, _selectedNodeStyle, hyperLink);
return builder.ToString().Trim(); ;
}
return baseClassName;
}
/// <devdoc>
/// Gets the URL for the specified image, properly pathing the image filename depending on which image it is
/// </devdoc>
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) &&
!StringUtil.EqualsIgnoreCase(caps["tagwriter"], typeof(Html32TextWriter).FullName));
}
internal TreeNodeStyle GetStyle(TreeNode node) {
if (node == null) {
throw new ArgumentNullException("node");
}
bool parent = node.ChildNodes.Count != 0 || node.PopulateOnDemand;
List<TreeNodeStyle> cache = parent ? CachedParentNodeStyles : CachedLeafNodeStyles;
bool needsSelectedStyle = node.Selected && _selectedNodeStyle != null;
int depth = node.Depth;
TreeNodeStyle typedStyle = CacheGetItem<TreeNodeStyle>(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<TreeNodeStyle>(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());
}
/// <devdoc>
/// 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).
/// </devdoc>
private void LoadNodeState(TreeNode node, ref int index, string expandState, IDictionary populatedNodes, int selectedNodeIndex) {
// Recursive method - prevent stack overflow.
RuntimeHelpers.EnsureSufficientExecutionStack();
// 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);
}
}
}
/// <internalonly/>
/// <devdoc>
/// Loads a saved state of the <see cref='System.Web.UI.WebControls.TreeView'/>.
/// </devdoc>
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);
}
}
/// <devdoc>
/// Overridden to register for postback, and if client script is enabled, renders out
/// the necessary script and hidden field to function.
/// </devdoc>
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 +
"(function() {\r\n 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})();\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);
}
}
/// <devdoc>
/// Overridden to create all the tree nodes based on the datasource provided
/// </devdoc>
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);
}
/// <devdoc>
/// Triggers a populate event for the specified node
/// </devdoc>
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) {
ControlRenderingHelper.WriteSkipLinkStart(writer, RenderingCompatibility, DesignMode, SkipLinkText, SpacerImageUrl, ClientID);
base.RenderBeginTag(writer);
if (DesignMode) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
}
/// <devdoc>
/// Overridden to render all the tree nodes
/// </devdoc>
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);
ControlRenderingHelper.WriteSkipLinkEnd(writer, DesignMode, SkipLinkText, ClientID);
}
/// <devdoc>
/// 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.
/// </devdoc>
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);
}
}
}
/// <internalonly/>
/// <devdoc>
/// Saves the state of the <see cref='System.Web.UI.WebControls.TreeView'/>.
/// </devdoc>
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;
}
}
/// <devdoc>
/// Allows a derived TreeView to set the DataBound proprety on a node
/// </devdoc>
protected void SetNodeDataBound(TreeNode node, bool dataBound) {
node.SetDataBound(dataBound);
}
/// <devdoc>
/// Allows a derived TreeView to set the DataItem on a node
/// </devdoc>
protected void SetNodeDataItem(TreeNode node, object dataItem) {
node.SetDataItem(dataItem);
}
/// <devdoc>
/// Allows a derived TreeView to set the DataPath on a node
/// </devdoc>
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);
}
}
}
/// <internalonly/>
/// <devdoc>
/// Marks the starting point to begin tracking and saving changes to the
/// control as part of the control viewstate.
/// </devdoc>
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
/// <internalonly/>
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 (AdapterInternal != null) {
IPostBackEventHandler pbeh = AdapterInternal 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
/// <internalonly/>
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
/// <internalonly/>
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<int, TreeNode> 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<int, TreeNode>(Math.Min(logLength, 16)); // don't eagerly allocate the maximum dictionary size
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 (populateIndex >= 0 && !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);
}
/// <internalonly/>
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;
}
}
}
}
|