各要素に LinkButton(デフォルトではハイパーリンク)を使ったカスタム SiteMapPath をユーザーコントロールとして作ってみました。
注意:
自力でユーザーコントロールを作らなくても、標準の SiteMapPath コントロールのテンプレートを使ってそれに LinkButton を実装すれば以下に紹介するユーザーコントロールと同等の機能が得られ、その方がはるかに簡単かつスマートでした。詳しくはこの記事の下の方の「2015/10/25 追記」を見てください。
SiteMapPath コントロールは、web.sitemap ファイルがきちんと書かれていれば(特に url)、それを Page に配置するだけで上の画像のようなパンくずリストが表示されます。
その個々の要素は <a title="Home" href="/default.aspx">Home</a> というようなハイパーリンクになっています。なので、これをクリックするとブラウザは /default.aspx を GET 要求しますので、単純に要求したページに遷移するだけです。
そうではなくて、クリックすると一旦表示しているページにポストバックして、HttpResponse.Redirect メソッドで /default.aspx にリダイレクトするというカスタム SiteMapPath をユーザーコントロールとして作ってみました。
その理由は、ポストバックした際にサーバー側で何らかの処置をしたいということです。
サイトのパス情報を取得するのは SiteMap オブジェクトを利用するのが便利です。SiteMap オブジェクトは ASP.NET によって自動的に生成されるサイトのナビゲーション構造のインメモリ表現で、単純に SiteMap として参照が取得できます。
それを使って、ユーザーコントロールとして、Panel に LinkButton を動的に追加していきカスタム SiteMapPath を作ってみました。コードは以下のようになります。要点はコメントとして記載しましたのでそれを見てください。
<%@ Control Language="C#" ClassName="SiteMapPathUserControl" %>
<script runat="server">
protected void Page_Init(object sender, EventArgs e)
{
List<SiteMapNode> nodeList = new List<SiteMapNode>();
// CurrentNode → RootNode の順で nodeList に Add
CreateNodeList(SiteMap.CurrentNode, nodeList);
// RootNode → CurrentNode の順に LinkButton を並べる
for (int i = nodeList.Count - 1; i >= 0; i--)
{
if (i == 0)
{
// CurrentNode は LinkButton でなくLiteral
Literal literal = new Literal();
literal.Text = nodeList[i].Title;
siteMapPathContainer.Controls.Add(literal);
}
else
{
// CurrentNode の親から RootNode まで LinkButton
LinkButton button = new LinkButton();
button.Text = nodeList[i].Title;
button.ToolTip = nodeList[i].Description;
button.CommandName = "navigate";
button.CommandArgument = nodeList[i].Url;
button.Click += new EventHandler(button_Click);
siteMapPathContainer.Controls.Add(button);
// 各要素間を " > " でつなぐ
Literal literal = new Literal();
literal.Text = " > ";
siteMapPathContainer.Controls.Add(literal);
}
}
}
// ルートまでたどって List<SiteMapNode> を作るヘルパ。
// web.sitemap ですべてのノードに url が設定済みの条件。
protected void CreateNodeList(
SiteMapNode node, List<SiteMapNode> nodeList)
{
nodeList.Add(node);
if (node.Url == SiteMap.RootNode.Url)
{
return;
}
else
{
CreateNodeList(node.ParentNode, nodeList);
}
}
protected void button_Click(object sender, EventArgs e)
{
if (((LinkButton)sender).CommandName == "navigate")
{
// ここでリダイレクトする前に必要な処置
Response.Redirect(((LinkButton)sender).CommandArgument);
}
}
</script>
<asp:Panel ID="siteMapPathContainer" runat="server">
</asp:Panel>
あまり需要はないかもしれませんが、せっかく作ったので忘れないように記事を書いておきました。
------ 2015/10/25 追記 ------
この記事の上の方の「注記」で書きましたように、標準 SiteMapPath コントロールのテンプレートを使ってそれに LinkButton を実装する方法が簡単でした。具体例は以下のコードの通りです。
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void LinkButton1_Click(object sender, EventArgs e)
{
if (((LinkButton)sender).CommandName == "navigate")
{
// ここでリダイレクトする前に必要な処置を行う
Response.Redirect(((LinkButton)sender).CommandArgument);
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:SiteMapPath ID="SiteMapPath2" runat="server">
<NodeTemplate>
<asp:LinkButton
ID="LinkButton1"
runat="server"
Text='<%# Eval("Title") %>'
ToolTip='<%# Eval("Description") %>'
CommandName="navigate"
CommandArgument='<%# Eval("Url") %>'
OnClick="LinkButton1_Click">
</asp:LinkButton>
</NodeTemplate>
<CurrentNodeTemplate>
<asp:Literal
ID="Literal1"
runat="server" Text='<%# Eval("Title") %>'>
</asp:Literal>
</CurrentNodeTemplate>
</asp:SiteMapPath>
</form>
</body>
</html>
Title, Url, Descriptin が正しく定義されている Web.sitemap ファイルがアプリケーションルート直下に存在することが条件ですので注意してください。