プログラムでマスター ページを指定する (C#)
PreInit イベント ハンドラー経由でプログラムでコンテンツ ページのマスター ページを設定する方法を見ていきます。
はじめに
「マスター ページを利用してサイト全体レイアウトを作成する」の最初の例以降、すべてのコンテンツ ページは @Page
ディレクティブの MasterPageFile
属性経由で宣言によってマスター ページを参照しています。 たとえば、次の @Page
ディレクティブは、コンテンツ ページをマスター ページ Site.master
にリンクします。
<%@ Page Language="C#" MasterPageFile="~/Site.master" ... %>
System.Web.UI
名前空間の Page
クラスには、コンテンツ ページのマスター ページへのパスを返す MasterPageFile
プロパティが含まれています。@Page
ディレクティブによって設定されるのはこのプロパティです。 このプロパティは、プログラムでコンテンツ ページのマスター ページを指定するためにも使用できます。 この方法は、ページにアクセスするユーザーなどの外部要因に基づいてマスター ページを動的に割り当てる場合に便利です。
このチュートリアルでは、Web サイトに 2 番めのマスター ページを追加し、実行時に使用するマスター ページを動的に決定します。
手順 1: ページのライフサイクルを確認する
コンテンツ ページである ASP.NET ページの要求が Web サーバーに到着するたびに、ASP.NET エンジンはページのコンテンツ コントロールをマスター ページの対応する ContentPlaceHolder コントロールに融合する必要があります。 この融合により、一般的なページ ライフサイクルを進めることができる単一のコントロール階層が作成されます。
図 1 に、この融合を示します。 図 1 の手順 1 は、最初のコンテンツとマスター ページのコントロール階層を示しています。 PreInit ステージの終わりに、ページ内のコンテンツ コントロールがマスター ページの対応する ContentPlaceHolders に追加されます (手順 2)。 この融合の後、マスター ページは融合されたコントロール階層のルートとして機能します。 この融合されたコントロール階層がページに追加されて、最終的なコントロール階層が生成されます (手順 3)。 最終結果として、ページのコントロール階層に融合されたコントロール階層が含まれます。
図 01: マスター ページとコンテンツ ページのコントロール階層が、PreInit ステージ中に融合されます (クリックするとフルサイズの画像が表示されます)
手順 2: コードから MasterPageFile
プロパティを設定する
マスター ページがこの融合に参加する部分は、Page
オブジェクトの MasterPageFile
プロパティの値によって異なります。 @Page
ディレクティブに MasterPageFile
属性を設定すると、ページのライフサイクルの最初のステージである初期化ステージ中に Page
の MasterPageFile
プロパティを割り当てるという実質的な効果があります。 別の方法として、プログラムでこのプロパティを設定できます。 ただし、図 1 の融合が行われる前に、このプロパティを設定することが不可欠です。
PreInit ステージの開始時に、Page
オブジェクトは PreInit
イベントを発生させて、その OnPreInit
メソッドを呼び出します。 次に、プログラムでマスター ページを設定するために、PreInit
イベントのイベント ハンドラーを作成するか、OnPreInit
メソッドをオーバーライドできます。 両方のアプローチを見てみましょう。
まず、サイトのホームページの分離コード クラス ファイルである Default.aspx.cs
を開きます。 次のコードを入力して、ページの PreInit
イベントのイベント ハンドラーを追加します。
protected void Page_PreInit(object sender, EventArgs e)
{
}
ここから、MasterPageFile
プロパティを設定できます。 MasterPageFile
プロパティに値 "~/Site.master" を割り当てるようにコードを更新します。
protected void Page_PreInit(object sender, EventArgs e)
{
this.MasterPageFile = "~/Site.master";
}
ブレークポイントを設定してデバッグを開始すると、Default.aspx
ページへのアクセスや、このページへのポストバックが発生するたびに、Page_PreInit
イベント ハンドラーが実行され、MasterPageFile
プロパティが "~/Site.master" に割り当てられることがわかります。
別の方法として、Page
クラスの OnPreInit
メソッドをオーバーライドし、そこで MasterPageFile
プロパティを設定できます。 この例では、特定のページではなく、BasePage
からマスター ページを設定しましょう。 「マスター ページでタイトル、メタ タグ、その他の HTML ヘッダーを指定する」チュートリアルでカスタムの基本ページ クラス (BasePage
) を作成したことを思い出してください。 現在、BasePage
が Page
クラスの OnLoadComplete
メソッドをオーバーライドし、このメソッドがサイト マップのデータに基づいてページの Title
プロパティを設定します。 OnPreInit
メソッドもオーバーライドするように BasePage
を更新して、マスター ページをプログラムで指定するようにしましょう。
protected override void OnPreInit(EventArgs e)
{
this.MasterPageFile = "~/Site.master";
base.OnPreInit(e);
}
すべてのコンテンツ ページが BasePage
から派生しているため、すべてのコンテンツ ページにそのマスター ページがプログラムで割り当てられるようになりました。 この時点で、Default.aspx.cs
の PreInit
イベント ハンドラーは余分です。自由に削除してください。
@Page
ディレクティブについて
少し混乱する可能性があるのは、コンテンツ ページの MasterPageFile
プロパティが 2 つの場所で指定されるということです。1 つはプログラムで BasePage
クラスの OnPreInit
メソッドから、もう 1 つは各コンテンツ ページの @Page
ディレクティブ内の MasterPageFile
属性経由で指定されます。
ページ ライフサイクルの最初のステージは初期化ステージです。 このステージ中に、Page
オブジェクトの MasterPageFile
プロパティに @Page
ディレクティブ内の MasterPageFile
属性の値が割り当てられます (指定されている場合)。 PreInit ステージは初期化ステージの後に続きます。プログラムで Page
オブジェクトの MasterPageFile
プロパティを設定し、それによって @Page
ディレクティブから割り当てられた値を上書きするのはここです。 Page
オブジェクトの MasterPageFile
プロパティはプログラムで設定するため、エンド ユーザーのエクスペリエンスに影響を与えずに @Page
ディレクティブから MasterPageFile
属性を削除できます。 これを確かめるために、思い切って Default.aspx
の @Page
ディレクティブから MasterPageFile
属性を削除して、ブラウザーからページにアクセスしてみましょう。 予想どおり、出力は属性が削除される前と同じです。
MasterPageFile
プロパティが @Page
ディレクティブ経由と、プログラムのどちらで設定されるかは、エンド ユーザーのエクスペリエンスに関係なく行われます。 ただし、@Page
ディレクティブの MasterPageFile
属性は、デザイン時にデザイナーで WYSIWYG ビューを生成するために Visual Studio によって使用されます。 Visual Studio で Default.aspx
に戻り、デザイナーに移動すると、「Master Page error: The page has controls that require a Master Page reference, but none is specified (マスター ページ エラー: ページにマスター ページ参照が必要なコントロールがありますが、何も指定されていません)」というメッセージが表示されます (図 2 を参照)。
つまり、Visual Studio で豊富なデザイン時エクスペリエンスを利用するには、@Page
ディレクティブに MasterPageFile
属性を残す必要があります。
デザイン ビューをレンダリングするための @Page ディレクティブの MasterPageFile 属性" />
図 02: Visual Studio は、@Page
ディレクティブの MasterPageFile
属性を使用してデザイン ビューをレンダリングします (クリックするとフルサイズの画像が表示されます)
手順 3: 代替マスター ページを作成する
コンテンツ ページのマスター ページは実行時にプログラムで設定できるため、外部条件に基づいて特定のマスター ページを動的に読み込むことができます。 この機能は、サイトのレイアウトをユーザーに基づいて変化させる必要がある状況で便利です。 たとえば、ブログ エンジン Web アプリケーションで、ユーザーが自分のブログのレイアウトを選択でき、それぞれのレイアウトは異なるマスター ページに関連付けられている場合があります。 実行時に、訪問者がユーザーのブログを表示したとき、Web アプリケーションはブログのレイアウトを決定し、対応するマスター ページをコンテンツ ページに動的に関連付ける必要があります。
いくつかの外部条件に基づいて、実行時にマスター ページを動的に読み込む方法を見てみましょう。 現在、この Web サイトには 1 つのマスター ページ (Site.master
) だけが含まれています。 実行時にマスター ページを選択する方法を示すために、別のマスター ページが必要です。 この手順では、新しいマスター ページの作成と構成に焦点をあてます。 実行時に使用するマスター ページの決定方法は、手順 4 で示します。
Alternate.master
という名前のルート フォルダーに新しいマスター ページを作成します。 Web サイトに AlternateStyles.css
という名前の新しいスタイル シートも追加します。
図 03: 別のマスター ページと CSS ファイルを Web サイトに追加します (クリックするとフルサイズの画像が表示されます)
タイトルをページの上部に、中央揃えで、濃紺の背景に表示するように Alternate.master
マスター ページをデザインしました。 左側の列を調べ、その内容を MainContent
の ContentPlaceHolder コントロールの下に移動しました。このコントロールの対象が、ページ幅全体になりました。 さらに、順序付けられていないレッスン リストをやめて、MainContent
の上の横方向のリストに置き換えました。 マスター ページ (および、その延長線上で、そのコンテンツ ページ) で使用されるフォントと色も更新しました。 Alternate.master
マスター ページを使用したときの Default.aspx
を図 4 に示します。
Note
ASP.NET には、"テーマ" を定義する機能が含まれています。 テーマとは、実行時にページに適用できるイメージ、CSS ファイル、およびスタイル関連の Web コントロール プロパティ設定のコレクションです。 テーマは、表示される画像と CSS ルールのみに、サイトのレイアウトの違いがある場合に使用する方法です。 異なる Web コントロールを使用するか、根本的に異なるレイアウトがあるなど、レイアウトが本質的に異なる場合は、別個のマスター ページを使用する必要があります。 テーマの詳細については、このチュートリアルの最後にある「関連項目」セクションを参照してください。
図 04: コンテンツ ページで新しい外観を使用できるようになりました (クリックするとフルサイズの画像が表示されます)
マスター ページとコンテンツ ページのマークアップが融合されるときに、MasterPage
クラスは、コンテンツ ページ内のすべてのコンテンツ コントロールがマスター ページの ContentPlaceHolder を参照していることを確認します。 存在しない ContentPlaceHolder を参照するコンテンツ コントロールが見つかった場合、例外がスローされます。 つまり、コンテンツ ページに割り当てられるマスター ページは、コンテンツ ページ内の各コンテンツ コントロールに対する ContentPlaceHolder を持っていることが必要です。
Site.master
マスター ページには、次の 4 つの ContentPlaceHolder コントロールが含まれています。
head
MainContent
QuickLoginUI
LeftColumnContent
この Web サイトのコンテンツ ページには、1 つか 2 つのみのコンテンツコントロールが含まれているものと、使用可能な ContentPlaceHolder それぞれに対するコンテンツ コントロールが含まれているものがあります。 新しいマスター ページ (Alternate.master
) が、Site.master
のすべての ContentPlaceHolders に対するコンテンツ コントロールを持つコンテンツ ページに割り当てられる可能性がある場合は、Alternate.master
にも Site.master
と同じ ContentPlaceHolder コントロールを含める必要があります。
Alternate.master
マスター ページを自分のマスター ページ (図 4 を参照) と似た外観にするには、まず AlternateStyles.css
スタイル シートにマスター ページのスタイルを定義します。 AlternateStyles.css
に次のルールを追加します。
body
{
font-family: Comic Sans MS, Arial;
font-size: medium;
margin: 0px;
}
#topContent
{
text-align: center;
background-color: Navy;
color: White;
font-size: x-large;
text-decoration: none;
font-weight: bold;
padding: 10px;
height: 50px;
}
#topContent a
{
text-decoration: none;
color: White;
}
#navContent
{
font-size: small;
text-align: center;
}
#footerContent
{
padding: 10px;
font-size: 90%;
text-align: center;
border-top: solid 1px black;
}
#mainContent
{
text-align: left;
padding: 10px;
}
次に、Alternate.master
に次の宣言型マークアップを追加します。 ご覧のように、Alternate.master
には、Site.master
のContentPlaceHolder コントロールと同じ ID
値を持つ 4 つの ContentPlaceHolder コントロールが含まれます。 さらに、ASP.NET AJAX フレームワークを使用する Web サイトのページに必要な ScriptManager コントロールも含まれます。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
<asp:ContentPlaceHolder id="head" runat="server">
</asp:ContentPlaceHolder>
<link href="AlternateStyles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="MyManager" runat="server">
</asp:ScriptManager>
<div id="topContent">
<asp:HyperLink ID="lnkHome" runat="server" NavigateUrl="~/Default.aspx"
Text="Master Pages Tutorials" />
</div>
<div id="navContent">
<asp:ListView ID="LessonsList" runat="server"
DataSourceID="LessonsDataSource">
<LayoutTemplate>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</LayoutTemplate>
<ItemTemplate>
<asp:HyperLink runat="server" ID="lnkLesson"
NavigateUrl='<%# Eval("Url") %>'
Text='<%# Eval("Title") %>' />
</ItemTemplate>
<ItemSeparatorTemplate> | </ItemSeparatorTemplate>
</asp:ListView>
<asp:SiteMapDataSource ID="LessonsDataSource" runat="server"
ShowStartingNode="false" />
</div>
<div id="mainContent">
<asp:ContentPlaceHolder id="MainContent" runat="server">
</asp:ContentPlaceHolder>
</div>
<div id="footerContent">
<p>
<asp:Label ID="DateDisplay" runat="server"></asp:Label>
</p>
<asp:ContentPlaceHolder ID="QuickLoginUI" runat="server">
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder ID="LeftColumnContent" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
新しいマスター ページをテストする
この新しいマスター ページをテストするには、MasterPageFile
プロパティに値 "~/Alternate.master" が割り当てられるように BasePage
クラスの OnPreInit
メソッドを更新してから、Web サイトにアクセスします。 ~/Admin/AddProduct.aspx
と ~/Admin/Products.aspx
の 2 つを除くすべてのページが、エラーなしで機能するはずです。 ~/Admin/AddProduct.aspx
で DetailsView に製品を追加すると、マスター ページの GridMessageText
プロパティを設定しようとするコード行で NullReferenceException
が発生します。 ~/Admin/Products.aspx
にアクセスすると、ページの読み込み時に InvalidCastException
がスローされ、「'ASP.alternate_master' 型のオブジェクトを 'ASP.site_master' 型にキャストすることはできません」というメッセージが表示されます。
これらのエラーは、Alternate.master
に定義されていないパブリック イベント、プロパティ、およびメソッドが Site.master
分離コード クラスに含まれているために発生します。 これら 2 つのページのマークアップ部分には、Site.master
マスター ページを参照する @MasterType
ディレクティブがあります。
<%@ MasterType VirtualPath="~/Site.master" %>
また、~/Admin/AddProduct.aspx
の DetailsView の ItemInserted
イベント ハンドラーには、緩やかに型指定された Page.Master
プロパティを型 Site
のオブジェクトにキャストするコードが含まれています。 @MasterType
ディレクティブ (このように使用) と ItemInserted
イベント ハンドラーのキャストは、Site.master
マスター ページに ~/Admin/AddProduct.aspx
ページと ~/Admin/Products.aspx
ページを密に結合します。
この密結合を解除するために、Site.master
と Alternate.master
を、パブリック メンバーの定義を含む共通の基底クラスから派生させることができます。 その後、この共通の基本データ型を参照するように @MasterType
ディレクティブを更新できます。
カスタムの基本マスター ページ クラスを作成する
BaseMasterPage.cs
という名前の新しいクラス ファイルを App_Code
フォルダーに追加し、System.Web.UI.MasterPage
から派生させます。 BaseMasterPage
に RefreshRecentProductsGrid
メソッドと GridMessageText
プロパティを定義する必要がありますが、これらを Site.master
から単純に移動することはできません。これらのメンバーは Site.master
マスター ページに固有の Web コントロール (RecentProducts
GridView と GridMessage
Label) で動作するためです。
必要なことは、これらのメンバーの定義は BaseMasterPage
で行われるが、実際の実装はその派生クラス (Site.master
および Alternate.master
) によって行われるように BaseMasterPage
を構成することです。 この種類の継承は、クラスとそのメンバーに abstract
のマークを付けることで可能になります。 つまり、これら 2 つのメンバーに abstract
キーワードを追加すると、BaseMasterPage
は RefreshRecentProductsGrid
と GridMessageText
を実装していないが、その派生クラスが実装することが告知されます。
BaseMasterPage
に PricesDoubled
イベントを定義し、派生クラスによってイベントを発生させる手段を提供する必要もあります。 この動作を支援するために .NET Framework で使用されるパターンは、基底クラスにパブリック イベントを作成し、OnEventName
という名前の保護された virtual
メソッドを追加することです。 これで、派生クラスはこのメソッドを呼び出してイベントを発生させたり、イベントが発生する直前または発生後にコードを実行するようにメソッドをオーバーライドしたりできます。
次のコードが含まれるように BaseMasterPage
クラスを更新します。
using System; public abstract class BaseMasterPage : System.Web.UI.MasterPage
{
public event EventHandler PricesDoubled;
protected virtual void OnPricesDoubled(EventArgs e)
{
if (PricesDoubled != null)
PricesDoubled(this, e);
}
public abstract void RefreshRecentProductsGrid();
public abstract string GridMessageText
{
get;
set;
}
}
次に、Site.master
分離コード クラスに移動し、クラスが BaseMasterPage
から派生するようにします。 BaseMasterPage
は abstract
であるため、この Site.master
でこれらの abstract
メンバーをオーバーライドする必要があります。 メソッドとプロパティ定義に override
キーワードを追加します。 さらに、DoublePrice
ボタンの Click
イベント ハンドラーで PricesDoubled
イベントを発生させるコードを、基底クラスの OnPricesDoubled
メソッドの呼び出しで更新します。
これらの変更を行うと、Site.master
分離コード クラスには次のコードが含まれているはずです。
public partial class Site : BaseMasterPage {
protected void Page_Load(object sender, EventArgs e)
{
DateDisplay.Text = DateTime.Now.ToString("dddd, MMMM dd");
}
public override void RefreshRecentProductsGrid()
{
RecentProducts.DataBind();
}
public override string GridMessageText
{
get
{
return GridMessage.Text;
}
set
{
GridMessage.Text = value;
}
}
protected void DoublePrice_Click(object sender, EventArgs e)
{
// Double the prices
DoublePricesDataSource.Update();
// Refresh RecentProducts
RecentProducts.DataBind();
// Raise the PricesDoubled event
base.OnPricesDoubled(EventArgs.Empty);
}
}
Alternate.master
の分離コード クラスも、BaseMasterPage
から派生し、2 つの abstract
メンバーをオーバーライドするように更新する必要があります。 ただし、Alternate.master
には最新の製品を一覧表示する GridView も、新しい製品がデータベースに追加された後にメッセージを表示する Label も含まれていないため、これらのメソッドは何もする必要はありません。
public partial class Alternate : BaseMasterPage
{
public override void RefreshRecentProductsGrid()
{
// Do nothing
}
public override string GridMessageText
{
get
{
return string.Empty;
}
set
{
// Do nothing
}
}
}
基本マスター ページ クラスを参照する
BaseMasterPage
クラスを完成させ、2 つのマスター ページでそれを拡張したので、最後の手順では、この共通の型を参照するように ~/Admin/AddProduct.aspx
ページと ~/Admin/Products.aspx
ページを更新します。 まず、両方のページの @MasterType
ディレクティブを変更します。変更前:
<%@ MasterType VirtualPath="~/Site.master" %>
移動先:
<%@ MasterType TypeName="BaseMasterPage" %>
@MasterType
プロパティは、ファイル パスを参照するのではなく、基本型 (BaseMasterPage
) を参照するようになりました。 その結果、両方のページの分離コード クラスで使用される厳密に型指定された Master
プロパティの型が (型 Site
ではなく) BaseMasterPage
になりました。 このように変更したら、~/Admin/Products.aspx
に再度アクセスします。 前は、ページが Alternate.master
マスター ページを使用するように構成されているのに、@MasterType
ディレクティブが Site.master
ファイルを参照したため、キャスト エラーが発生しました。 しかし今度は、ページはエラーなしでレンダリングされます。 これは、Alternate.master
マスター ページを型 BaseMasterPage
のオブジェクトにキャストできるためです (拡張されるため)。
~/Admin/AddProduct.aspx
で行う必要がある小さな変更が 1 つあります。 DetailsView コントロールの ItemInserted
イベント ハンドラーは、厳密に型指定された Master
プロパティと緩やかに型指定された Page.Master
プロパティの両方を使用します。 @MasterType
ディレクティブを更新したときに厳密に型指定された参照を修正しましたが、緩やかに型指定された参照を更新する必要がまだあります。 次のコード行を置き換えます。
Site myMasterPage = Page.Master as Site;
置き換える内容は次のとおりです。これは Page.Master
を基本型にキャストします。
BaseMasterPage myMasterPage = Page.Master as BaseMasterPage;
手順 4: コンテンツ ページにバインドするマスター ページを決定する
BasePage
クラスは現在、すべてのコンテンツ ページの MasterPageFile
プロパティを、ページ ライフサイクルの PreInit ステージでハードコーディングされた値に設定しています。 このコードを更新して、何らかの外部要因をマスター ページの基礎として使用できます。 読み込むマスター ページは、現在ログオンしているユーザーの設定によって変わると考えられます。 その場合、現在アクセスしているユーザーのマスター ページの基本設定を調べるコードを BasePage
の OnPreInit
メソッドに記述する必要があります。
使用するマスター ページ (Site.master
または Alternate.master
) をユーザーが選択できる Web ページを作成し、その選択肢をセッション変数に保存しましょう。 まず、ChooseMasterPage.aspx
という名前のルート ディレクトリに新しい Web ページを作成します。 このページ (またはそれ以降の任意のコンテンツ ページ) を作成するとき、それをマスター ページにバインドする必要はありません。マスター ページはプログラムで BasePage
に設定されるためです。 ただし、新しいページをマスター ページにバインドしない場合、新しいページの既定の宣言型マークアップには、Web フォームと、マスター ページによって提供されるその他のコンテンツが含まれます。 このマークアップを適切なコンテンツ コントロールに手動で置き換える必要があります。 そのため、新しい ASP.NET ページをマスター ページにバインドする方が簡単であると思われます。
Note
Site.master
と Alternate.master
の ContentPlaceHolder コントロールのセットは同じであるため、新しいコンテンツ ページを作成するときにどちらのマスター ページを選択しても問題ありません。 一貫性のために、Site.master
の使用をお勧めします。
図 05: Web サイトに新しいコンテンツ ページを追加します (クリックするとフルサイズの画像が表示されます)
このレッスンのエントリを含むように Web.sitemap
ファイルを更新します。 マスター ページと ASP.NET AJAX レッスンの <siteMapNode>
の下に次のマークアップを追加します。
<siteMapNode url="~/ChooseMasterPage.aspx" title="Choose a Master Page" />
ChooseMasterPage.aspx
ページにコンテンツを追加する前に、ページの分離コード クラスを更新して (System.Web.UI.Page
ではなく) BasePage
から派生するようにします。 次に、ページに DropDownList コントロールを追加し、その ID
プロパティを MasterPageChoice
に設定し、Text
値が "~/Site.master" と "~/Alternate.master" である 2 つの ListItem を追加します。
ボタン Web コントロールをページに追加し、その ID
プロパティと Text
プロパティをそれぞれ SaveLayout
と [Save Layout Choice] (レイアウトの選択を保存) に設定します。 この時点で、ページの宣言型マークアップは次のようになります。
<p>
Your layout choice:
<asp:DropDownList ID="MasterPageChoice" runat="server">
<asp:ListItem>~/Site.master</asp:ListItem>
<asp:ListItem>~/Alternate.master</asp:ListItem>
</asp:DropDownList>
</p>
<p>
<asp:Button ID="SaveLayout" runat="server" Text="Save Layout Choice" />
</p>
ページが最初に表示されるときに、ユーザーが現在選択しているマスター ページの選択肢を表示する必要があります。 Page_Load
イベント ハンドラーを作成し、次のコードを追加します。
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
if (Session["MyMasterPage"] != null)
{
ListItem li = MasterPageChoice.Items.FindByText(Session["MyMasterPage"].ToString());
if (li != null)
li.Selected = true;
}
}
}
上記のコードは、初回のページ アクセスでのみ実行されます (後続のポストバックでは実行されません)。 まずセッション変数 MyMasterPage
が存在するかどうかを確認します。 存在する場合は、MasterPageChoice
DropDownList に一致する ListItem を見つけようとします。 一致する ListItem が見つかった場合、その Selected
プロパティは true
に設定されます。
ユーザーの選択肢を MyMasterPage
セッション変数に保存するコードも必要です。 SaveLayout
ボタンの Click
イベントのイベント ハンドラーを作成し、次のコードを追加します。
protected void SaveLayout_Click(object sender, EventArgs e)
{
Session["MyMasterPage"] = MasterPageChoice.SelectedValue;
Response.Redirect("ChooseMasterPage.aspx");
}
Note
Click
イベント ハンドラーがポストバックで実行される時点までに、マスター ページは既に選択されています。 そのため、ユーザーのドロップダウン リストの選択肢は、次回のページ アクセスまで反映されません。 Response.Redirect
は、ChooseMasterPage.aspx
を再度要求することをブラウザーに強制します。
ChooseMasterPage.aspx
ページが完成したら、最後のタスクは、BasePage
が MyMasterPage
セッション変数の値に基づいて MasterPageFile
プロパティを割り当てるようにすることです。 セッション変数が設定されていない場合は、BasePage
の既定値を Site.master
にします。
protected override void OnPreInit(EventArgs e)
{
SetMasterPageFile();
base.OnPreInit(e);
}
protected virtual void SetMasterPageFile()
{
this.MasterPageFile = GetMasterPageFileFromSession();
}
protected string GetMasterPageFileFromSession()
{
if (Session["MyMasterPage"] == null)
return "~/Site.master";
else
return Session["MyMasterPage"].ToString();
}
Note
Page
オブジェクトの MasterPageFile
プロパティを割り当てるコードを OnPreInit
イベント ハンドラーから 2 つの別個のメソッドに移動しました。 この最初のメソッド SetMasterPageFile
は、MasterPageFile
プロパティを、2 番めのメソッド GetMasterPageFileFromSession
によって返される値に割り当てます。 BasePage
を拡張する将来のクラスが必要に応じてそれをオーバーライドしてカスタム ロジックを実装できるように、SetMasterPageFile
メソッドを virtual
にしました。 BasePage
の SetMasterPageFile
プロパティをオーバーライドする例は、次のチュートリアルで示します。
このコードが配置された状態で、ChooseMasterPage.aspx
ページにアクセスします。 最初は Site.master
マスター ページが選択されていますが (図 6 を参照)、ユーザーはドロップダウン リストから別のマスター ページを選択できます。
図 06: Site.master
マスター ページを使用してコンテンツ ページが表示されています (クリックするとフルサイズの画像が表示されます)
図 07: 今度は Alternate.master
マスター ページを使用してコンテンツ ページが表示されています (クリックするとフルサイズの画像が表示されます)
まとめ
コンテンツ ページにアクセスすると、そのコンテンツ コントロールはそのマスター ページの ContentPlaceHolder コントロールと融合されます。 コンテンツ ページのマスター ページは、初期化ステージ中に @Page
ディレクティブの MasterPageFile
属性に割り当てられる Page
クラスの MasterPageFile
プロパティによって示されます。 このチュートリアルで示したように、PreInit ステージが終了する前に行う限り、MasterPageFile
プロパティに値を割り当てることができます。 プログラムでマスター ページを指定できることで、外部要因に基づいてコンテンツ ページをマスター ページに動的にバインドするなどの、より高度なシナリオの扉が開きます。
プログラミングに満足!
もっと読む
この記事で説明したトピックの詳細については、次のリソースを参照してください。
- ASP.NET ページのライフサイクル図
- ASP.NET ページのライフサイクルの概要
- ASP.NET のテーマとスキンの概要
- マスター ページ: ヒント、テクニック、トラップ
- ASP.NET のテーマ
作成者について
複数の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell 氏は、1998 年から Microsoft Web のテクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の著書は、「Sams Teach Yourself ASP.NET 3.5 in 24 Hours」です。 Mitchell 氏には、mitchell@4GuysFromRolla.com で、または彼のブログ (http://ScottOnWriting.NET) 経由で連絡できます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は Suchi Banerjee でした。 今後の MSDN の記事を確認することに関心がありますか? ご希望の場合は、mitchell@4GuysFromRolla.com までお知らせください