Динамическое изменение заголовка SiteMapNode
-
06-07-2019 - |
Вопрос
У нас есть веб-сайт, который использует стандартную стандартную карту сайта с настройками безопасности следующим образом:
<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 (NEW) - аналогично Попытке 2, но переопределяет все виртуальные элементы, связанные с чтением данных ( CurrentNode
, < code> FindSiteMapNode , FindSiteMapNodeFromKey
, GetChildNodes
, GetCurrentNodeAndHintAncestorNodes
, GetCurrentNodeAndHintNeighborhoodNodes
/ code>
Get> code>,
GetParentNodeRelativeToCurrentNodeAndHintDownFromParent
, GetParentNodeRelativeToNodeAndHintDownFromParent
, HintAncestorNodes
, HintNeighborhoodNodes для чтения / передачи в узел для динамического чтения / чтения, чтобы перейти в узел для динамического чтения / чтения, чтобы перейти к чтению или переходить в динамический узел где-то.
Это не сработало. Я помещаю отладочные операторы во все переопределенные члены, и кажется, что ни один из них вообще не вызывается при привязке данных к выпадающему списку. Единственное объяснение, которое я могу придумать, состоит в том, что все узлы считываются в память за один раз во время вызова BuildSiteMap
, так что SiteMapNode
не затрагивает класс провайдера при перечислении дочерние узлы.
У кого-нибудь есть предложения получше?
Решение
В нашем пользовательском SiteMapProvider мы переопределяем метод BuildSiteMap и создаем SiteMapNodes вручную. Чтобы изменить и / или добавить настраиваемые свойства, мы добавляем настраиваемые атрибуты в SiteMapNodes, создавая коллекцию NameValueCollection и добавляем ее в конструктор SiteMapNode.
Другие советы
Вы довольно близки к попытке №2 - вам просто нужно переопределить GetParentNode и FindSiteMapNode .
Спасибо Марку и Рексу за предложения. В итоге я оставил поставщика карты сайта в покое и просто исправил один узел на главной странице, таким образом:
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
Тем не менее, я принял ответ Марка, потому что именно так я и сделаю, если выяснится, что в будущем необходимо будет сделать более обширные изменения.
Спасибо Кристиан за ваши исследования. Используя ваши результаты, я пришел к следующему, что может помочь другим:
' 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