Динамическое изменение заголовка SiteMapNode

У нас есть веб-сайт, который использует стандартную стандартную карту сайта с настройками безопасности следующим образом:

<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, FindSiteMapNode, FindSiteMapNodeFromKey, GetChildNodes, GetCurrentNodeAndHintAncestorNodes, GetCurrentNodeAndHintNeighborhoodNodes, GetParentNode , GetParentNodeRelativeToCurrentNodeAndHintDownFromParent, GetParentNodeRelativeToNodeAndHintDownFromParent, HintAncestorNodes, HintNeighborhoodNodes), чтобы попытаться перехватить чтение динамического узла где-то.

Это не сработало. Я помещаю отладочные операторы во все переопределенные члены, и кажется, что ни один из них вообще не вызывается при привязке данных к выпадающему списку. Единственное объяснение, которое я могу придумать, заключается в том, что все узлы считываются в память за один раз во время вызова BuildSiteMap, так что SiteMapNode не затрагивает класс провайдера при перечислении дочерних узлов.

У кого-нибудь есть предложения получше?

4 голоса | спросил Christian Hayter 26 +03002009-10-26T15:02:20+03:00312009bEurope/MoscowMon, 26 Oct 2009 15:02:20 +0300 2009, 15:02:20

4 ответа


0

В нашем пользовательском SiteMapProvider мы переопределяем метод BuildSiteMap и создаем SiteMapNodes вручную. Чтобы изменить и /или добавить пользовательские свойства, мы добавляем пользовательские атрибуты в SiteMapNodes, создавая коллекцию NameValueCollection и добавляя ее в конструктор SiteMapNode.

ответил Mark Redman 26 +03002009-10-26T15:51:45+03:00312009bEurope/MoscowMon, 26 Oct 2009 15:51:45 +0300 2009, 15:51:45
0

Вы довольно близки к попытке №2 - вам просто нужно переопределить GetParentNode и FindSiteMapNode .

ответил Rex M 26 +03002009-10-26T15:39:46+03:00312009bEurope/MoscowMon, 26 Oct 2009 15:39:46 +0300 2009, 15:39:46
0

Спасибо Марку и Рексу за предложения. В итоге я оставил поставщика карты сайта в покое и просто исправил один узел на главной странице, таким образом:

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

Тем не менее, я принял ответ Марка, потому что именно так я и сделаю, если выяснится, что в будущем необходимо будет сделать более масштабные изменения.

ответил Christian Hayter 9 12009vEurope/Moscow11bEurope/MoscowMon, 09 Nov 2009 11:56:05 +0300 2009, 11:56:05
0

Спасибо Кристиан за ваши исследования. Используя ваши результаты, я нашел следующее, которое может помочь другим:

' 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
ответил Dan 16 FriEurope/Moscow2011-12-16T22:21:54+04:00Europe/Moscow12bEurope/MoscowFri, 16 Dec 2011 22:21:54 +0400 2011, 22:21:54

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132