SiteMapNodeのタイトルを動的に変更する
-
06-07-2019 - |
質問
次のようなセキュリティトリミングを備えた沼地標準のデフォルトサイトマップを使用するWebサイトがあります。
<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>
すべて非常にうまくいきましたが、バックエンドの基準に基づいて1つのノードの 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
これは動作しているようです。ただし、 BuildSiteMap
メソッドで GetAllNodes
を呼び出すのが心配です。 1つの値を修正するためだけに、すべてのデータをメモリに再帰的にプルすることは、私には間違っているようです。また、 BuildSiteMap
がいつ呼び出されるかを制御することもできません。ノードデータが最初に必要になったときにオンデマンドで呼び出されるAttempt 1のようなものを好むでしょう。
試行4(新規)-試行2と同様ですが、データの読み取りに関係する all 仮想メンバーをオーバーライドします( CurrentNode
、< code> FindSiteMapNode 、 FindSiteMapNodeFromKey
、 GetChildNodes
、 GetCurrentNodeAndHintAncestorNodes
、 GetCurrentNodeAndHintNeighborhoodNodes
、 GetParentNode
、 GetParentNodeRelativeToCurrentNodeAndHintDownFromParent
、 GetParentNodeRelativeToNodeAndHintDownFromParent
、 HintAncestorNodes
、 HintNeighborhoodNodes
)、動的ノードの読み取りをインターセプトするどこかに。
これは機能しませんでした。オーバーライドされたすべてのメンバーにデバッグステートメントを配置しましたが、データがドロップダウンリストにバインドされるときに、それらのいずれも呼び出されないようです。私が考えることができる唯一の説明は、 BuildSiteMap
の呼び出し中にノードがすべて一度にメモリに読み込まれるため、列挙時に SiteMapNode
がプロバイダークラスにヒットしないことです。子ノード。
より良い提案はありますか?
解決
カスタムSiteMapProviderでは、BuildSiteMapメソッドをオーバーライドし、SiteMapNodesを手動で構築します。カスタムプロパティを変更または追加するには、NameValueCollectionを作成してSiteMapNodesにカスタム属性を追加し、これをSiteMapNodeコンストラクターに渡します。
他のヒント
2番目の試みはかなり近い- GetParentNode および FindSiteMapNode も同様です。
提案をしてくれたMarkとRexに感謝します。私がやったことは、サイトマッププロバイダーをそのままにして、マスターページの1つのノードを修正するだけでした。
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
ただし、Markの答えは受け入れました。これは、将来さらに大規模な修正が必要であることが判明した場合にそれを行う方法だからです。
研究をしてくれたクリスチャンに感謝します。あなたの結果を使用して、私は他の人を助けるかもしれない以下を思いつきました:
' 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