ポータル アプリケーションは今日非常にポピュラーなものとなっています。よいポータルには、それぞれ共通の特徴があります。利便性の高いポータルは、モジュラー化されていて一貫性があり、ページ遷移が容易なユーザー インターフェイスにより豊富なコンテンツ部分を提供します。より洗練されたポータルとなると、さらに進んでサイト メンバーがコンテンツを提供したり、ドキュメントをアップロードしたり、ポータル ページをパーソナライズしたりすることが可能となります。
マイクロソフトは、Windows SharePoint Services のリリースにより Windows Server 2003 プラットフォームにスケーラブルなポータル フレームワークを追加しています。これにより、サイト メンバーシップのサポート、コンテンツおよびドキュメントの管理、データのモジュラー化表示といった、ポータル フレームワークに必要な基本要素が提供されます。
Web パーツは、カスタマイゼーションおよびパーソナライゼーションの基盤を提供します。サイトの設定にもよりますが、ユーザーは Web パーツの追加、再設定、削除といった作業だけで、Windows SharePoint Services のページを簡単にパーソナライズまたはカスタマイズすることができます。カスタム Web パーツを開発することにより、Windows SharePoint Services を使用したサイトを拡張できる簡単でしかも強力な手段を手に入れることができます。カスタマイゼーションおよびパーソナライゼーションをサポートするカスタム Web パーツを作成するのに必要な作業は、Web パーツ クラスにいくつかプロパティを追加して特殊な属性を 2 つ、3 つ適用するだけです。サイト カスタマイゼーションやメンバー パーソナライゼーションに関連するデータのシリアライズ、格納、取得といった、力作業はすべて Windows SharePoint Services の Web パーツ インフラストラクチャが行ってくれます。
ASP.NET 2.0 は、Windows SharePoint Services が提供する Web パーツによく似た Web パーツ コントロール セットを提供しています。共通している点は、ASP.NET 2.0 の Web パーツ コントロール セットも、カスタマイゼーションおよびパーソナライゼーション データのシリアライズ、格納、取得といった処理を自動的に行ってくれるよう設計されている点です。異なる点は、SQL Server や Active Directory とは密結合ではないという点です。この点で、Windows SharePoint Services の Web パーツに比べてよりフレキシブルになっています。このことは、フォームベース認証を使ったポータル アプリケーションの構築を考えている企業や特定のデータベース ソリューションに捉われたくないと考える企業にとっては朗報でしょう。

Figure 1 モジュラー型 Web パーツ デザインのサンプル ポータル
本資料では、ASP.NET 2.0 の Web パーツを使用して書かれたサンプル ポータル アプリケーションを用いながら、ASP.NET 2.0 の Web パーツを紹介していきますが、主としてポータル アプリケーション用の Web パーツを開発する際に直面するデザイン上の重要な問題を認識してもらうことを目的とします。まずは、ASP.NET 2.0 の新しい Web パーツ コントロール セットを使用する際に知っておくべき基本コンセプトとコントロール タイプの説明から始めたいと思います。例として 図 1 を参照してください。
Web パーツ入門

図 2 標準的な Web パーツ ページのレイアウト
Web パーツをホストするよう設計されたページを Web パーツ ページといいます。Web パーツ ページは、図 2 で示すように、WebPartManager コントロールの 1 つのインスタンスと 1 つ以上の WebPartZone コントロールを必須とします。Web パーツ ページでは、EditorZone コントロールまたは CatalogZone コントロールを任意で使用することができます。ただし .aspx ファイルでは、WebPartManager コントロールのタグは、同じ Web パーツ インフラストラクチャに関連付けされている別のコントロールのタグよりも前に記述しなければいけません。Web パーツ ページのレイアウトや外観をより効率よく制御したければ、HTML テーブルを使い .aspx ファイル内に複数のゾーンを配置してもかまいません。
まずは、WebPartManager コントロールと WebPartZone コントロールを持つ、簡単な Web パーツ ページの一例を見てみることにしましょう。:
<asp:WebPartManager ID=" WebPartManager1" runat="server" />
<asp:WebPartZone ID="WebPartZone1" runat="server" HeaderText="Zone 1">
<ZoneTemplate>
<!-- ここで Web パーツを追加 -->
</ZoneTemplate>
</asp:WebPartZone>
WebPartZone が生成されると、Web Part 定義を使用して Web パーツ インスタンスを生成することができるようになります。Web パーツ定義の作成方法としては、2 通りの方法があります。1 つ目は WebPart クラスから継承したカスタム クラスを作成する方法で、2 つ目はユーザー コントロールを作成する方法です。それぞれのメリット・デメリットについては、後で詳しく取り上げます。まずは、WebPart から派生した簡単なクラス (図 3 を参照してください) の作成から始めることにしましょう。
Web パーツ インスタンスは、特定のインデックス上の特定の WebPartZone 内にある特定のページに存在します。1 つの WebPartZone は複数の Web パーツを持つことができます。たとえば、図 4 は WebPartZone1 という名称のゾーン内に 2 つの Web パーツを表示している例です。

図 4 特定ゾーン内の Web パーツ
Web パーツは、プログラミング、宣言のいずれの方法でもゾーンに追加することができます。Web パーツをカタログのパーツに追加する方法については、後ほど紹介します。CatalogPart を作成すると、ポータル ユーザーは新しい Web パーツを実行時に WebPartZones に追加することができるようになります。
Web パーツをコードにより WebPartZone に追加する方法は、扱おうとする Web パーツのタイプによって異なります。WebPart から継承したクラスであれば、クラスのインスタンスをプログラミングにより生成し、WebPartManager クラスの AddWebPart メソッドを呼び出します。AddWebPart を呼び出す際には、Web パーツ インスタンス、ターゲット WebPartZone、そして Web パーツが表示されるターゲット ゾーン内のインデックスを引数として渡さなければなりません。:
// WebPart 派生クラスから Web パーツ インスタンスを生成 WebPart wp1 = new WingtipWebParts.HelloWorld(); WebPartManager1.AddWebPart(wp1, WebPartZone1, 0);
以上は Web パーツをプログラム的に WebPartZone に追加する方法です。一方、宣言による方法は、Web パーツ ページの .aspx ファイル内でコントロール タグを使用します。たとえばこのページを、ユーザーが始めて取得したときに Web パーツを特定の WebPartZone に表示させたい場合、WebPartZone 内に ZoneTemplate を追加することができます。:
<%@ Register Assembly="WingtipWebParts" Namespace="WingtipWebParts"
TagPrefix="Wingtip" %>
<asp:WebPartManager ID=" WebPartManager1" runat="server" />
<asp:WebPartZone ID="WebPartZone1" runat="server" HeaderText="Zone 1">
<ZoneTemplate>
<Wingtip:HelloWorld runat="server" id="HelloWorld" />
</ZoneTemplate>
</asp:WebPartZone>
Web パーツ ページに表示される Web パーツは、何もしなければ外観はとてもつまらないものとなるということを覚えておいてください。イントラネット ポータル アプリケーションへの機能リクエストとしてよくあげられるのが、サイトへの "スキン" 設定機能、つまりユーザーの趣味に合わせて外観を変更する機能です。少し前まで、こうした要求に対応するにはコントロール レンダリングの変更をサポートする独自のインフラストラクチャを構築しなければいけませんでした。それには、非常にたくさんの労力が必要でした。ASP.NET 2.0 では、個々のコントロールにページ全体またはアプリケーション レベルで適用可能なスタイルやコントロール属性のコレクションである「テーマ」というコンセプトを導入しています。
ポータル アプリケーションを洗練されたプロフェッショナルな外観にするには、WebPartZone、EditorZone、CatalogZone すべてのコントロールの外観をカスタマイズします。この作業ははじめての人にとっては退屈に思えるかもしれません。ボディー、タイトル バー、さらに Web パーツ、Editor パーツ、Catalog パーツの動的メニュー等、外観のスキニングやブランディングといった作業が含まれるからです。さいわい、ASP.NET 2.0 に新しく組み込まれたテーマ機能を使用することで、こうしたビジュアル的な処理すべてを .aspx ファイルから抜き出し、再利用可能な .skin ファイルおよび .css ファイルに保存することが可能です。本資料で使用するサンプル ポータル アプリケーション (MSDNMagazine Web サイトより入手できます) では、スキンおよびテーマを使って Web パーツ の外観をカスタマイズしています。
表示モードとページ スコープ
WebPartManager コントロールのインスタンスは、Web パーツ ページごとに 1 つのインスタンスが実行され、WebPartZone コントロールとの関連付け方法等、Web パーツ インスタンスの管理を行います。また、WebPartManager コントロールは、Web パーツ ページのブラウズ、デザイン、編集の表示モードの切り替えを可能にするプログラマティック インターフェイスを提供します。たとえば現在のページをプログラミングによりデザイン モードに切り替えたい場合は、以下のコードのように、DisplayMode プロパティに DesignDisplayMode を設定したイベント ハンドラを持つリンク コントロールを追加するだけです。
WebPartManager1.DisplayMode = WebPartManager.DesignDisplayMode;
Web パーツ ページの各表示モードの違いを理解しておくことは大切です。Web パーツ ページは、デフォルトではブラウズ モードで動作します。ブラウズ モードでは、ユーザーは Web パーツに変更を加えることはできません。Web パーツ ページがデザイン モードに切り替わると、ユーザーは Web パーツを WebPartZone 内またはゾーン間で移動することができるようになります。利用可能な表示モードについては、図 5 の一覧をご覧ください。
ASP.NET 2.0 の Web パーツ コントロールは、ブラウザ内でのドラッグ アンド ドロップを可能にするために必要なすべての DHTML および JavaScript を自動的に生成してくれます。こうした DHTML や JavaScript 機能をサポートしていないブラウザの場合は、ドラッグ アンド ドロップ編集を除いた機能すべてが保持されます。つまり、すべてのクライアントで Microsoft Internet Explorer 5.0 またはそれ以上を使用する必要があるといった心配は不要です。また、ASP.NET 2.0 の Web パーツは、パーソナライゼーション データの格納および取得管理を行い、前回のセッション時にユーザーが配置した Web パーツの場所を記憶します。
WebPartManager は、表示モードの切り替えをコードからの可能にするだけでなく、Web パーツ ページのユーザー スコープと共有スコープの切り替え手段も提供します。ページのスコープは、Web パーツへの変更がカスタマイゼーションかパーソナライゼーションかを示します。カスタマイゼーションによる変更はすべてのユーザーから見ることができ、パーソナライゼーションによる変更は設定を行ったユーザーにしか見えないということを覚えておいてください。Web パーツ ページの変更は、以下のように WebPartManager コントロールの ToggleScope メソッドを呼び出すことで実行できます。
WebPartManager1.Personalization.ToggleScope();
デフォルトのスコープはユーザー スコープです。このスコープの場合、Web パーツへの変更は、現在のユーザーのみに見えるパーソナライゼーションとして記録されます。ToggleScope メソッドを呼び出し、呼び出しが成功すると、Web パーツ ページは共有スコープになります。このスコープの場合、変更はカスタマイゼーションとして記録されます。共有スコープは、管理者やサイトの設計者が Web パーツ ページ上の Web パーツに変更を加える際、すべてのユーザーに反映されるカスタマイゼーションを一括して行えるようにするためのものです。グローバル カスタマイゼーションとパーソナライゼーションによる変更で重複が生じた場合は、ユーザーのパーソナライゼーションによる変更が常に優先されます。
現在のユーザーが常に共有スコープに移行できるわけではありません。デフォルトでは、共有スコープへの移行に必要な権限を与えられているユーザーはいません。この権限は、Web.config で付与する必要があります。以下は、admin または site_designer が設定されているすべてのユーザーに、共有スコープに移行してグローバル カスタマイゼーションを実行できる権限を付与するコード例です。:
<webParts>
<personalization>
<authorization>
<allow roles="admin, site_designer" verbs="enterSharedScope" />
</authorization>
</personalization>
</webParts>
プロパティとパーソナライゼーション
各 Web パーツ オブジェクトには、カスタマイズまたはパーソナライズが可能な標準プロパティ セットが定義されています。たとえば、Web パーツは Title プロパティというプロパティを持ちます。このプロパティは、WebPartZone に追加した後でもカスタマイズすることができます。EditorZone コントロールや各種 Editor パーツを使用すると、ユーザーは Web パーツ プロパティを変更することが可能になります。
ユーザーが Web パーツ ページに EditDisplayMode を設定すると、[Web Part] メニューに [Edit] コマンドが表示されます。特定の Web パーツ上で [Edit] コマンドを実行すると、EditorZone と、EditorZone に配置された Editor パーツが現れます。ASP.NET 2.0 には、Web パーツ標準の外観、動作、レイアウトを修正できる Editor パーツがあらかじめいくつか組み込まれています。
Web パーツ パーソナライゼーション スキーマは、パーソナライズ可能なカスタム プロパティを追加することで簡単に拡張できます。Web パーツ クラス定義にプロパティを追加し、Personalizable、WebBrowsable や WebDisplayName といった属性を適用するだけです。これだけで、Web パーツ コントロールは、カスタマイズまたはパーソナライズされたプロパティ値の格納、取得を処理できるようになります。
パーソナライゼーション プロパティを作成する場合、通常は同時にプライベート フィールドを定義します。:
private bool _HR = true;
[Personalizable(PersonalizationScope.User),
WebBrowsable, WebDisplayName("Show HR News"),
WebDescription("Use this property to show/hide HR news")]
public bool HR
{
get { return _HR; } set { _HR = value; }
}
ユーザーに上記のようにプロパティをパーソナライズさせたい場合は、現在のページの EditorZone に PropertyGridEditorPart を追加するだけです。図 6 は、上記設定を行った実際の表示画面です。

図 6 Editor パーツの使用によりユーザーは Web パーツのパーソナライズが可能になります。
Web パーツ プロパティを文字列型または数値型で定義した場合、PropertyGridEditorPart は値を変更するためのテキストボックスを表示します。Web パーツ プロパティを ブール値で定義した場合は、図 6 のようにチェックボックスを表示します。
図 7 は、Windows SharePoint Services による Web パーツ開発から拝借した一歩進んだプログラミング テクニック、列挙型のパソーナライゼーション プロパティを定義する方法を示しています。
WebBrowsable パーソナライゼーション プロパティを列挙型で作成できるすばらしいところは、図 7 の Timeframe プロパティで示されているように、PropertyGridEditorPart が当該ユーザーに利用可能なプロパティ設定を表示するドロップダウン リストを生成してくれる点です。これはユーザーにとっては非常に便利であり、ユーザーは有効なプロパティ値を確実に選択できるようになります。
Timeframe プロパティ定義にはもう 1 点特筆すべき点があります。PersonalizationScope.Shared を設定した Personalizable 属性です。プロパティを共有プロパティとして定義する際、このような方法で定義すると、カスタマイズができてパーソナライズはできないプロパティとなります。共有プロパティはパーソナライゼーションでは利用できません。そのため、現在の Web パーツ ページがユーザー スコープにあると、その共有プロパティは PropertyGridEditorPart には表示されません。表示されるのは、Web パーツ ページが共有スコープにあるときのみです。
Web パーツ カタログ
あらかじめ Web パーツが WebPartZones に組み込まれた Web パーツ ページを見たことがあるでしょう。このテクニックは、ユーザーによる Web パーツの追加を実行時に可能にするアプローチにより実装できます。具体的には、PageCatalogPart や DeclarativeCatalogPart などの CatalogZone および CatalogParts (図 8 を参照してください) の使用で実現します。
この方法で CatalogZone や CatalogPart を追加すると、ユーザーは 図 9 で示すようなユーザー インターフェイスにより Web パーツを実行時に動的に追加できるようになります。

図 9 CatalogZone を使用することによりユーザーは Web パーツ を動的に追加することが可能になります。
始めに、PageCatalogPart の役割を理解しておきましょう。デザイン表示モードまたは編集表示モードでは、ユーザーは Web パーツ上で Close コマンドを呼び出すことができます。ユーザーが Web パーツをクローズすると、Web パーツと Web パーツのパーソナライゼーションまたはカスタマイゼーション設定が保持されるため、同じ Web パーツを再度後から追加することができます。これができるように、PageCatalogPart はこれまでにクローズされた Web パーツ、つまりページに追加することのできる Web パーツすべてをリスト表示します。
Close コマンドは Delete コマンドとは異なります。Delete コマンドは編集表示モードでしか使用できません。ユーザーが Web パーツを削除すると、カスタマイゼーション データやパーソナライゼーション データも含め、その Web パーツの存在そのものに関する情報がストレージから削除されます。
DeclarativeCatalogPart は、宣言により Web パーツを配置することができます。図 8 のコードは、このカタログに WeatherWebPart という名前のカスタム パーツを配置する方法を示しています。この手法を用いることで、ユーザーはあらゆる種類の Web パーツを利用できるようになります。
なお、Web パーツ ページを構築するにあたり基礎知識を習得するため補完資料として、"ASP.NET 2.0 Web パーツ フレームワークの紹介" (Stephen Walther 寄稿) のご一読をお奨めいたします。本資料では、EditorZones と CatalogZones を使用して Web パーツ ページを構築するより詳しい具体例を提供しています。また、Verb や Connection、さらに Web パーツのインポートとエキスポートといった高度なトピックまで掘り下げています。
ASP.NET 2.0 ポータルの開発
Web パーツ インフラストラクチャだけでなく、ASP.NET 2.0 にはイントラネット用のポータル サイトの開発を楽しくする新機能がいくつかあります。前述のように、テーマやスキンの導入により、ポータル ページにおけるスタイル プロパティの分離や、ページごとではなく全体に対するスタイルの一括変換が容易になりました。これよりすごいとなると、やはりマスター ページの導入です。マスター ページの使用により、すべての WebPartZone と WebPartManager コントロール全体を 1 つのテンプレート ページに分離することが可能となります。どのページも、このテンプレート ページからベースとなる外観や機能を継承することができます。本資料のサンプル ポータルでは、マスター ページにある各 WebPartZone の ZoneTemplate 内に ContentPlaceholder コントロールを追加するというおもしろいテクニックを使っています。この方法では、このマスター ページを使用するコンテンツ ページから、各 ContentPlaceHolder コントロールに割り当てられている Content コントロールを使って独自の Web パーツを追加することができます。
ポータル サイトのマスター ページを定義する際に必要なことの 1 つに、ユーザーに対しどのようなカスタマイゼーション機能を設定するかがあります。すでに学習したように、ページに追加されているゾーン タイプによって、ユーザーがページを変更する際に選択できるカスタマイゼーション モードは数種類あります。
カスタマイゼーション オプションをユーザーに表示するやり方の一つに、WebPartManager コントロールとボタンのコレクション (通常は LinkButtons) を 1 つのユーザー コントロールにラップし、これをマスター ページにドロップしてサイトのすべてのページでカスタマイゼーション オプションを提供するといった方法があります。上記のように複数のコントロールを 1 つのユーザー コントロールにカプセル化するという処理は (WebPartManagerPanel と呼びましょう)、たとえば Web サイトのマスター ページが複数になってしまい、ページ別に部分的に異なるレイアウトを割り付けるような場合に便利です。図 1 のポータル アプリケーションのメニュー バーには、WebPartManager の現在のモードとスコープを表示するサンプル ユーザー コントロールが表示されています。また、ページのモードを WebPartManager (本サンプル ポータル アプリケーションが使用しているコントロール) がサポートしている編集モードに変更することができる LinkButtons も用意されています。
WebPartManagerPanel で提供できる別の便利な機能としては、現在のユーザーと現在のページの状況をもとに利用可能な表示モードを動的に表示 / 非表示にできるところです。WebPartManager の SupportedDisplayModes コレクションを調べることでサポートされている表示モードを照会し、得られた情報を使用して当該モードを提供するメニュー アイテムをオンまたはオフにすることができます。たとえば、現在のページが CatalogDisplayMode をサポートしているかどうか知りたい場合は、次のような処理を実行します。:
if (WebPartManager1.SupportedDisplayModes.Contains(
WebPartManager.CatalogDisplayMode)) {
// ここでカタログ表示モード LinkButton を有効にする...
}
なお、適切な権限を持たないユーザーのコンテキスト内で ToggleScope を呼び出すと、呼び出しは失敗してしまいます。ユーザーの共有スコープへの移行を可能にする UI エレメント (ユーザー インターフェイス エレメント) を表示または非表示にする判断をする場合は、WebPartManager コントロールの Personalization プロパティが提供する CanEnterSharedScope プロパティを照会するようなコードの実装がいいでしょう。:
if (WebPartManager1.Personalization.CanEnterSharedScope) {
// ユーザーの共有スコープへの移行を可能にする UI エレメントを表示
}
本資料のサンプルで使われている WebPartManagerPanel ユーザー コントロールには、現在のユーザーとページ機能を元に表示を動的に調整するパネルの実装一式が含まれています。
Web パーツとしてのユーザー コントロール
Windows SharePoint Services で Web パーツを構築する際に感じるストレスの 1 つに、デザイナの支援なしにコントロールのユーザー インターフェイスすべてをプログラミングで作成しなければならないという点があります。多くの Web パーツは、相互に作用し合うサーバー側コントロールの集まりであるという事実のため、残念ながら Web パーツの作成においては Visual Studio デザイナは思う通りに使えません。この解決策はあきらかで、開発者が ASP.NET ユーザー コントロールを作成し、作成したコントロールを Web パーツとして使えるようにできればよいのです。(SmartPart というサードパーティー製のツールにより、Windows SharePoint Services でユーザー コントロールを Web パーツとして使用することが可能になります。)
ASP.NET 2.0 の Web パーツは、どのコントロールも修正やラップを行わずに直接 Web パーツとして使用できるようにすることでこの問題に対応しています。このことは、ユーザー コントロールを Web パーツ コレクションに組み込めるという点だけでなく、どんなカスタム コントロールでも今ある ASP.NET ページに統合できるという点で非常に利便性が高いです。
内部的な動作としては次のようになります。標準コントロール (非 Web パーツ) が WebPartZone に追加されると、WebPartManager.CreateWebPart が暗黙に呼び出されます。これにより、GenericWebPart クラスのインスタンスが生成され、先ほど追加されたコントロールで初期化が行われます。GenericWebPart クラスは WebPart 基本クラスから継承されたクラスで、Web パーツ プロパティのコア部分の実装を提供します。GenericWebPart は、構築されると初期化に使用したコントロールを子コントロールとして追加します。レンダリング処理では、大半のコンポジット コントロール同様、GenericWebPart は応答バッファに対するレンダリングは一切行わずに、自身の子コントロールにレンダリングを任せます。結果として、どんなコントロールでもページ上の WebPartZone に追加することができ、追加されたコントロールは問題なく動作します。たとえば、次のページはユーザー コントロール 1 つと標準 Calendar コントロール 1 つを持つ WebPartZone を定義していて、いずれも GenericWebPart クラスの生成時に暗黙でラップ処理が行われます:
<%@ Register Src="webparts/CustomerList.ascx"
TagName="CustomerList" TagPrefix="Wingtip" %>
<asp:WebPartManager ID=" WebPartManager1" runat="server" />
<asp:WebPartZone ID="WebPartZone1" runat="server" HeaderText="Zone 1">
<ZoneTemplate>
<Wingtip:CustomerList runat="server" id="CustomerList" />
<asp:Calendar runat="server" id="CustomerCalendar" />
</ZoneTemplate>
</asp:WebPartZone>
標準 Web パーツ同様、動的に GenericWebPart にラップされた状態のコントロールを生成することが可能です。ユーザー コントロールの場合、Page.LoadControl の呼び出しによりコントロールのインスタンスを動的にロード、生成しなければいけません。続いて、一意の ID を明示的にコントロールに割り当てます。次に、WebPartManager オブジェクトの CreateWebPart メソッドを呼び出して、GenericWebPart クラスのインスタンスを生成します。これにより、ユーザー コントロール インスタンスのラッパとして動作します。最後に、CreateWebPart 呼び出しから返される GenericWebPart 参照を受け取り、追加したい WebPartZone を指定した AddWebPart 呼び出しに渡します。:
// User Control ファイルから Web パーツ インスタンスを生成します Control uc = this.LoadControl(@"webparts\CompanyNews.ascx"); uc.ID = "wp2"; GenericWebPart wp2 = WebPartManager1.CreateWebPart(uc); WebPartManager1.AddWebPart(wp2, WebPartZone1, 1);

図 10 GenericWebPart
このテクニックの唯一の欠点は、コントロールに対して Web パーツ固有の機能の制御機会を持てない点です。理由は、GenericWebPart クラスがコントロールからではなく WebPart から継承されたクラスであるためです。このことは、GenericWebPart でラップされたコントロールを持つページを実行するとすぐに分かります。ほとんどの Web パーツがそうであるように、デフォルトではタイトルに Untitled、アイコン無し、コントロールに関連付けされた説明も一切ない状態で表示されます。図 10 は、デフォルトのタイトル (Untitled) とアイコンを持つ GenericWebParts でラップされたサンプル ユーザー コントロールです。
この問題の対処方法の 1 つとして、ユーザー コントロールの Init イベントのハンドラを追加する方法があります。コントロールが GenericWebPart でラップされている場合は (GenericWebPart でラップされているかどうかは Parent プロパティのタイプを照会することで判断できます)、GenericWebPart クラスの属性を次のように設定します:
void Page_Init(object src, EventArgs e) {
GenericWebPart gwp = Parent as GenericWebPart;
if (gwp != null) {
gwp.Title = "My custom user control";
gwp.TitleIconImageUrl = @"~\img\ALLUSR.GIF";
gwp.CatalogIconImageUrl = @"~\img\ALLUSR.GIF";
}
}
ページを再度実行すると、ユーザー コントロールが GenericWebPart でラップされている限り、親 GenericWebPart のプロパティに行われた変更は、コントロールを持つ Web パーツのレンダリング時に反映されます。図 11 は新しく属性が追加されたユーザー コントロールです。タイトルとアイコンに注目してください。

図 11 タイトルとアイコン
この他の有力なソリューションとしては、IWebPart インターフェイスを直接ユーザー コントロール クラスに実装するといった方法があります。ただしこの方法はあまり効果的ではないかもしれません。なぜなら、詳細は GenericWebPart クラスが処理しているため、ユーザー コントロールが Web パーツについて照会されることはまずないからです。さいわい、GenericWebPart クラスの設計者はこうした必要性を予見していたようで、当該コントロールが IWebPart インターフェイスを実装している場合、ラップされたコントロールに自動的に委任するプロパティを GenericWebPart クラスに実装してくれています。
つまりユーザー コントロールの Web パーツ機能のカスタマイズとは、IWebPart インターフェイスを実装してそれが定義する 7 つのプロパティ値を指定する、ただそれだけのことなのです。図 12 は、GenericWebPart プロパティを動的に変更する前と同じ結果となるユーザー コントロールのコードビハインド クラスの一例を示しています。
読者の皆さんの中には、ではユーザー コントロールに代替の基本クラスを作成してはどうかと考えた人がいるかもしれません。そのコントロールはたとえば、UserControl から派生したクラスで、IWebPart を実装していてポータル内のすべてのユーザー コントロールから継承できるといった具合です。実はこの基本クラスは、すでに本資料のサンプル ポータルで実現しています。このソリューションだと、コンストラクタで処理しているプロパティをユーザー コントロール自身が初期化することができますので、残りの処理はそのままにしておきます。図 13 は、IWebPart を実装したユーザー コントロールの代替基本クラスと、このクラスを利用してタイトルとアイコン プロパティを設定するユーザー コントロールのコードビハインド クラスを示しています。
ユーザー コントロールでここまで色々なことができるとなると、ユーザー コントロールでデザイナ支援を実現し、WebPart 機能のカスタマイズもできれば、なぜ最初からカスタム コントロールを作成しないのかと疑問に思うかもしれません。実際理由はいくつかあります。まず第一の理由は、ユーザー コントロールにはカスタム Verb を追加できない点です。そのため、WebPart から直接派生させ、Verbs プロパティをオーバーライドせざるを得ないのです。あるいは別の選択肢として、IWebEditable のコントロールへの実装を検討してみてもいいでしょう。
その他の理由としては、本来ユーザー コントロールのスコープはアプリケーション ディレクトリとなっている点です。つまり、複数の Web アプリケーションでユーザー コントロールの実装を共有することは不可能ということです。それをするには、.ascx ファイルをプロジェクトから別のプロジェクトに物理的にコピーしなければいけません。これに対し、WebPart クラスから派生したカスタム Web パーツの場合、再利用可能な DLL にコンパイルした上で、グローバル アセンブリ キャッシュ (GAC) にグローバルに配置することができます。またカスタム Web パーツを使用すると、Visual Studio 内でデフォルトの外観を変更できるコントロール用のカスタム デザイナを作成することができます。また、ツールボックスにドロップされた際に Web パーツと関連付けするアイコンを作成することもできます。図 14 は、カスタム Web パーツ vs. ユーザー コントロールの能力比較表です。
Web パーツとパーソナライゼーション プロバイダ
プロバイダは ASP.NET 2.0 の新機能の 1 つであり、今回のリリースにおいて、コーディングをほとんどせずに機能性の高いコントロールをこれだけたくさん提供できる理由の 1 つでもあります。プロバイダの基本的な考え方としては、特定機能に対するデータ関連処理の共通セットを定義し、それらの処理を共通 ProviderBase クラスから継承した抽象クラスの宣言に集約するものと理解するといいでしょう。本資料で取り上げているパーソナライゼーション機能の場合、データ関連処理としての必須項目は以下が挙げられます:
- 特定のページおよびユーザーの Web パーツ プロパティとレイアウトの保存。
- 特定のページおよびユーザーの Web パーツ プロパティとレイアウトの読み込み。
- 特定ページの汎用 Web パーツ プロパティおよびレイアウトの (ゼネラル カスタマイゼーション目的で) 保存。
- 特定ページの汎用 Web パーツ プロパティおよびレイアウトの (ゼネラル カスタマイゼーション目的で)読み込み。
- 特定ページおよびユーザーの Web パーツ プロパティとレイアウトをデフォルトにリセット。
- 特定ページの Web パーツ プロパティおよびレイアウトを (ゼネラル カスタマイゼーション目的で) デフォルトにリセット。
上記の他にも、パーシステンス機能を必要とするパーソナライゼーション インフラストラクチャの一部である補助機能はいくつかありますが、おおむね 6 つの機能に集約されます。これら 6 つのアクションを実行できる能力があり、データを何の問題なく保存・取得する能力のあるクラスがあるとすれば、そのクラスを使うことで各ページ上の WebPartManager は、すべてのパーソナライゼーションおよびカスタマイゼーション データをサイトの実行時に保存することができます。このようなメソッドを定義した抽象クラスを PersonalizationProvider といい、このクラスを具象化したものがデフォルトで使われる SqlPersonalizationProvider です。これら 6 つの機能を実現するメソッドを 3 つのメソッドにまとめました。それが 図 15 です。どのメソッドも、渡された userName パラメータが NULL か否かによって、ユーザー パーソナライゼーションまたは共有カスタマイゼーションのいずれとでも動作できることに注目してください。

図 16 相互関係
すべてのパーソナライゼーション データはストレート バイナリ データ (byte[]) として格納され、SqlPersonalizationProvider はデフォルトではデータベース内のイメージ ファイルに書き込まれることに注意してください。ASP.NET 2.0 はこうしたメソッドを提供するクラスの存在を認識しているため、今までよりもたくさんのロジックをコントロールの基本セットに組み込むことができます。今回は、Web パーツを使用する各ページ上の WebPartManager が、ページごとのパーソナライゼーション設定をシリアライズ、復元できるよう、現在の PersonalizationProvider クラスに対して適切な呼び出しを実行します。図 16 の図は EditorZone コントロールとデフォルト SqlPersonalizationProvider の相互関係を示したものです。
ASP.NET 2.0 を使いこなしていくにつれ、このプロバイダ アーキテクチャの用例をよりたくさん知ることができるでしょう。プロバイダとしては、メンバーシップ用、ロール マネージメント用、サイト マップ データ用、ヘルス モニタリング用など様々な用途のプロバイダがあり、いずれも複数のコントロールで相互に運用できるよう、類似したメソッドのコアセットを定義しています。
パーソナライゼーション データ ストアを変更する
ASP.NET 2.0 のほとんどのプロバイダ同様、パーソナライゼーション用のデフォルト プロバイダは、SQL Server のバックエンドをターゲットとして実装されています。設定ファイルを変更しなければ、デフォルトの SqlPersonalizationProvider は、ローカル ファイルベースのデータベースをサポートする SQL Server 2005 Express Edition への接続文字列を使用します。接続文字列は次のようになります。:
data source=.\SQLEXPRESS; Integrated Security=SSPI; AttachDBFilename=|DataDirectory|aspnetdb.mdf; User Instance=true
SQL Server 2005 Express Edition ファイルベース データベースを使用するメリットは、ユーザーによるセットアップ作業がなくてもその場でデータベースが作成される点です。つまり全く新しいサイトを作成した場合、データベースのセットアップを一切せずにすぐにパーソナライゼーション機能を使い始めることができるのです。サイトに初めてアクセスし、何らかの設定を行うと、当該サイトの App_Data ディレクトリに aspnetdb.mdf という名前のファイルが新規に作成され、すべてのデフォルト プロバイダのサポートに必要なテーブル、ストアド プロシージャで初期化が行われます。
これは、たくさんの同時実行ユーザーのサポートやスケールアップを必要としない小規模サイトにとっては最適ですが、エンタープライズ システム等の大規模システムの場合は十分に管理された専用データベース サーバーにデータを格納する必要があります。さいわいなことに、SqlPersonalizationProvider が使用するデータベースの変更は簡単です。SqlPersonalizationProvider の設定では、LocalSqlServer への接続文字列を初期化します。つまり、LocalSqlServer という名前を持つ設定ファイルの <connectionStrings> セクション内でエントリを検索し、そこに関連付けされている接続文字列を使ってデータベースへの接続をオープンします。この文字列は、少し前のパラグラフで紹介しています。つまり、デフォルトでは SQL Server 2005 Express Edition のローカル .mdf ファイルに書き込みが行われることになります。これを変更するには、Web.config ファイルで LocalSqlServer 接続文字列コレクションをクリアした上で、新しい接続文字列値を指定し直す必要があります (あるいは、マシンワイドの設定ファイルである Machine.config ファイルでこの設定を行うと、当該マシン上のサイトすべてに設定を反映させることができます)。以下は、SQL Server 2000 のローカル インスタンスを示すプロバイダ データベースを変更する Web.config ファイルの記述例です。:
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/
V.2.0">
<connectionStrings>
<clear />
<add name="LocalSqlServer" connectionString=
"server=.;integrated security=sspi;database=aspnetdb"/>
</connectionStrings>
...
</configuration>
この変更を有効にするには、SqlPersonalizationProvider が必要とするテーブルとストアド プロシージャが格納されたローカル サーバー上に aspnetdb という名前のデータベースが存在していなければいけません。このデータベースを作成するためのユーティリティ (aspnet_regsql.exe) が、ASP.NET 2.0 に用意されています。このユーティリティをデフォルト設定のまま実行すると、すべてのプロバイダに必須のテーブルを持つ aspnetdb という名前のデータベースがローカルに作成されます。あるいは、テーブルやストアド プロシージャを既存のデータベースにインストールしてもかまいません。こうしたテーブルやストアド プロシージャの名前はすべて "aspnet" というプレフィックスで始まるため、既存のテーブルを壊してしまうという心配はまずありません。
ASP.NET 2.0 のすべてのプロバイダ同様、このレベルのインダイレクションでは、ページまたはページに含まれる Web パーツに一切の修正を加えることなくバックエンド データ ストアを完全に変更することが可能な、非常にフレキシブルなアーキテクチャを実現できます。
独自のパーソナライゼーション プロバイダを作成する
パーソナライゼーション プロバイダの接続文字列を変更できるということは、ある程度のフレキシビリティを手にすることができます。しかし、SqlPersonalizationProvider は内部的に System.Data.Sql.Client 名前空間を使ってデータの取得を行っています。つまり、SqlServer データベースに限定されてしまっていることを意味します。パーソナライゼーションを別のデータベース、あるいは全く別のデータ ストアに格納する必要があるときは、独自のカスタム パーソナライゼーション プロバイダを構築するという手順が別途必要となります。さいわい、大変な作業のほとんどはすでに終わっているため、後はそれを利用するだけです。パーソナライゼーション データを SqlServer 以外のデータ ストアに格納する例として、本資料のサンプル ポータル サイトでは、FileBasedPersonalizationProvider という名前のカスタム パーソナライゼーション プロバイダの完全実装を用意しています。FileBasedPersonalizationProvider は、アプリケーションの App_Data ディレクトリ内のローカル バイナリ ファイルにすべてのパーソナライゼーションおよびカスタマイゼーション データを保持します。バイナリ ファイル名は、ユーザーおよびパスごとに一意に生成されます。また、全体的なユーザー設定については一意のパスごとに 1 つのファイルが存在します。
カスタム パーソナライゼーション プロバイダを構築するには、まず PersonalizationProvider 基本クラスからクラス継承により新規クラスを作成し、基本クラスから継承されたすべての抽象メソッドをオーバーライドする必要があります。図 17 のクラス定義部分で具体的な方法を示しています。
パーソナライゼーション プロバイダを作成するにあたり必ず実装しなければならないメソッドは実際には 2 つしかありません。LoadPersonalizationBlobs と SavePersonalizationBlob です。これら 2 つのメソッドは、パーソナライゼーション データのバイナリ シリアライゼーションを実行します。メソッドは、ページがロードされたときのデータの取得や、Web パーツを組み込んだページ上で編集、カタログ、デザイン ビューでデータが変更された場合にデータを反映させる場合などに、(通常は特定ユーザーの代理として) パーソナライゼーションう インフラストラクチャが呼び出します。
サンプル コードのダウンロードでは、SavePersonalizationBlob の実装が、渡された userName と path を元にした一意の名前のファイルに dataBlob パラメータを書き込んでいます。同様に、LoadPersonalizationBlobs の実装でファイル検索を行い (SavePersonalizationBlob の場合と同じネーミング スキームを使用)、ユーザー データ ブロブまたは共有データ ブロブのいずれかを返します。いずれのメソッドも、渡された userName パラメータが NULL の場合、デフォルトで共有データを保存またはロードします。NULL 以外の場合は、ユーザー データを保存またはロードします。図 18 は、FileBasedPersonalizationProvider サンプル プロバイダにおけるこれら 2 つのメソッドの実装例と、ユーザー名とパス情報をもと一意のファイル名を生成する一対のヘルパー メソッドを示しています。
プロバイダの実装が完了したら、そのプロバイダを [personalization configuration] セクションの [providers] セクションから、"登録されたパーソナライゼーション プロバイダ" として追加します。この作業を行う前に、追加するプロバイダを Web.config ファイルでパーソナライゼーションのデフォルト プロバイダとして指定してあげる必要があります。以下は、今回作成したサンプル カスタム プロバイダをアプリケーションのデフォルト プロバイダに指定する場合の記述例です。:
<webParts>
<personalization defaultProvider="FileBasedPersonalizationProvider">
<providers>
<add name="FileBasedPersonalizationProvider"
type="Wingtip.Providers.FileBasedPersonalizationProvider" />
</providers>
</personalization>
</webParts>
サイトを実行してみます。すると、すべてのパーソナライゼーション データがローカル バイナリ ファイルに格納されます。サンプルは、決して拡張性の高いソリューションとは言えませんが、バックエンドの種類に関係なく独自のパーソナライゼーション プロバイダを実装する場合の参考になるかと思います。図 19 は、新しく設定したプロバイダが Web パーツ インフラストラクチャ全体に組み込まれた様子を示しています。

図 19 FileBasedPersonalizationProvider を使用する
次のステップへ
ASP.NET 2.0 とそこに組み込まれた新しい Web パーツ コントロール セットをうまく利用することにより、カスタマイゼーションとパーソナライゼーションをサポートするリッチなポータル アプリケーションを手軽に作成できることが分かりました。このインフラストラクチャの最も重要な特徴を挙げるとすれば、おそらくそのプラガビリティでしょう。プロバイダ アーキテクチャの支援により、どんなバックエンド データ ストアにも、特定のシリアライゼーション インプリメンテーションやデータ ストレージに限定されることなくパーソナライゼーション データを書き込むことが可能です。早速今日にでも、ASP.NET 2.0 の Web パーツをお試しいただきカスタマイズ可能なサイトを作成してみてください。
本資料をお楽しみいただけましたら、実際に ASP.NET 2.0 ポータル アプリケーションで Web パーツを構築してみるといいでしょう。より多くのことを学習できるはずです。本資料で紹介したサンプルは、MSDN Magazine Web サイトからダウンロードできます。また、ASP.NET 2.0 QuickStart tutorials (英語) にもたくさんのサンプルが用意されています。参考資料として役立ててください。それからぜひ、Fredrik Normen'のブログ (英語) も訪れてください。ASP.NET 2.0 で Web パーツを扱う際の用例がいくつか紹介されています。