asp:ListViewで2つのアイテムテンプレートを定義することは可能ですか?
はい、可能です。 この投稿を読んでください(コードが適切にフォーマットされたらここにコードを投稿するので、紛失しないようにしてください)。
クレジット:
Dino Esposito は IDesign アーキテクトであり、プログラミング ASP.NET 3.5 コア リファレンスの著者です。イタリアを拠点とする Dino は、世界中の業界イベントで頻繁に講演を行っています。彼に連絡するには、cutting@microsoft.com で連絡を取るか、 weblogs.asp.net /desposで彼のブログに参加してください。
複数のアイテム テンプレート
ListView コントロールは、データ ソースをループして次のアルゴリズムを適用することで、マークアップを生成します。最初に、項目セパレーターが必要かどうかをチェックします。その場合、テンプレートをインスタンス化し、データ項目オブジェクトを作成します。データ アイテム オブジェクトは、アイテム テンプレートのコンテナーであり、ビュー内のアイテムのインデックスとバインドされたデータ ソースに関する情報を保持します。アイテム テンプレートがインスタンス化されると、ItemCreated イベントが発生します。次のステップはデータバインディングです。これが完了すると、ItemDataBound イベントが発生します。
ご覧のとおり、各アイテムのテンプレートをプログラムで変更できる、処理できるパブリック イベントはありません。Init または Load ページ イベントでテンプレートを変更できますが、それはバインドされたすべてのアイテムに適用されます。ItemCreated を処理し、そこで ItemTemplate プロパティを設定すると、変更は次のアイテムに影響しますが、現在処理中のアイテムには影響しません。ItemCreating イベントが必要ですが、そのようなイベントは ListView コントロールによって発生しません。解決策は、図 6 に示すように、独自の ListView コントロールを作成することです。
図 6 ItemCreating イベントの発生
namespace Samples.Controls
{
public class ListViewItemCreatingEventArgs : EventArgs
{
private int _dataItemIndex;
private int _displayIndex;
public ListViewItemCreatingEventArgs(int dataItemIndex,
int displayIndex) {
_dataItemIndex = dataItemIndex;
_displayIndex = displayIndex;
}
public int DisplayIndex {
get { return _displayIndex; }
set { _displayIndex = value; }
}
public int DataItemIndex {
get { return _dataItemIndex; }
set { _dataItemIndex = value; }
}
}
public class ListView : System.Web.UI.WebControls.ListView
{
public event EventHandler<ListViewItemCreatingEventArgs>
ItemCreating;
protected override ListViewDataItem CreateDataItem(int
dataItemIndex, int displayIndex) {
// Fire a NEW event: ItemCreating
if (ItemCreating != null)
ItemCreating(this, new ListViewItemCreatingEventArgs
(dataItemIndex, displayIndex));
// Call the base method
return base.CreateDataItem(_dataItemIndex, displayIndex);
}
}
}
CreateDataItem メソッドをオーバーライドすることで、項目テンプレートがインスタンス化される直前にコードを実行できる可能性があります。CreateDataItem メソッドは、ListView クラスで保護および仮想として宣言されています。図 6 からわかるように、メソッドのオーバーライドは非常に単純です。最初にカスタム ItemCreating イベントを発生させてから、基本メソッドを呼び出して続行します。
ItemCreating イベントは、いくつかの整数 (データ ソース内のアイテムの絶対インデックスとページ固有のインデックス) をユーザー コードに返します。たとえば、ページ サイズが 10 の場合、ListView が 2 番目のページの最初のアイテムのレンダリングに取り組んでいる場合、dataItemIndex には 11 個のアイテムが含まれ、displayIndex には 1 個のアイテムが含まれます。新しい ItemCreating イベントを使用するには、次のコードでわかるように、カスタム ListView コントロールでメソッドとハンドラーを宣言するだけです。
<x:ListView runat="server" ID="ListView1"
ItemPlaceholderID="itemPlaceholder"
DataSourceID="ObjectDataSource1"
OnItemCreating="ListView1_ItemCreating">
<LayoutTemplate>
<div>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</div>
</LayoutTemplate>
</x:ListView>
コードでは、次のようにイベントを処理できます。
void ListView1_ItemCreating(
object sender, ListViewItemCreatingEventArgs e)
{
string url = "standard.ascx";
if (e.DisplayIndex % DataPager1.PageSize == 0)
url = "firstItem.ascx";
ListView1.ItemTemplate = Page.LoadTemplate(url);
}
ここでは、2 つの異なるユーザー コントロールを使用して、データ項目をレンダリングします。特定のユーザー コントロールは、表示インデックスによって決定されます。最初の項目を除いて、すべての項目が同じテンプレートを共有します。図 7 は、動作中のページを示しています。
一般的な現実世界のページの複雑さを考えると、このソリューションは単純すぎるように見えます。多くの場合、表示するコンテンツに基づいて異なるテンプレートを使用する必要があります。カスタム ListView コントロールをさらに拡張して、データ バインディング プロセス内で項目テンプレートを変更する必要があります。図 8 のコードを見てください。
図 8 コンテンツに基づくテンプレートの選択
namespace Samples.Controls
{
public class ListViewItemCreatingEventArgs : EventArgs
{
private int _dataItemIndex;
private int _displayIndex;
public ListViewItemCreatingEventArgs(int dataItemIndex,
int displayIndex) {
_dataItemIndex = dataItemIndex;
_displayIndex = displayIndex;
}
public int DisplayIndex {
get { return _displayIndex; }
set { _displayIndex = value; }
}
public int DataItemIndex {
get { return _dataItemIndex; }
set { _dataItemIndex = value; }
}
}
public class ListView : System.Web.UI.WebControls.ListView
{
public event EventHandler<ListViewItemCreatingEventArgs>
ItemCreating;
private int _displayIndex;
private bool _shouldInstantiate = false;
protected override void InstantiateItemTemplate(Control container,
int displayIndex) {
if (_shouldInstantiate) {
base.InstantiateItemTemplate(container, displayIndex);
_shouldInstantiate = false;
}
}
protected override ListViewDataItem CreateDataItem(int
dataItemIndex, int displayIndex) {
// Fire a NEW event: ItemCreating
if (ItemCreating != null)
ItemCreating(this, new
ListViewItemCreatingEventArgs(dataItemIndex,
displayIndex));
// Cache for later
_displayIndex = displayIndex;
// Call the base method
return base.CreateDataItem(_dataItemIndex, displayIndex);
}
protected override void OnItemCreated(ListViewItemEventArgs e) {
base.OnItemCreated(e);
// You can proceed with template instantiation now
_shouldInstantiate = true;
InstantiateItemTemplate(e.Item, _displayIndex);
}
}
}
CreateDataItem メソッドは、ItemCreating イベントを発生させ、後で使用するために表示インデックスをキャッシュします。さらに、InstantiateItemTemplate メソッドをオーバーライドして、実際のテンプレートのインスタンス化を遅らせます。そのために、プライベート ブール フラグが使用されます。前述のように、ListView は項目テンプレートをインスタンス化した後にデータ バインディング プロセスを開始します。
ただし、図 8 のコードに示されている実装では、ItemCreated イベントが発生するまでアイテム テンプレートは実際にはインスタンス化されません。ItemCreated イベントが発生すると、データ項目オブジェクトは DataItem プロパティを通じて ListView 項目コンテナーにバインドされます。コードで ItemCreated イベントを処理することにより、次のように、バインドされたデータ項目に基づいて使用する項目テンプレートを決定できます。
protected override void OnItemCreated(ListViewItemEventArgs e)
{
base.OnItemCreated(e);
_shouldInstantiate = true;
InstantiateItemTemplate(e.Item, _displayIndex);
}
この場合、ベース メソッドは ItemCreated イベントをページに対して発生させます。その後、カスタム ListView コントロールは Boolean フラグをリセットし、メソッドを呼び出して項目テンプレートをインスタンス化します。最終的に、項目テンプレートは組み込みの ListView コントロールよりも少し遅れてインスタンス化されますが、バインドされたデータ項目の内容を確認した後、ItemCreated イベント ハンドラーでプログラムによって各項目の ItemTemplate プロパティを設定できます (図を参照)。 9)。図 10 は、男性用に青色のテンプレートが使用され、女性用にピンク色のテンプレートが使用されているサンプル ページを示しています。
図 9 アイテム テンプレートの設定
void ListView1_ItemCreated(object sender, ListViewItemEventArgs e)
{
// Grab a reference to the data item
ListViewDataItem currentItem = (e.Item as ListViewDataItem);
Employee emp = (Employee) currentItem.DataItem;
if (emp == null)
return;
// Apply your logic here
string titleOfCourtesy = emp.TitleOfCourtesy.ToLower();
string url = "forgentlemen.ascx";
if (titleOfCourtesy == "ms." || titleOfCourtesy == "mrs.")
url = "forladies.ascx";
// Set the item template to use
Samples.ListView ctl = (sender as Samples.Controls.ListView);
ctl.ItemTemplate = Page.LoadTemplate(url);
}