Changer dynamiquement le titre d'un SiteMapNode
-
06-07-2019 - |
Question
Nous avons un site Web qui utilise un plan Sitemap par défaut conforme aux normes de la marque BOG avec le filtrage de sécurité, comme suit:
<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>
Très bien, mais une requête a été introduite pour modifier le Titre
d'un nœud en fonction de certains critères d'arrière-plan. Cela semble simple, mais apparemment pas.
Tentative 1 : gestion de l'événement SiteMapResolve
. Il semble que l'endroit où cet événement est géré n'a pas d'importance, je l'ai indiqué dans Global.asax
simplement parce que c'est l'un des endroits où je l'ai essayé et que cela a fonctionné.
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
Cela fonctionnait bien lorsque la page correspondante était consultée, mais malheureusement, une partie de la navigation du site implique une zone de liste déroulante liée aux données de la carte du site, comme suit:
<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" />
Lorsque ce menu est rendu, l'événement SiteMapResolve
ne se déclenche pour aucun contenu car le nœud actuel est la page sur laquelle le menu est défini. En conséquence, le menu affiche le titre d’espace réservé non-sens du fichier de sitemap physique plutôt que le titre correct.
Tentative 2 : création de mon propre fournisseur de sitemap. Je ne voulais pas dupliquer tout le comportement par défaut. J'ai donc essayé de dériver du fournisseur par défaut comme suit.
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
Cela ne fonctionne pas car GetChildNodes
ne semble pas être appelé très souvent lors de la navigation sur le site.
Tentative 3 : essayez de corriger les données immédiatement après leur chargement en mémoire, et non après leur accès.
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
Cela semble fonctionner. Cependant, l'appel à GetAllNodes
dans la méthode BuildSiteMap
m'inquiète. Il me semble juste de ne pas rentrer de manière récursive toutes les données en mémoire simplement pour corriger une valeur. De plus, je n’ai aucun contrôle sur le moment où BuildSiteMap
est appelé. Je préférerais quelque chose de plus similaire à Attempt 1, appelé à la demande lorsque les données du nœud sont nécessaires pour la première fois.
Tentative 4 (NOUVEAU) : similaire à Tentative 2, mais remplace tous les membres virtuels liés à la lecture de données ( CurrentNode
, < code> FindSiteMapNode , FindSiteMapNodeFromKey
, GetChildNodes
, GetCurrentNodeAndHintAncestorNodes
, GetCurrentNodeAndHintNearthhoodNodes> code de sécurité>,
GetParentNodeRelativeToCurrentNodeAndHintDownFromParent
, quelque part.
Cela n'a pas fonctionné. Je mets des instructions de débogage dans tous les membres remplacés, et il semble qu'aucune d'elles ne soit appelée lors de la liaison de données à la liste déroulante. La seule explication à laquelle je puisse penser est que tous les nœuds sont lus en mémoire en une seule fois lors de l'appel BuildSiteMap
, de sorte que le SiteMapNode
ne frappe pas la classe fournisseur en énumérant nœuds enfants.
Quelqu'un a-t-il de meilleures suggestions?
La solution
Dans notre site personnalisé SiteMapProvider, nous substituons la méthode BuildSiteMap et construisons le SiteMapNodes manuellement. Pour modifier et / ou ajouter des propriétés personnalisées, nous ajoutons des attributs personnalisés à SiteMapNodes en créant un NameValueCollection et en le transmettant au constructeur SiteMapNode.
Autres conseils
Vous êtes assez proche de la tentative n ° 2. Vous devez également remplacer GetParentNode et FindSiteMapNode également.
Merci à Mark et Rex pour les suggestions. Ce que j’ai fini par faire, c’est de laisser le fournisseur de sitemap seul et de corriger le nœud de la page principale, par exemple:
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
Toutefois, j’ai accepté la réponse de Mark, car c’est ce que je ferai s’il s’avère que des modifications plus importantes devront être apportées à l’avenir.
Merci Christian pour vos recherches. En utilisant vos résultats, j'ai mis au point les éléments suivants qui pourraient aider les autres:
' 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