我们的网站使用带有安全修整的沼泽标准默认站点地图,如下所示:
<siteMap defaultProvider="default" enabled="true">
<providers>
<add siteMapFile="~/Web.sitemap" securityTrimmingEnabled="true" name="default" type="System.Web.XmlSiteMapProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</siteMap>
一切都很顺利,但是根据某些后端条件,已经有人要求更改一个节点的Title
。听起来很简单,但显然不是。
尝试1 - 处理SiteMapResolve
事件。似乎 处理此事件似乎无关紧要,我已经在Global.asax
中展示了它,仅仅因为那是我尝试过它的地方之一而且它有效。
Public Class Global_asax
Inherits System.Web.HttpApplication
Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
AddHandler SiteMap.SiteMapResolve, AddressOf SiteMapResolve
End Sub
Sub Application_EndRequest(ByVal sender As Object, ByVal e As EventArgs)
RemoveHandler SiteMap.SiteMapResolve, AddressOf SiteMapResolve
End Sub
Private Shared Function SiteMapResolve(ByVal sender As Object, ByVal e As SiteMapResolveEventArgs) As SiteMapNode
Dim node As SiteMapNode = SiteMap.CurrentNode
If IsThisTheNodeToChange(node) Then
node = node.Clone()
node.Title = GetNodeTitle()
End If
Return node
End Function
End Class
当相关页面导航到时,这工作正常,但不幸的是,网站导航的一部分涉及到数据绑定到站点地图的组合框,如下所示:
<asp:SiteMapDataSource ID="siteMapDataSource" runat="Server" ShowStartingNode="false" StartFromCurrentNode="false" StartingNodeOffset="1" />
<asp:DropDownList ID="pageMenu" runat="Server" AutoPostBack="True" DataSourceID="siteMapDataSource" DataTextField="Title" DataValueField="Url" />
呈现此菜单时,SiteMapResolve
事件不会触发任何内容,因为当前节点是定义菜单的页面。因此,菜单显示物理站点地图文件中的无意义占位符标题,而不是正确的标题。
尝试2 - 编写自己的站点地图提供程序。我不想复制所有默认行为,所以我尝试从默认提供程序派生如下。
Public Class DynamicXmlSiteMapProvider
Inherits XmlSiteMapProvider
Private _dataFixedUp As Boolean = False
Public Overrides Function GetChildNodes(ByVal node As SiteMapNode) As SiteMapNodeCollection
Dim result As SiteMapNodeCollection = MyBase.GetChildNodes(node)
If Not _dataFixedUp Then
For Each childNode As SiteMapNode In result
FixUpNode(childNode)
Next
End If
Return result
End Function
Private Sub FixUpNode(ByVal node As SiteMapNode)
If IsThisTheNodeToChange(node) Then
node.ReadOnly = False
node.Title = GetNodeTitle()
node.ReadOnly = True
_dataFixedUp = True
End If
End Sub
End Class
这不起作用,因为在网站上导航时似乎不经常调用GetChildNodes
。
尝试3 - 尝试在数据加载到内存后立即修复数据,而不是在数据被访问时。
Public Class DynamicXmlSiteMapProvider
Inherits XmlSiteMapProvider
Private _dataFixInProgress As Boolean = False
Private _dataFixDone As Boolean = False
Public Overrides Function BuildSiteMap() As SiteMapNode
Dim result As SiteMapNode = MyBase.BuildSiteMap()
If Not _dataFixInProgress AndAlso Not _dataFixDone Then
_dataFixInProgress = True
For Each childNode As SiteMapNode In result.GetAllNodes()
FixUpNode(childNode)
Next
_dataFixInProgress = False
_dataFixDone = True
End If
Return result
End Function
Private Sub FixUpNode(ByVal node As SiteMapNode)
If IsThisTheNodeToChange(node) Then
node.ReadOnly = False
node.Title = GetNodeTitle()
node.ReadOnly = True
End If
End Sub
End Class
这似乎有效。但是,我担心GetAllNodes
方法中对BuildSiteMap
的调用。我只是为了修复一个值而递归地将所有数据拉入内存似乎是错误的。此外,我无法控制何时调用BuildSiteMap
。我希望更像是尝试1,在首次需要节点数据时按需调用。
尝试4(新) - 与尝试2类似,但覆盖与阅读数据有关的所有虚拟成员CurrentNode
,{{1} },FindSiteMapNode
,FindSiteMapNodeFromKey
,GetChildNodes
,GetCurrentNodeAndHintAncestorNodes
,GetCurrentNodeAndHintNeighborhoodNodes
,GetParentNode
,GetParentNodeRelativeToCurrentNodeAndHintDownFromParent
,GetParentNodeRelativeToNodeAndHintDownFromParent
, HintAncestorNodes
),试图拦截某处动态节点的读取。
这不起作用。我将调试语句放在所有被覆盖的成员中,当数据绑定到下拉列表时,似乎根本没有调用它们。我能想到的唯一解释是,在HintNeighborhoodNodes
调用期间,节点都被一次性读入内存,因此在枚举子节点时BuildSiteMap
没有访问提供程序类。
有没有人有更好的建议?
答案 0 :(得分:2)
在我们的自定义SiteMapProvider中,我们重写BuildSiteMap方法并手动构建SiteMapNodes。要更改和/或添加自定义属性,我们通过创建NameValueCollection将自定义属性添加到SiteMapNodes,并将其传递给SiteMapNode构造函数。
答案 1 :(得分:1)
您与第2次尝试非常接近 - 您还需要覆盖GetParentNode和 FindSiteMapNode 。
答案 2 :(得分:1)
感谢Mark和Rex的建议。我最终做的是单独离开站点地图提供程序,只修复母版页中的一个节点,因此:
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
' Do this as early as possible in the page lifecycle so that it happens before any
' automatic data binding or control initialisation is done.
Dim node As SiteMapNode = GetNodeToEdit()
Dim nodeReadOnly As Boolean = node.ReadOnly
node.ReadOnly = False
node.Title = GetNodeTitle()
node.ReadOnly = nodeReadOnly
End Sub
但是,我已经接受了马克的回答,因为如果事实证明将来需要进行更广泛的修改,我就会这样做。
答案 3 :(得分:1)
感谢Christian的研究。使用您的结果,我想出了可能有助于其他人的以下内容:
' Dynamically update some menu items.
'
' Since the correct Medical Center ID is not known until runtime, need to
' append "&MEDICAL_CENTER_ID=xxx" to all of the report's URLs in Web.sitemap.
Public Shared Sub UpdateMenu(ByVal MedicalCenterID As String)
Dim CurrentNodeTitle As String = ""
Dim NodeReadOnlyProperty As Boolean
' Home menu item
CurrentNodeTitle = SiteMap.CurrentNode.Title
' Determines if the current node has child nodes.
If (SiteMap.CurrentNode.HasChildNodes) Then
' Loop through top level 1 menu items (looking for Reports)
For Each ChildNodesEnumerator1 As SiteMapNode In SiteMap.CurrentNode.ChildNodes
CurrentNodeTitle = ChildNodesEnumerator1.Title
If CurrentNodeTitle = "Reports" Then
' Loop through level 2 menu items (looking for specfic reports)
For Each ChildNodesEnumerator2 As SiteMapNode In ChildNodesEnumerator1.ChildNodes
CurrentNodeTitle = ChildNodesEnumerator2.Title
If CurrentNodeTitle = "Multi-Day Vehicle Requests" Or _
CurrentNodeTitle = "XXXXXXXXXXXXXXXXX" Then
' First check if the URL has not been modified already
If InStr(ChildNodesEnumerator2.Url, "MEDICAL_CENTER_ID") = 0 Then
NodeReadOnlyProperty = ChildNodesEnumerator2.ReadOnly
ChildNodesEnumerator2.ReadOnly = False
ChildNodesEnumerator2.Url = ChildNodesEnumerator2.Url & "&MEDICAL_CENTER_ID=" & MedicalCenterID
ChildNodesEnumerator2.ReadOnly = NodeReadOnlyProperty
End If
End If
Next
End If
Next
End If
End Sub