代码之家  ›  专栏  ›  技术社区  ›  Christian Hayter

动态更改SiteMapNode的标题

  •  4
  • Christian Hayter  · 技术社区  · 15 年前

    我们有一个使用BOG标准默认站点地图的网站,其安全性调整如下:

    <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" />
    

    当呈现此菜单时, 站点地图解析 事件不会为任何内容触发,因为当前节点是定义菜单的页面。因此,菜单显示物理站点地图文件中的无意义占位符标题,而不是正确的标题。

    尝试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 方法。对于我来说,递归地将所有数据拉入内存以修复一个值似乎是错误的。而且,我无法控制什么时候 建筑地图 被调用。我更喜欢类似于尝试1的方法,当首先需要节点数据时,可以按需调用。

    尝试4(新) -类似于尝试2,但凌驾于 全部的 与读取数据有关的虚拟成员( CurrentNode , FindSiteMapNode , FindSiteMapNodeFromKey , 获取子节点 , GetCurrentNodeAndHintAncestorNodes , GetCurrentNodeAndHintNeighborhoodNodes , GetParentNode , GetParentNodeRelativeToCurrentNodeAndHintDownFromParent ,请 GetParentNodeRelativeToNodeAndHintDownFromParent , HintAncestorNodes , HintNeighborhoodNodes )尝试在某个位置截取动态节点的读数。

    这不起作用。我将调试语句放在所有被重写的成员中,当数据绑定到下拉列表时,似乎根本没有调用它们。我能想到的唯一解释是,在 构建站点地图 打电话,这样 SiteMapNode 枚举子节点时未命中提供程序类。

    有人有更好的建议吗?

    4 回复  |  直到 13 年前
        1
  •  2
  •   Mark Redman    15 年前

    在自定义SiteMapProvider中,我们重写BuildSiteMap方法并手动构造SiteMapNodes。要更改和/或添加自定义属性,我们通过创建一个NameValueCollection并将其添加到SiteMapNode构造函数,将自定义属性添加到SiteMapNodes。

        2
  •  1
  •   Rex M    15 年前

    你很接近尝试2-你只需要覆盖 GetParentNode FindSiteMapNode 也。

        3
  •  1
  •   Christian Hayter    15 年前

    感谢马克和雷克斯的建议。我最终做的是让站点地图提供商独自一人,只需在母版页中修复一个节点,这样:

    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
    

    但是,我接受了马克的回答,因为如果将来需要做更广泛的修改,我会这样做的。

        4
  •  1
  •   Dan    13 年前

    感谢克里斯蒂安的研究。根据你的研究结果,我提出了以下可能对其他人有所帮助的方法:

    ' 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