//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.UI.WebControls { using System; using System.ComponentModel; using System.Web; using System.Web.UI; using System.Web.Util; [ Designer("System.Web.UI.Design.WebControls.SiteMapPathDesigner, " + AssemblyRef.SystemDesign) ] public class SiteMapPath : CompositeControl { private const string _defaultSeparator = " > "; private static readonly object _eventItemCreated = new object(); private static readonly object _eventItemDataBound = new object(); private SiteMapProvider _provider = null; private Style _currentNodeStyle; private Style _rootNodeStyle; private Style _nodeStyle; private Style _pathSeparatorStyle; private Style _mergedCurrentNodeStyle; private Style _mergedRootNodeStyle; private ITemplate _currentNodeTemplate; private ITemplate _rootNodeTemplate; private ITemplate _nodeTemplate; private ITemplate _pathSeparatorTemplate; public SiteMapPath() { } [ DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty), WebCategory("Styles"), WebSysDescription(SR.SiteMapPath_CurrentNodeStyle) ] public Style CurrentNodeStyle { get { if (_currentNodeStyle == null) { _currentNodeStyle = new Style(); if (IsTrackingViewState) { ((IStateManager)_currentNodeStyle).TrackViewState(); } } return _currentNodeStyle; } } /// /// Gets or sets the that defines how the current node is rendered. /// [ Browsable(false), DefaultValue(null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(SiteMapNodeItem)), WebSysDescription(SR.SiteMapPath_CurrentNodeTemplate) ] public virtual ITemplate CurrentNodeTemplate { get { return _currentNodeTemplate; } set { _currentNodeTemplate = value; } } [ DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty), WebCategory("Styles"), WebSysDescription(SR.SiteMapPath_NodeStyle) ] public Style NodeStyle { get { if (_nodeStyle == null) { _nodeStyle = new Style(); if (IsTrackingViewState) { ((IStateManager)_nodeStyle).TrackViewState(); } } return _nodeStyle; } } /// /// Gets or sets the that defines how the parent node is rendered. /// [ Browsable(false), DefaultValue(null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(SiteMapNodeItem)), WebSysDescription(SR.SiteMapPath_NodeTemplate) ] public virtual ITemplate NodeTemplate { get { return _nodeTemplate; } set { _nodeTemplate = value; } } /// /// Indicates the number of parent nodes to display. /// [ DefaultValue(-1), Themeable(false), WebCategory("Behavior"), WebSysDescription(SR.SiteMapPath_ParentLevelsDisplayed) ] public virtual int ParentLevelsDisplayed { get { object o = ViewState["ParentLevelsDisplayed"]; if (o == null) { return -1; } return (int)o; } set { if (value < -1) { throw new ArgumentOutOfRangeException("value"); } ViewState["ParentLevelsDisplayed"] = value; } } [ DefaultValue(PathDirection.RootToCurrent), WebCategory("Appearance"), WebSysDescription(SR.SiteMapPath_PathDirection) ] public virtual PathDirection PathDirection { get { object o = ViewState["PathDirection"]; if (o == null) { return PathDirection.RootToCurrent; } return (PathDirection)o; } set { if ((value < PathDirection.RootToCurrent) || (value > PathDirection.CurrentToRoot)) { throw new ArgumentOutOfRangeException("value"); } ViewState["PathDirection"] = value; } } [ DefaultValue(_defaultSeparator), Localizable(true), WebCategory("Appearance"), WebSysDescription(SR.SiteMapPath_PathSeparator) ] public virtual string PathSeparator { get { string s = (string)ViewState["PathSeparator"]; if (s == null) { return _defaultSeparator; } return s; } set { ViewState["PathSeparator"] = value; } } [ DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty), WebCategory("Styles"), WebSysDescription(SR.SiteMapPath_PathSeparatorStyle) ] public Style PathSeparatorStyle { get { if (_pathSeparatorStyle == null) { _pathSeparatorStyle = new Style(); if (IsTrackingViewState) { ((IStateManager)_pathSeparatorStyle).TrackViewState(); } } return _pathSeparatorStyle; } } /// /// Gets or sets the that defines how the path Separator is rendered. /// [ Browsable(false), DefaultValue(null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(SiteMapNodeItem)), WebSysDescription(SR.SiteMapPath_PathSeparatorTemplate) ] public virtual ITemplate PathSeparatorTemplate { get { return _pathSeparatorTemplate; } set { _pathSeparatorTemplate = value; } } [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), WebSysDescription(SR.SiteMapPath_Provider) ] public SiteMapProvider Provider { get { // Designer must specify a provider, as code below access runtime config if (_provider != null || DesignMode) return _provider; // If not specified, use the default provider. if (String.IsNullOrEmpty(SiteMapProvider)) { _provider = SiteMap.Provider; if (_provider == null) { throw new HttpException(SR.GetString(SR.SiteMapDataSource_DefaultProviderNotFound)); } } else { _provider = SiteMap.Providers[SiteMapProvider]; if (_provider == null) { throw new HttpException(SR.GetString(SR.SiteMapDataSource_ProviderNotFound, SiteMapProvider)); } } return _provider; } set { _provider = value; } } [ DefaultValue(false), WebCategory("Appearance"), WebSysDescription(SR.SiteMapPath_RenderCurrentNodeAsLink) ] public virtual bool RenderCurrentNodeAsLink { get { object o = ViewState["RenderCurrentNodeAsLink"]; if (o == null) { return false; } return (bool)o; } set { ViewState["RenderCurrentNodeAsLink"] = value; } } [ DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty), WebCategory("Styles"), WebSysDescription(SR.SiteMapPath_RootNodeStyle) ] public Style RootNodeStyle { get { if (_rootNodeStyle == null) { _rootNodeStyle = new Style(); if (IsTrackingViewState) { ((IStateManager)_rootNodeStyle).TrackViewState(); } } return _rootNodeStyle; } } /// /// Gets or sets the that defines how the root node is rendered. /// [ Browsable(false), DefaultValue(null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(SiteMapNodeItem)), WebSysDescription(SR.SiteMapPath_RootNodeTemplate) ] public virtual ITemplate RootNodeTemplate { get { return _rootNodeTemplate; } set { _rootNodeTemplate = value; } } [ Localizable(true), WebCategory("Accessibility"), WebSysDefaultValue(SR.SiteMapPath_Default_SkipToContentText), WebSysDescription(SR.SiteMapPath_SkipToContentText) ] public virtual String SkipLinkText { get { string s = ViewState["SkipLinkText"] as String; return s == null ? SR.GetString(SR.SiteMapPath_Default_SkipToContentText) : s; } set { ViewState["SkipLinkText"] = value; } } [ DefaultValue(true), Themeable(false), WebCategory("Behavior"), WebSysDescription(SR.SiteMapPath_ShowToolTips) ] public virtual bool ShowToolTips { get { object o = ViewState["ShowToolTips"]; if (o == null) { return true; } return (bool)o; } set { ViewState["ShowToolTips"] = value; } } [ DefaultValue(""), Themeable(false), WebCategory("Behavior"), WebSysDescription(SR.SiteMapPath_SiteMapProvider) ] public virtual string SiteMapProvider { get { string provider = ViewState["SiteMapProvider"] as string; return (provider == null) ? String.Empty : provider; } set { ViewState["SiteMapProvider"] = value; _provider = null; } } [ WebCategory("Action"), WebSysDescription(SR.DataControls_OnItemCreated) ] public event SiteMapNodeItemEventHandler ItemCreated { add { Events.AddHandler(_eventItemCreated, value); } remove { Events.RemoveHandler(_eventItemCreated, value); } } /// /// Occurs when an item is databound within a control tree. /// [ WebCategory("Action"), WebSysDescription(SR.SiteMapPath_OnItemDataBound) ] public event SiteMapNodeItemEventHandler ItemDataBound { add { Events.AddHandler(_eventItemDataBound, value); } remove { Events.RemoveHandler(_eventItemDataBound, value); } } /// /// /// protected internal override void CreateChildControls() { Controls.Clear(); CreateControlHierarchy(); ClearChildState(); } /// /// A protected method. Creates a control hierarchy based on current sitemap OM. /// protected virtual void CreateControlHierarchy() { if (Provider == null) return; int index = 0; CreateMergedStyles(); SiteMapNode currentNode = Provider.GetCurrentNodeAndHintAncestorNodes(-1); if (currentNode != null) { SiteMapNode parentNode = currentNode.ParentNode; if (parentNode != null) { CreateControlHierarchyRecursive(ref index, parentNode, ParentLevelsDisplayed); } CreateItem(index++, SiteMapNodeItemType.Current, currentNode); } } private void CreateControlHierarchyRecursive(ref int index, SiteMapNode node, int parentLevels) { if (parentLevels == 0) return; SiteMapNode parentNode = node.ParentNode; if (parentNode != null) { CreateControlHierarchyRecursive(ref index, parentNode, parentLevels - 1); CreateItem(index++, SiteMapNodeItemType.Parent, node); } else { CreateItem(index++, SiteMapNodeItemType.Root, node); } CreateItem(index, SiteMapNodeItemType.PathSeparator, null); } private SiteMapNodeItem CreateItem(int itemIndex, SiteMapNodeItemType itemType, SiteMapNode node) { SiteMapNodeItem item = new SiteMapNodeItem(itemIndex, itemType); int index = (PathDirection == PathDirection.CurrentToRoot ? 0 : -1); SiteMapNodeItemEventArgs e = new SiteMapNodeItemEventArgs(item); //Add sitemap nodes so that they are accessible during events. item.SiteMapNode = node; InitializeItem(item); // Notify items OnItemCreated(e); // Add items based on PathDirection. Controls.AddAt(index, item); // Databind. item.DataBind(); // Notify items. OnItemDataBound(e); item.SiteMapNode = null; // SiteMapNodeItem is dynamically created each time, don't track viewstate. item.EnableViewState = false; return item; } private void CopyStyle(Style toStyle, Style fromStyle) { Debug.Assert(toStyle != null); // Review: How to change the default value of Font.Underline? if (fromStyle != null && fromStyle.IsSet(System.Web.UI.WebControls.Style.PROP_FONT_UNDERLINE)) toStyle.Font.Underline = fromStyle.Font.Underline; toStyle.CopyFrom(fromStyle); } private void CreateMergedStyles() { _mergedCurrentNodeStyle = new Style(); CopyStyle(_mergedCurrentNodeStyle, _nodeStyle); CopyStyle(_mergedCurrentNodeStyle, _currentNodeStyle); _mergedRootNodeStyle = new Style(); CopyStyle(_mergedRootNodeStyle, _nodeStyle); CopyStyle(_mergedRootNodeStyle, _rootNodeStyle); } /// /// /// Overriden to handle our own databinding. /// public override void DataBind() { // do our own databinding OnDataBinding(EventArgs.Empty); // contained items will be databound after they have been created, // so we don't want to walk the hierarchy here. } /// /// A protected method. Populates iteratively the specified with a /// sub-hierarchy of child controls. /// protected virtual void InitializeItem(SiteMapNodeItem item) { Debug.Assert(_mergedCurrentNodeStyle != null && _mergedRootNodeStyle != null); ITemplate template = null; Style style = null; SiteMapNodeItemType itemType = item.ItemType; SiteMapNode node = item.SiteMapNode; switch (itemType) { case SiteMapNodeItemType.Root: template = RootNodeTemplate != null ? RootNodeTemplate : NodeTemplate; style = _mergedRootNodeStyle; break; case SiteMapNodeItemType.Parent: template = NodeTemplate; style = _nodeStyle; break; case SiteMapNodeItemType.Current: template = CurrentNodeTemplate != null ? CurrentNodeTemplate : NodeTemplate; style = _mergedCurrentNodeStyle; break; case SiteMapNodeItemType.PathSeparator: template = PathSeparatorTemplate; style = _pathSeparatorStyle; break; } if (template == null) { if (itemType == SiteMapNodeItemType.PathSeparator) { Literal separatorLiteral = new Literal(); separatorLiteral.Mode = LiteralMode.Encode; separatorLiteral.Text = PathSeparator; item.Controls.Add(separatorLiteral); item.ApplyStyle(style); } else if (itemType == SiteMapNodeItemType.Current && !RenderCurrentNodeAsLink) { Literal currentNodeLiteral = new Literal(); currentNodeLiteral.Mode = LiteralMode.Encode; currentNodeLiteral.Text = node.Title; item.Controls.Add(currentNodeLiteral); item.ApplyStyle(style); } else { HyperLink link = new HyperLink(); if (style != null && style.IsSet(System.Web.UI.WebControls.Style.PROP_FONT_UNDERLINE)) link.Font.Underline = style.Font.Underline; link.EnableTheming = false; link.Enabled = this.Enabled; // VSWhidbey 281869 Don't modify input when url pointing to unc share if (node.Url.StartsWith("\\\\", StringComparison.Ordinal)) { link.NavigateUrl = ResolveClientUrl(HttpUtility.UrlPathEncode(node.Url)); } else { link.NavigateUrl = Context != null ? Context.Response.ApplyAppPathModifier(ResolveClientUrl(HttpUtility.UrlPathEncode(node.Url))) : node.Url; } link.Text = HttpUtility.HtmlEncode(node.Title); if (ShowToolTips) link.ToolTip = node.Description; item.Controls.Add(link); link.ApplyStyle(style); } } else { template.InstantiateIn(item); item.ApplyStyle(style); } } /// /// /// Loads a saved state of the . /// protected override void LoadViewState(object savedState) { if (savedState != null) { object[] myState = (object[])savedState; Debug.Assert(myState.Length == 5); base.LoadViewState(myState[0]); if (myState[1] != null) ((IStateManager)CurrentNodeStyle).LoadViewState(myState[1]); if (myState[2] != null) ((IStateManager)NodeStyle).LoadViewState(myState[2]); if (myState[3] != null) ((IStateManager)RootNodeStyle).LoadViewState(myState[3]); if (myState[4] != null) ((IStateManager)PathSeparatorStyle).LoadViewState(myState[4]); } else { base.LoadViewState(null); } } /// /// /// A protected method. Raises the event. /// protected override void OnDataBinding(EventArgs e) { base.OnDataBinding(e); // reset the control state Controls.Clear(); ClearChildState(); CreateControlHierarchy(); ChildControlsCreated = true; } /// /// A protected method. Raises the event. /// protected virtual void OnItemCreated(SiteMapNodeItemEventArgs e) { SiteMapNodeItemEventHandler onItemCreatedHandler = (SiteMapNodeItemEventHandler)Events[_eventItemCreated]; if (onItemCreatedHandler != null) { onItemCreatedHandler(this, e); } } /// /// A protected method. Raises the /// event. /// protected virtual void OnItemDataBound(SiteMapNodeItemEventArgs e) { SiteMapNodeItemEventHandler onItemDataBoundHandler = (SiteMapNodeItemEventHandler)Events[_eventItemDataBound]; if (onItemDataBoundHandler != null) { onItemDataBoundHandler(this, e); } } /// /// protected internal override void Render(HtmlTextWriter writer) { // Copied from CompositeControl.cs if (DesignMode) { ChildControlsCreated = false; EnsureChildControls(); } base.Render(writer); } /// /// Adds the SkipToContextText. /// protected internal override void RenderContents(HtmlTextWriter writer) { ControlRenderingHelper.WriteSkipLinkStart(writer, RenderingCompatibility, DesignMode, SkipLinkText, SpacerImageUrl, ClientID); base.RenderContents(writer); ControlRenderingHelper.WriteSkipLinkEnd(writer, DesignMode, SkipLinkText, ClientID); } /// /// /// Stores the state of the System.Web.UI.WebControls.SiteMapPath. /// protected override object SaveViewState() { object[] myState = new object[5]; myState[0] = base.SaveViewState(); myState[1] = (_currentNodeStyle != null) ? ((IStateManager)_currentNodeStyle).SaveViewState() : null; myState[2] = (_nodeStyle != null) ? ((IStateManager)_nodeStyle).SaveViewState() : null; myState[3] = (_rootNodeStyle != null) ? ((IStateManager)_rootNodeStyle).SaveViewState() : null; myState[4] = (_pathSeparatorStyle != null) ? ((IStateManager)_pathSeparatorStyle).SaveViewState() : null; for (int i = 0; i < myState.Length; i++) { if (myState[i] != null) return myState; } return null; } /// /// /// Marks the starting point to begin tracking and saving changes to the /// control as part of the control viewstate. /// protected override void TrackViewState() { base.TrackViewState(); if (_currentNodeStyle != null) ((IStateManager)_currentNodeStyle).TrackViewState(); if (_nodeStyle != null) ((IStateManager)_nodeStyle).TrackViewState(); if (_rootNodeStyle != null) ((IStateManager)_rootNodeStyle).TrackViewState(); if (_pathSeparatorStyle != null) ((IStateManager)_pathSeparatorStyle).TrackViewState(); } } }