はじめに
ASP.NET 1.0の特徴の1つに、「アダプティブレンダリング」機能がありました。この機能のお陰で、ASP.NET Webページでレンダリングされるマークアップが、アクセス側のブラウザにとって適したものになります。要するに、ASP.NETページにアクセスすると、そのWebコントロールがマークアップにレンダリングされ、ブラウザに送られて表示されるのです。しかし、Webコントロールによって生成されるマークアップは、ページにアクセスするときに使用するブラウザに依存します。「アップレベル」ブラウザでアクセスすると、ASP.NET WebコントロールはHTML 4.0準拠のマークアップをレンダリングします。一方、「ダウンレベル」ブラウザの場合、WebコントロールはHTML 3.2準拠のマークアップをレンダリングします。ASP.NET 1.xでは、「machine.config」または「web.config」内の<browserCaps>要素を通じて、ブラウザに「アップレベル」または「ダウンレベル」のラベルが付けられました(ASP.NET 1.xの1つの欠点は、既定で、IE 4.0以上だけが「アップレベル」とマークされることでした。このため、FireFoxやOperaなどの最新のブラウザが、ダウンレベルとして認識されてしまいました)。ASP.NET 1.xのアダプティブレンダリング機能の詳細については、「A Look at ASP.NET’s Adaptive Rendering」を参照してください。
ASP.NET 2.0は、ASP.NET 1.xと同様のアダプティブレンダリング機能を備えていますが、ブラウザの機能は、<browserCaps>要素ではなく、ブラウザ定義ファイルで決定されます(<browserCaps>は、下位互換性のためにASP.NET 2.0でも引き続きサポートされていますが、将来にわたってのサポートは保証されていないため、使用しない方が良いでしょう)。ASP.NET 2.0のレンダリングフレームワークは、アダプティブレンダリングに加えて、「コントロールアダプタ」を使って構成することもできます。コントロールアダプタはオプションのクラスです。これが存在し、正しく構成されている場合は、コントロールの既定のレンダリングロジックではなく、このコントロールアダプタを使ってWebコントロールがレンダリングされます。つまり、コントロールアダプタを使用することで、Webコントロールの核となる機能を利用できるだけでなく、発行されるマークアップを完全にカスタマイズできます。これは、すべてのブラウザの既定のレンダリングを変更したい場合や、携帯電話やポータブルデバイスなど、特定のユーザーエージェントに対してカスタマイズされたレンダリングを提供する必要がある場合に便利です。
「ASP.NET 2.0 CSS Friendly Control Adapters」には、ASP.NET 2.0のコントロールアダプタの機能を理解する上で参考になる例が示されています。Microsoftが提供する無料のこのコントロールアダプタセットは、優れたCSSの方法を使って、さまざまな組み込みのASP.NETコントロールをレンダリングする、コントロールアダプタのセットを提供します。例えばMenu Webコントロールは、既定ではHTMLの<table>としてレンダリングされます。しかし、コントロールアダプタでは、MenuはCSSのポジショニング機能を使用する「順序なしリスト」としてレンダリングされ、適切にメニューが表示されます。さらに、CSSコントロールアダプタでは、コントロールレベルのスタイル設定(レンダリング後のマークアップ内でインラインのstyle要素としてレンダリングされるもの)は無視されます。代わりに、そのスタイル情報を分離して、CSSクラスとして指定することが要求されます。
Menuコントロールのコントロールアダプタに加えて、CSS Friendly Control Adaptersには、TreeView、GridView、DataList、 DetailsView、Login、CreateUserWizardなど、各種コントロールのアダプタが含まれています。この記事では、CSS Friendly Control Adaptersの概要を説明したうえで、Webサイトのマークアップを整理し、向上する方法について説明します。
コントロールアダプタの概要
ユーザーがASP.NET Webページにアクセスするとき、そのASP.NETページが最終的に目標とするのは、ページのコンテンツを、要求元のクライアントに返送できるマークアップとしてレンダリングすることです。ASP.NETページは、コントロールツリー(Webコントロールと静的なHTMLコンテンツ)の中を移動して、基本的に「自分自身をレンダリングせよ」という指示を出すことによって、レンダリングされたコンテンツを生成します。各コントロールは素直に自身をレンダリングし、レンダリング後のマークアップをページに返します。このようなレンダリング結果が寄せ集められて、要求元のクライアントに送り返されます。
ASP.NET 2.0 Webコントロールが自身をレンダリングする場合は、いったん処理を停止して、自分が使用できるアダプタがないかどうかを探します。コントロールアダプタは、ControlAdapterクラスから派生するクラスとして実装されており、BeginRender()、Render()、RenderChildren()、EndRender()など、レンダリング関連の重要なメソッドを備えています。レンダリングされるWebコントロールがコントロールアダプタを持たない場合は、レンダリングが内部で処理されます(つまり、外部のコントロールアダプタがレンダリングを処理するのではなく、既定のレンダリングロジックが使用されます)。コントロールアダプタを使用するかどうかは、ブラウザ定義ファイル内の設定によって決まります。このファイルには、どのWebコントロールがどのブラウザに対してコントロールアダプタを使用するかについての指示が含まれています。
ASP.NET Webアプリケーションでコントロールアダプタを使用するには、次の2つの手順を実行する必要があります。
- コントロールアダプタを作成(またはダウンロード)します。繰り返しになりますが、これは、直接的または間接的に
ControlAdapter基本クラスから派生するクラスに過ぎません。.NET Frameworkには、PageAdapter、WebControlAdapter、DataBoundControlAdapterなど、ControlAdapterクラスを展開したいくつかのクラスが既に含まれています(これらのクラスの多くは、コントロールアダプタ構築時の派生クラスです)。
- ブラウザ定義ファイルを構成または作成して、特定のWebコントロールおよび特定のユーザーエージェントに対してコントロールアダプタを使用することを指定します。ブラウザ定義ファイルの拡張子は「.browser」です。既定のブラウザ定義ファイルは、「%WINDIR%Microsoft.NETFrameworkv2.0.50727CONFIGBrowsers」にあります。この情報は、「App_Browsers」フォルダに.browserファイルを追加することによって、特定のWebアプリケーション用にカスタマイズできます。
この記事では、独自のコントロールアダプタは作成せずに、MicrosoftのCSS Friendly Control Adaptersをダウンロードして使用します。
ASP.NET Webコントロールの既定のマークアップの改良
カスケーディングスタイルシート(CSS)はWeb開発で使用される手法であり、特定のHTML要素、HTML要素のクラス、または特定のHTML要素タイプのすべてのインスタンスのプレゼンテーション設定を指定できます(すべての<div>要素の表示設定の指定など)。特定のHTML要素のプレゼンテーション設定を指定するには、要素のstyle属性を使用します。例えば、以下のマークアップでは、段落要素の背景色が緑色であることを指定しています。
<p style="background-color: Green;">
This has a green background!
</p>
しかしながら、理想的には、プレゼンテーション情報はCSSクラスを使って指定します。CSSクラスは、要素のクラスのプレゼンテーションスタイルを定義するものであり、<style>要素で、または、<link>要素を介してページに関連付けられる外部のCSSファイルを通じて定義されます。定義されたクラスは、要素のclass属性を通じて特定のHTML要素に関連付けられます。以下のマークアップでは、テキストを赤色の太字フォントで表示する.Warningという名前のクラスを追加しています。その後の<span>要素はクラスに関連付けられており、その結果、ブラウザを通じて表示される場合、赤色の太字のテキストとして表示されます。
<style type="text/css">
.Warning {
font-weight: bold;
color: red;
}
</style>
<span class="Warning">
Danger! Danger!
</span>
CSSクラスを使用する方法は、2つの理由で、インラインスタイルよりも優れています。第一に、プレゼンテーション情報を1箇所(<style>要素または個別のCSSファイル)に指定し、それをページ内の複数の場所に適用できるため、ページ全体のサイズが削減されます。インラインスタイルだけを使用するのであれば、プレゼンテーション情報を各コントロールに対して繰り返し適用しなければなりません。第二に、プレゼンテーション情報が集中化されていることにより、変更が必要な設定が1箇所しかないため、サイトの再設計やプレゼンテーションの変更も非常に簡単です。
残念ながら、ASP.NETのWebコントロールのスタイル関連のプロパティは、インラインスタイルとしてレンダリングされます。つまり、Label Webコントロールをページに追加し、そのForeColorプロパティを「Red」に設定した場合、Labelは次のようなマークアップをレンダリングします。
<span style="color: red;">Label Text</span>
しかし、一部のコントロールはインテリジェントであり、インラインスタイルではなくクラスを使用できます。例えば、Menuコントロールは、そのスタイル関連のプロパティをレンダリングするときに、インラインスタイルではなくクラスを使用します。さらに、すべてのWebコントロールはCssClassプロパティを備えており、その使用が推奨されます。しかし、インラインプロパティの使用は魅力的であり、多くの開発者が実際に使用しています。
また、多くのASP.NET Webコントロールは、レンダリングの位置を決める際に、標準のブロック要素(<div>など)とCSSを通じた位置指定を使用するのではなく、HTMLの<table>を使用しています。HTMLの<table>は、表形式のデータを格納するために設計されたものであり、レイアウト目的で使用するものではありません。しかし、MenusやTreeViewsをはじめとする組み込みのASP.NET Webコントロールでは、これがレイアウト目的で使用されています。
ただし、この動作は、コントロールアダプタでオーバーライドできます。コントロールアダプタはゼロから作成することもできますが、Microsoftが提供するASP.NET 2.0 CSS Friendly Control Adaptersをダウンロードすれば、最小限の手間でこれを自分のASP.NET Webアプリケーションにプラグインできます。コントロールアダプタをいったんインストールすると、インラインスタイル属性はレンダリングされず(CSSクラスを使用する必要があります)、MenuやTreeViewなどのコントロールの<table>方式レイアウトが、CSS方式に置換されます。
レイアウトに<table>を使用するか、CSSを使用するか
筆者の考えでは、
<table>は、GridViewにデータをリストするなど、表形式のデータを表示するために使用すべきであり、ページレイアウトの指定(例えば
<table>を使って3列×2行のレイアウトをページに配置するなど)に使用すべきではありません。理想的には、ページレイアウトにはCSS方式を使用すべきです。しかし、言行不一致ですが、筆者はしばしばレイアウト目的で
<table>を使用しています。一番の理由は、CSSで行うより
<table>でコンテンツをレイアウトする方法の方がよくわかっているからです。概して筆者は、
<table>方式でレイアウトするか、CSS方式でレイアウトするかという問題に、あまり興味がありません。理想的にはCSSを使用すべきですが、テーブルを完全に排し、すべてをCSSで実現するために必要な多くの知識を学ぶことに作業時間のかなりの部分(何だかんだといって最終段階における95%程度)を費やすほど熱狂的な支持者ではありません(幸い、CSS Friendly Control Adaptersなどのツールは、学習曲線を心配することなく、この移行を実現してくれます)。
この問題に関する筆者の無関心な態度とは対照的に、一部の人はどちらかの方法を強硬に主張しており、Web上にも、一方の方法を賞賛し、他方の方法を非難する記事やエッセイが多数存在します。書き方に工夫があって特に面白かったのは、Bill MerikallioとAdam Prattによる「
Why tables for layout is stupid」です。2つの方法のプラス面とマイナス面をバランスよく述べたものとしては、「
Tables vs. CSS: PROS and CONS」があります。
CSS Friendly Control AdaptersのダウンロードおよびASP.NET Webサイトへの統合
CSS Friendly Control Adaptersを使ってCSS対応のASP.NET Webサイトの構築を開始するには、最初に、Microsoftからファイルをダウンロードする必要があります。CSS Friendly Control AdaptersのWebサイトから最新バージョンを入手できます。CSS Friendly Control Adapters Version 1.0はここからダウンロードできます。
これらのコントロールアダプタは、VSIファイルとして提供されます。Visual Studio 2005またはVisual Web Developerがインストールされている場合は、ダウンロードしたVSIファイルをそのまま実行します。Visual Studioインストールに、新しいテンプレートが追加されます。CSS Friend Control Adapters Version 1.0には、新しいASP.NET CSS対応Webサイトを作成するためのテンプレートと、ASP.NET CSS Friendly Control Adaptersに関するチュートリアルが含まれています。チュートリアルには、必要なコアファイルと、すべてのアダプタの詳細な例が含まれています。一方、Webサイトテンプレートには、コアファイルと、いくつかの簡単なサンプルだけが含まれています。CSS Friendly Control Adaptersのサイトでは、サンプルをオンラインで見ることもできます。
これらのテンプレートを使用するには、Visual Studio 2005を起動し、新規Webサイトの作成を選択します。[New Web Site]ダイアログボックスには、[ASP.NET CSS Friendly Website]および[Tutorial on ASP.NET CSS Friendly Control Adapters]が表示されます。
いずれかのテンプレートを使用すると、Webサイトと、コントロールアダプタを使用するのに最低限必要なコアファイルが作成されます。具体的には、「App_Code」フォルダ内のコントロールアダプタクラスファイル、「App_Browsers」フォルダ内の「CSSFriendlyAdapters.browser」ブラウザ定義ファイル(すべてのブラウザに対して使用する各コントロールアダプタを指示)、および「JavaScript」フォルダ内のいくつかのJavaScriptファイル(主にMenuコントロールとTreeViewコントロールが、レンダリング出力にとって重要なクライアント側機能を提供するために使用)です。既存のASP.NET WebサイトをCSS対応のWebサイトに変換するには、この3つのフォルダをコピーします。
CSS Friendly Control Adaptersを使用した場合のMenuのレンダリング後のマークアップ
CSS Friendly Control Adaptersを使用した場合の出力の違いを確認するために、具体例を見てみましょう。トップレベルのメニュー項目と4つの子メニュー項目から成るメニューを表示するように定義された、Menu Webコントロールがあるものとします。このような構造は、以下の宣言的なマークアップを使って、Menu Webコントロール内で静的に定義できます。
<asp:Menu ID="ProductMenu" runat="server" Orientation="Horizontal">
<Items>
<asp:MenuItem Text="Products">
<asp:MenuItem Text="Beverages" />
<asp:MenuItem Text="Condiments" />
<asp:MenuItem Text="Confections" />
<asp:MenuItem Text="Dairy/Milk" />
</asp:MenuItem>
</Items>
</asp:Menu>
MenuのBackColorプロパティがYellowに設定され、子メニュー項目のBackColorがPinkに設定されていることにも注意してください。Menuコントロールの「既定」のレンダリングロジックを使用した場合、レンダリング結果のHTMLは次のようになります。
<table id="ProductMenu" class="ProductMenu_2"
cellpadding="0" cellspacing="0" border="0">
<tr>
<td onmouseover="Menu_HoverStatic(this)"
onmouseout="Menu_Unhover(this)"
onkeyup="Menu_Key(this)"
id="ProductMenun0">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="white-space:nowrap;">
<a class="ProductMenu_1"
href="javascript:__doPostBack(’ProductMenu’,
’Products’)">Products</a>
</td>
<td style="width:0;">
<img src="/CSSControlAdapters/WebResource.axd?d=chn_rjD
XviMiG52QjFyPeTtWQOV3vwPBosett_KzYtE1&t=632965002926288600"
alt="Expand Products"
style="border-style:none;vertical-align:middle;" />
</td>
</tr>
</table>
</td>
</tr>
</table>
<div id="ProductMenun0Items" class="ProductMenu_0 ProductMenu_3">
<table border="0" cellpadding="0" cellspacing="0">
<tr onmouseover="Menu_HoverDynamic(this)"
onmouseout="Menu_Unhover(this)" onkeyup="Menu_Key(this)"
id="ProductMenun1">
<td>
<table cellpadding="0" cellspacing="0"
border="0" width="100%">
<tr>
<td style="white-space:nowrap;width:100%;">
<a class="ProductMenu_1"
href="javascript:__doPostBack(’ProductMenu’,
’ProductsBeverages’)">Beverages</a>
</td>
</tr>
</table>
</td>
</tr>
<tr onmouseover="Menu_HoverDynamic(this)"
onmouseout="Menu_Unhover(this)"
onkeyup="Menu_Key(this)" id="ProductMenun2">
<td>
<table cellpadding="0" cellspacing="0"
border="0" width="100%">
<tr>
<td style="white-space:nowrap;width:100%;">
<a class="ProductMenu_1"
href="javascript:__doPostBack(’ProductMenu’,
’ProductsCondiments’)">Condiments</a>
</td>
</tr>
</table>
</td>
</tr>
<tr onmouseover="Menu_HoverDynamic(this)"
onmouseout="Menu_Unhover(this)"
onkeyup="Menu_Key(this)" id="ProductMenun3">
<td>
<table cellpadding="0" cellspacing="0"
border="0" width="100%">
<tr>
<td style="white-space:nowrap;width:100%;">
<a class="ProductMenu_1"
href="javascript:__doPostBack(’ProductMenu’,
’ProductsConfections’)">Confections</a>
</td>
</tr>
</table>
</td>
</tr>
<tr onmouseover="Menu_HoverDynamic(this)"
onmouseout="Menu_Unhover(this)"
onkeyup="Menu_Key(this)" id="ProductMenun4">
<td>
<table cellpadding="0" cellspacing="0"
border="0" width="100%">
<tr>
<td style="white-space:nowrap;width:100%;">
<a class="ProductMenu_1"
href="javascript:__doPostBack(’ProductMenu’,
’ProductsDairy/Milk’)">Dairy/Milk</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<div class="ProductMenu_0" id="ProductMenun0ItemsUp"
onmouseover="PopOut_Up(this)"
onmouseout="PopOut_Stop(this)" style="text-align:center;">
<img src="/CSSControlAdapters/WebResource.axd?d=qidxSK8lNw1v6pL8
IQZxzZrT3SMuvVDHqZfjfzhu6MM1&t=632965002926288600"
alt="Scroll up" />
</div>
<div class="ProductMenu_0" id="ProductMenun0ItemsDn"
onmouseover="PopOut_Down(this)"
onmouseout="PopOut_Stop(this)" style="text-align:center;">
<img src="/CSSControlAdapters/WebResource.axd?d=uuL5BWm1omFEgQu
N_dcSXY3whrEsc7zS_QoWROKGtMo1&t=632965002926288600"
alt="Scroll down" />
</div>
</div>
ご覧の通り、このマークアップでは、各メニュー項目に対して<table>要素を使用しています。ここには示されていませんが、子メニュー項目の表示と非表示を処理する外部JavaScriptファイルへの参照があります。
CSS Friendly Control Adaptersをプラグインすることによって、レンダリング後のマークアップは、理解しやすいものに変わります。
<div class="AspNet-Menu-Horizontal" id="ProductMenu">
<ul class="AspNet-Menu">
<li class="AspNet-Menu-WithChildren">
<a href="javascript:__doPostBack(’ProductMenu’,
’bProducts’)" class="AspNet-Menu-Link">Products</a>
<ul>
<li class="AspNet-Menu-Leaf">
<a href="javascript:__doPostBack(’ProductMenu’,
’bProductsBeverages’)"
class="AspNet-Menu-Link">Beverages</a>
</li>
<li class="AspNet-Menu-Leaf">
<a href="javascript:__doPostBack(’ProductMenu’,
’bProductsCondiments’)"
class="AspNet-Menu-Link">Condiments</a>
</li>
<li class="AspNet-Menu-Leaf">
<a href="javascript:__doPostBack(’ProductMenu’,
’bProductsConfections’)"
class="AspNet-Menu-Link">Confections</a>
</li>
<li class="AspNet-Menu-Leaf">
<a href="javascript:__doPostBack(’ProductMenu’,
’bProductsDairyMilk’)"
class="AspNet-Menu-Link">Dairy/Milk</a>
</li>
</ul>
</li>
</ul>
</div>
各メニュー項目には、適切な名前のCSSクラス(AspNet-Menu、AspNet-Menu-WithChildren、AspNet-Menu-Leafなど)が割り当てられます。これらのクラスのプレゼンテーション設定は、ページの<style>要素、または理想的には個別のCSSファイルで指定されます。CSS Friendly Control Adaptersを使ってレンダリングされるコントロールの、外観のカスタマイズの詳細については、「CSS Friendly Control Adapters White Paper」を参照してください。
まとめ
ASP.NETでは、コントロールやページを非常に柔軟にレンダリングできます。コントロールは、自動的にアダプティブレンダリングを使用して、アクセスしてきたユーザーがアップレベルのブラウザを使用しているのか、ダウンレベルのブラウザを使用しているのかに基づいて、適切なマークアップを発行します。レンダリングは、必要に応じて、コントロールアダプタを通じて完全にカスタマイズできます。この記事では、コントロールアダプタの基本について説明し、Microsoftが無料で提供するASP.NET 2.0 CSS Friendly Control Adatpersについて説明しました。これらのコントロールアダプタをWebサイトにプラグインすると、Menus、TreeViews、GridViews、DetailsViewsなどのコントロールが、CSS方式のレイアウトを使ってレンダリングを実行できるようになります。さらに、これらのコントロールアダプタにより、開発者はCSSクラスを使ってプレゼンテーション設定を指定するようになるので、コントロールレベルのスタイル設定が使われなくなり、レンダリング後のマークアップからインラインスタイルが排除されます。
それでは、ハッピープログラミング!
参考資料
- Architectural Overview of Adaptive Control Behavior(MSDNライブラリ)
- ASP.NET 2.0 CSS Friendly Control Adapters Homepage
- ASP.NET 2.0 CSS Friendly Control Adapters: The White Paper
- Control Adapters(MSDN Magazineの掲載記事)