by WebSurfer
2010年11月13日 16:25
GridView や ListView でページングを行っている場合、プログラムで表示するページを指定したいケースがあると思います。
GridView の場合は簡単で、PageIndex プロパティを用いてページの指定ができます。
ところが、ListView には、GridView の PageIndex プロパティのような、表示するページを指定できるプロパティがありません。
これは、GirdView にはページング機能が統合されているのに対し、ListView そのものにはページング機能は実装されていないからです。
通常、ListView でページングを行うには、DataPager コントロールを用います。これを利用すれば、GridView.PageIndex プロパティと同様に、ListView でもプログラムで表示するページを指定することができます。
以下に、ListView において、プログラムで表示するページを指定する例を書いておきます。
まず、DataPager フィールドに NumericPagerField クラスを実装する必要があります。その HandleEvent メソッドを利用します。
HandleEvent メソッドは、引数の CommandEventArgs オブジェクトの CommandName プロパティの値を見て、それにページ番号が指定されている場合、対応するページに移動させます。
上の画像のサンプルのコードは以下の通りです。TextBox にページ番号を入力してボタンをクリックすると、指定したページに飛びます。
<%@ 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 Button1_Click(object sender, EventArgs e)
{
int page = 0;
if (Int32.TryParse(TextBox1.Text, out page))
{
page -= 1;
}
CommandEventArgs commandEventArgs =
new CommandEventArgs(page.ToString(), "");
DataPager dp =
(DataPager)ListView1.FindControl("DataPager1");
NumericPagerField numericPagerField =
dp.Fields[2] as NumericPagerField;
if (numericPagerField != null)
{
numericPagerField.HandleEvent(commandEventArgs);
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<style type="text/css">
table.style1
{
border-style: solid;
border-width: 2px;
border-color: Black;
text-align: center;
border-collapse: collapse;
}
table.style1 th
{
border-style: solid;
border-width: 2px 1px 2px 1px;
border-color: Black;
background-color: #6699FF;
color: #FFFFFF;
}
table.style1 td
{
border-style: solid;
border-width: 1px;
border-color: Black;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
Page:
<asp:TextBox ID="TextBox1" runat="server">
</asp:TextBox>
<asp:Button ID="Button1"
runat="server"
Text="Go"
OnClick="Button1_Click" />
<hr />
<asp:ObjectDataSource ID="ObjectDataSource1"
runat="server"
EnablePaging="True"
SelectCountMethod="GetNumberOfMessages"
SelectMethod="GetDataByIndex"
TypeName="OrdersDataSetTableAdapters.OrdersTableAdapter">
<SelectParameters>
<asp:Parameter Name="employeeid" DefaultValue="-1" />
</SelectParameters>
</asp:ObjectDataSource>
<asp:ListView ID="ListView1"
runat="server"
DataKeyNames="OrderID"
DataSourceID="ObjectDataSource1">
<LayoutTemplate>
<table runat="server">
<tr runat="server">
<td runat="server">
<table ID="itemPlaceholderContainer"
runat="server"
class="style1">
<tr runat="server">
<th id="Th1" runat="server">
OrderID</th>
<th id="Th2" runat="server">
CustomerID</th>
<th id="Th3" runat="server">
OrderDate</th>
<th id="Th4" runat="server">
Freight</th>
<th id="Th5" runat="server">
ShipCountry</th>
</tr>
<tr ID="itemPlaceholder"
runat="server">
</tr>
</table>
</td>
</tr>
<tr runat="server">
<td runat="server">
<asp:DataPager ID="DataPager1"
runat="server"
PageSize="10">
<Fields>
<asp:TemplatePagerField>
<PagerTemplate>
<b>
Page
<asp:Label runat="server"
ID="CurrentPageLabel"
Text="<%# Container.TotalRowCount>0 ? (Container.StartRowIndex / Container.PageSize) + 1 : 0 %>" />
of
<asp:Label runat="server"
ID="TotalPagesLabel"
Text="<%# Math.Ceiling ((double)Container.TotalRowCount / Container.PageSize) %>" />
(
<asp:Label runat="server"
ID="TotalItemsLabel"
Text="<%# Container.TotalRowCount%>" />
records)
<br />
</b>
</PagerTemplate>
</asp:TemplatePagerField>
<asp:NextPreviousPagerField
ButtonType="Button"
ShowFirstPageButton="true"
ShowNextPageButton="false"
ShowPreviousPageButton="false" />
<asp:NumericPagerField
PreviousPageText="< Prev 5"
NextPageText="Next 5 >"
ButtonCount="5" />
<asp:NextPreviousPagerField
ButtonType="Button"
ShowLastPageButton="true"
ShowNextPageButton="false"
ShowPreviousPageButton="false" />
</Fields>
</asp:DataPager>
</td>
</tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<asp:Label ID="OrderIDLabel"
runat="server"
Text='<%# Eval("OrderID") %>' />
</td>
<td>
<asp:Label ID="CustomerIDLabel"
runat="server"
Text='<%# Eval("CustomerID") %>' />
</td>
<td>
<asp:Label ID="OrderDateLabel"
runat="server"
Text='<%# Eval("OrderDate", "{0:yyyy/MM/dd}") %>' />
</td>
<td>
<asp:Label ID="FreightLabel"
runat="server"
Text='<%# Eval("Freight", "${0:N2}") %>' />
</td>
<td>
<asp:Label ID="ShipCountryLabel"
runat="server"
Text='<%# Eval("ShipCountry") %>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
</div>
</form>
</body>
</html>
なお、上記は Resetting the Page Index in a ListView を参考にしています(参考というよりほぼ丸写しですが)。
by WebSurfer
2010年10月30日 14:34
先のポスト DataPager のバグその1 に続いて、再び DataPager のバグの話です。なお、このバグも .NET 4 では修正されており、.NET 3.5 のみの問題です。
ListView の中に配置した DataPager の設定にある条件が重なると Stack Overflow を起こします。条件というのは以下の 5 つです。
-
ListView の LayoutTemplate の中に DataPager を配置
-
ListView に EmptyDataTemplate を配置
-
ListView にバインドされるデータの件数が 0
-
DataPager.QueryStringField を設定(例: QueryStringField="pageNumber")
-
そのページを、クエリ文字列 ?pageNumber=1 として要求
残念ながら原因は分かりません。(涙)
でも、回避策は簡単で、DataPager を ListView の外に出してしまえばいいのです。件数 0 の場合は ListView の外に DataPager を配置しても、それが表示されることはないです。
または、当然ながら、DataPager.QueryStringField の設定を止めても回避できます。DataPager.QueryStringField を使う理由が、GridView.PageIndex プロパティのようにコードでページを指定したいということであれば、別の手段があります。
その「別の手段」の具体的な方法は、Resetting the Page Index in a ListView が参考になると思います。
by WebSurfer
2010年10月29日 23:18
.NET 3.5 で ListView コントロールと共に導入された DataPager コントロールには、QueryStringField プロパティ周りにバグがあります。
QueryStringField プロパティというのは、クエリ文字列を利用してページの移動先を指定し、ページの移動に HTTP GET コマンド(デフォルトでは POST)を使用できるようにするためのものです。
例えば、QueryStringField="pageNumber" と設定した場合、ページャーをクリックして指定した移動先のページ番号が 5 であったとすると、URL にクエリ文字列 ?pageNumber=5 を追加して HTTP GET コマンドでそのページを要求するようになります。
MSDN ライブラリ「DataPager.QueryStringField プロパティ」によると "検索エンジンですべてのデータ ページにインデックスが付けられるようにするには、このプロパティを設定すると便利です。" とのことです。
ところが、このページを要求する際に、クエリ文字列のキーに null が含まれる場合(例えば、?pageNumber=5& とすると、& の後に null のキーがあると判断されるようです)、NullReferenceException がスローされるという問題があります。
ページ内でページャーをクリックしているだけなら、自動的に ?pageNumber=x (x は要求するページ番号)というクエリ文字列が URL に追加されて要求がかかるので間違いなく動作しますが、別のページでクエリ文字列を組み立ててそのページを要求する場合、間違って ?pageNumber=x& などとすると例外がスローされます
この問題は、2008 年 7 月に Connect に報告されています。
この報告によると、受信したクエリ文字列のキーに null が含まれていると、ライブラリ内のメソッドの以下の部分で key(String オブジェクト)が null になって NullReferenceException がスローされるということだそうです。これはバグとして次期バージョンで修正されるという話になっていました。
foreach (string key in request.QueryString.AllKeys)
{
if (... && !key.Equals(queryStringField,
StringComparison.OrdinalIgnoreCase))
今日、このページをのぞいてみたら、すでに修正済みというようなことが書いてあります。ということは、少なくとも .NET 4 では修正されているはずです。
ということで、早速試してみました。結果は:
-
.NET 3.5 SP1: ダメでした。NullReferenceException がスローされます。
-
.NET 4: 例外はスローされなかったので、バグは修正されているようです。
というわけで、ASP.NET 3.5 では回避策がまだ必要なようです。上に紹介した Connect のページに回避策も提案されていますので、そちらを参照してください。