by WebSurfer
13. November 2010 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
7. November 2010 19:04
GirdView や ListView で、ある列の合計金額を計算して、フッターなどに表示したいというケースが時々あります。備忘録として、その例を書いておきます。
GridView は「行」(GridViewRow クラス)で構成されているのに対して、ListView は「項目」(ListViewItem クラス)で構成されているという違いがありますが、基本的な方法は行/項目にデータがバインドされるときのイベントを利用して、値を取得して合計していくという操作は同じだと思います。
GridView, ListView どちらの場合も、データソースコントロールが取得してバインドするデータが DataTable の場合(デフォルト)は、DataItem プロパティを使って DataRowView を取得できますので、それから各行/項目の値を取得するのがよさそうです。
合計した結果を書き込むところが、ちょっと違います。
GridView では、フッターでも RowDataBound イベントが発生します。そのイベントハンドラでデータ行かフッター行かが判定でき、フッター行の場合に合計をフッターに書き込むことができます。
ShowFooter="True" として、その中の TableCell の Text プロパティに書き込む例は以下の通りです。
decimal total = 0m;
protected void GridView1_RowDataBound(object sender,
GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView drv = (DataRowView)e.Row.DataItem;
total = total + (decimal)drv["Freight"];
}
else if (e.Row.RowType == DataControlRowType.Footer)
{
e.Row.Cells[1].Text = "Freight Total";
e.Row.Cells[2].Text = String.Format("${0:N2}", total);
}
}
ListView では、合計の取得は ItemDataBound イベントハンドラで可能ですが、GridView の時のようにフッター行に合計結果を書き込むことはできません。
LayoutTemplate にフッターの行を追加して Label を配置し、その Text プロパティに書き込むことになります。そのタイミングは、ListView.DataBound イベントがよさそうです。
上の画像を出力した ListView のコードを以下にアップしておきます。
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
decimal total = 0m;
protected void ListView1_ItemDataBound(object sender,
ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
ListViewDataItem lvdi = (ListViewDataItem)e.Item;
DataRowView drv = (DataRowView)lvdi.DataItem;
total = total + (decimal)drv["Freight"];
}
}
protected void ListView1_DataBound(object sender, EventArgs e)
{
Label label = (Label)ListView1.FindControl("totalLabel");
label.Text = String.Format("${0:N2}", total);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Show Total in ListView</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;
}
.footer
{
background-color: #CCFFFF;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<h3>Alfreds Futterkiste</h3>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand="SELECT [OrderID], [OrderDate], [Freight]
FROM [Orders]
WHERE [CustomerID]='ALFKI'">
</asp:SqlDataSource>
<asp:ListView ID="ListView1"
runat="server"
DataKeyNames="OrderID"
DataSourceID="SqlDataSource1"
EnableModelValidation="True"
OnItemDataBound="ListView1_ItemDataBound"
OnDataBound="ListView1_DataBound">
<ItemTemplate>
<tr>
<td>
<asp:Label ID="OrderIDLabel"
runat="server"
Text='<%# Eval("OrderID") %>' />
</td>
<td>
<asp:Label ID="OrderDateLabel"
runat="server"
Text='<%# Eval("OrderDate", "{0:yyyy/MM/dd}") %>' />
</td>
<td style="text-align: right;">
<asp:Label ID="FreightLabel"
runat="server"
Text='<%# Eval("Freight", "${0:N2}") %>' />
</td>
</tr>
</ItemTemplate>
<LayoutTemplate>
<table ID="itemPlaceholderContainer"
runat="server"
class="style1">
<tr runat="server">
<th runat="server">
OrderID</th>
<th runat="server">
OrderDate</th>
<th runat="server">
Freight</th>
</tr>
<tr ID="itemPlaceholder" runat="server">
</tr>
<tr class="footer">
<td></td>
<td>Freight Total</td>
<td style="text-align: right;">
<asp:Label ID="totalLabel" runat="server" /></td>
</tr>
</table>
</LayoutTemplate>
</asp:ListView>
</div>
</form>
</body>
</html>
by WebSurfer
31. October 2010 19:32
ListView の InsertItemTemplate に DropDownList を配置し、DropDownList.SelectedValue を DB に挿入するにはどのようにすればよいかという話です。ただし、.NET 4 では問題なく、.NET 3.5 に限った話です。
何故かは不明ですが、InsertItemTemplate に DropDownList を配置した場合は、DropDownList の SelectedValue プロパティを以下のように設定してもうまくいきません。「Eval()、XPath()、および Bind() のようなデータバインド メソッドは、データバインドされたコントロールのコンテキストでのみ使用することができます。」というエラーになります。
<InsertItemTemplate>
<tr style="">
・・・中略・・・
<td>
<asp:SqlDataSource ID="SqlDataSource1" ・・・
・・・中略・・・
<asp:DropDownList ID="DropDownList1"
runat="server"
DataSourceID="SqlDataSource1"
DataTextField="CategoryName"
DataValueField="CategoryName"
AppendDataBoundItems="True"
SelectedValue='<%# Bind("memo") %>'>
<asp:ListItem Value="">空白</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
</InsertItemTemplate>
なお、上記と同様な形で EditItemTemplate に配置した場合は問題なく、DB は SelectedValue でちゃんと更新されます。
この問題の回避策は、以下のように SqlDataSource.Inserting イベントハンドラで処置することです。
protected void SqlDataSource1_Inserting(object sender,
SqlDataSourceCommandEventArgs e)
{
DropDownList ddl =
(DropDownList)ListView1.InsertItem.FindControl("DropDownList1");
e.Command.Parameters["@memo"].Value = ddl.SelectedValue;
}
何故、InsertItemTemplate に DropDownList を配置した場合ダメなのかが分からなかったし、もっとスマートな解決策があるのではないか(ひょっとして、とんでもなく間抜けなのことしているのではないか)ということが気になっていましたが、どうやら .NET 3.5 のバグらしいということで、安心(?)しています。(笑)