by WebSurfer
17. July 2011 14:29
GridView に表示するレコードを DropDownList を使って絞り込むサンプルです。
以前、実験室 SelectCommand の切り替え にも、2 つの DropDownList を使って GridView に表示されるレコードを絞り込むサンプルを書きましたが、それは SqlDataSource の SelectCommand(正確には SELECT クエリの WHERE 句)を書き換えていて、そのためのコードを書かなければならないのが面倒で、かつ、あまりスマートな方法とはいえません。
今回、ここに書いた方法は、WHERE 句は以下のように固定しておいて、DropDownList の SelectedValue プロパティの値によって全件抽出と条件抽出の両方に対応できるようにしたものです。
WHERE (@CustomerID='ALL' OR o.CustomerID=@CustomerID)
AND (@EmployeeID=0 OR e.EmployeeID=@EmployeeID)
上記で、@CustomerID='ALL' と @EmployeeID=0 が全件抽出の条件で、DropDownList.SelectedValue がそれぞれ "ALL" と "0" の時にその条件に一致させるようにします。
そのために、DropDownList の AppendDataBoundItems プロパティを true に設定した上で、全件抽出条件のための ListItem を追加します。なお、EmployeeID については、データ型が int なので、ListItem は以下のように設定しなければなりません。DropDownList.SelectedValue プロパティは string 型を返しますが、データベースプロバイダが型変換してくれます。
<asp:ListItem Value="0">ALL</asp:ListItem>
これによって C# のコードは一行も書かずに、すべてウィザードベースで作ることができます。上の画像の aspx ページのコードは以下の通りです。データベースには Northwind サンプルデータベースの Orders, Customers, Employees テーブルを使っています。
<%@ 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">
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:SqlDataSource ID="SqlDataSource1"
runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand=
"SELECT DISTINCT o.CustomerID, c.CompanyName
FROM Orders AS o
INNER JOIN Customers AS c
ON o.CustomerID = c.CustomerID">
</asp:SqlDataSource>
Customer:
<asp:DropDownList ID="DropDownList1"
runat="server"
AppendDataBoundItems="True"
DataSourceID="SqlDataSource1"
DataTextField="CompanyName"
DataValueField="CustomerID"
AutoPostBack="True">
<asp:ListItem>ALL</asp:ListItem>
</asp:DropDownList>
<br />
<asp:SqlDataSource ID="SqlDataSource2"
runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand=
"SELECT DISTINCT o.EmployeeID,
e.FirstName + ' ' + e.LastName AS Name
FROM Orders AS o
INNER JOIN Employees AS e
ON o.EmployeeID = e.EmployeeID">
</asp:SqlDataSource>
Employee:
<asp:DropDownList ID="DropDownList2"
runat="server"
AppendDataBoundItems="True"
DataSourceID="SqlDataSource2"
DataTextField="Name"
DataValueField="EmployeeID"
AutoPostBack="True">
<asp:ListItem Value="0">ALL</asp:ListItem>
</asp:DropDownList>
<asp:SqlDataSource ID="SqlDataSource3"
runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand=
"SELECT o.OrderID, c.CompanyName,
e.FirstName + ' ' + e.LastName AS Name,
o.ShippedDate, o.Freight
FROM Orders AS o
INNER JOIN Customers AS c
ON o.CustomerID = c.CustomerID
INNER JOIN Employees AS e
ON o.EmployeeID = e.EmployeeID
WHERE (@CustomerID='ALL' OR o.CustomerID=@CustomerID)
AND (@EmployeeID=0 OR e.EmployeeID=@EmployeeID)
ORDER BY o.OrderID">
<SelectParameters>
<asp:ControlParameter ControlID="DropDownList1"
Name="CustomerID"
PropertyName="SelectedValue" />
<asp:ControlParameter ControlID="DropDownList2"
Name="EmployeeID"
PropertyName="SelectedValue" />
</SelectParameters>
</asp:SqlDataSource>
<asp:GridView ID="GridView1"
runat="server"
AllowPaging="True"
AutoGenerateColumns="False"
DataKeyNames="OrderID"
DataSourceID="SqlDataSource3">
<Columns>
<asp:BoundField
DataField="OrderID"
HeaderText="OrderID"
InsertVisible="False"
ReadOnly="True"
SortExpression="OrderID" />
<asp:BoundField
DataField="CompanyName"
HeaderText="Customer"
SortExpression="CompanyName" />
<asp:BoundField
DataField="Name"
HeaderText="Employee"
ReadOnly="True"
SortExpression="Name" />
<asp:BoundField
DataField="ShippedDate"
HeaderText="Shipped Date"
SortExpression="ShippedDate"
DataFormatString="{0:yyyy/M/d}" >
<ItemStyle HorizontalAlign="Center" />
</asp:BoundField>
<asp:BoundField
DataField="Freight"
HeaderText="Freight"
SortExpression="Freight"
DataFormatString="${0:N2}" >
<ItemStyle HorizontalAlign="Right" />
</asp:BoundField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
by WebSurfer
30. April 2011 12:52
2017/8/16 注記追加
Windows 10 IE11 では Quirks モード(IE5 相当)にしても expression 関数が働かないようで、テーブルのヘッダ・列は固定されませんのでご注意ください。この記事はもう意味がないかもしれませんが、せっかく書いたので残しておきます。
以前の記事 table のヘッダ、列を固定 では ListView を使ったときの例を書きましたが、ここでは GridView を使ったときの例を紹介します。 (2013/2/7 追記:このページに紹介したのは IE の互換モード(Quirks モード)専用ですが、GridView のヘッダ、列を固定(その 2)のページに IE7+ (標準モードの), Firefox, Chrome, Safari, Opera コンパチのものを書きましたので、そちらも見てください)
GridView も html にレンダリングされると table, tr, th, td などの要素になりますが、それらは自動的に(勝手に)生成され、かつ、直接スタイルを適用できないので結構面倒です。
今回は先の ListView を使った場合の例よりヘッダーを少し複雑にしました(一部のセルの rowspan を 2 にしてみました)ので、ますます面倒になっています。(笑)
という訳で、実際にはあまり役に立たないと思いますが、せっかく苦労して作ったので書いておくことにしました。
上の画像を出力したコードは下記の通りです。実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。
<%@ 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">
DataTable CreateDataTable()
{
DataTable dt = new DataTable();
DataRow dr;
dt.Columns.Add(new DataColumn("Code", typeof(Int32)));
dt.Columns.Add(new DataColumn("Name", typeof(string)));
dt.Columns.Add(new DataColumn("Price", typeof(Int32)));
dt.Columns.Add(new DataColumn("Qty", typeof(Int32)));
dt.Columns.Add(new DataColumn("Amount", typeof(Int32)));
dt.Columns.Add(new DataColumn("Remarks", typeof(string)));
for (int i = 0; i < 50; i++)
{
dr = dt.NewRow();
dr["Code"] = i;
dr["Name"] = "Item " + i.ToString();
dr["Price"] = 123000 * (i + 1);
dr["Qty"] = i + 1;
dr["Amount"] = 123000 * (i + 1) * (i + 1);
dr["Remarks"] = "Remarks " + i.ToString();
dt.Rows.Add(dr);
}
return dt;
}
void Page_Load(Object sender, EventArgs e)
{
if (!IsPostBack)
{
GridView1.DataSource = CreateDataTable();
GridView1.DataBind();
}
}
// RowDataBound で細工すると PostBack で表示が崩れるので
// 注意(ViewState との関係が崩れるらしい)
protected void GridView1_RowCreated(object sender,
GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
System.Collections.Generic.List<TableCell> cells =
new System.Collections.Generic.List<TableCell>();
foreach (TableCell cell in e.Row.Cells)
{
cells.Add(cell);
}
GridViewRow row1 =
new GridViewRow(
-1,
-1,
DataControlRowType.Header,
DataControlRowState.Normal);
cells[0].RowSpan = 2;
cells[1].RowSpan = 2;
cells[5].RowSpan = 2;
TableHeaderCell headerCell = new TableHeaderCell();
headerCell.ColumnSpan = 3;
headerCell.Controls.Add(new LiteralControl("注文詳細"));
row1.Cells.Add(cells[0]);
row1.Cells.Add(cells[1]);
row1.Cells.Add(headerCell);
row1.Cells.Add(cells[5]);
GridViewRow row2 =
new GridViewRow(
-1,
-1,
DataControlRowType.Header,
DataControlRowState.Normal);
for (int i = 2; i < 5; i++)
{
row2.Cells.Add(cells[i]);
}
row1.CssClass = "FreezingHeader1";
row2.CssClass = "FreezingHeader2";
GridView1.Controls[0].Controls.Clear();
GridView1.Controls[0].Controls.Add(row1);
GridView1.Controls[0].Controls.Add(row2);
}
}
// position:relative を適用することにより border の
// 幅が変わってしまう。以下はその調整。
protected void GridView1_PreRender(object sender, EventArgs e)
{
// ヘッダは 3 行できるのでそれの識別用
int count = 0;
// GridView.Rows はデータ行の GridViewRow のみ
// GridView1.Controls[0].Controls はヘッダ、
// フッターも含む
foreach (GridViewRow row in GridView1.Controls[0].Controls)
{
if (row.RowType == DataControlRowType.Header)
{
if (count == 0)
{
row.Cells[0].Style["border-width"] = "2 1 1 2";
row.Cells[1].Style["border-width"] = "2 1 1 1";
// これがないとヘッダの "備考" のセルの下半分
// が切れてしまう。
// "コード" と "商品名" セルには FreezingCol
// に position:relative が含まれるので不用
row.Cells[3].Style["position"] = "relative";
row.Cells[3].Style["border-width"] = "2 2 1 1";
}
// 何故か自動でできてしまうヘッダの 3 行目を消去
if (count == 2)
{
row.Style["display"] = "none";
}
}
else if (row.RowType == DataControlRowType.DataRow)
{
if (row.RowIndex == GridView1.Rows.Count - 1)
{
row.Cells[0].Style["border-width"] = "1 1 2 2";
row.Cells[1].Style["border-width"] = "1 1 2 1";
}
else
{
row.Cells[0].Style["border-width"] = "1 1 1 2";
row.Cells[1].Style["border-width"] = "1 1 1 1";
}
}
else if (row.RowType == DataControlRowType.Footer)
{
// 今回、フッターはないので何もしない。
}
count++;
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<%--Quirks モードに設定--%>
<meta http-equiv="X-UA-Compatible" content="IE=5" />
<style type="text/css">
.FreezingHeader1
{
z-index: 10;
position: relative;
top: expression(this.offsetParent.scrollTop);
background-color: #0000cc;
}
.FreezingHeader2
{
z-index: 5;
position: relative;
top: expression(this.offsetParent.scrollTop);
background-color: #0000cc;
}
.FreezingCol
{
z-index: 1;
left: expression(
document.getElementById("freezingDiv").scrollLeft);
position: relative;
background-color: white;
}
#freezingDiv
{
overflow: auto;
width: 350px;
height: 200px;
}
table.style1
{
border-style: none; /* 指定するとスクロールでずれる */
text-align: center;
border-collapse: collapse;
}
table.style1 th
{
border-style: solid;
border-width: 2px;
border-color: #0000cc;
background-color: #6699FF;
color: #FFFFFF;
padding: 5px;
}
table.style1 td
{
border-style: solid;
border-width: 2px;
border-color: #0000cc;
padding: 5px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div id="freezingDiv">
<asp:GridView ID="GridView1"
runat="server"
CssClass="style1"
Width="450px"
AutoGenerateColumns="False"
OnRowCreated="GridView1_RowCreated"
OnPreRender="GridView1_PreRender">
<Columns>
<asp:BoundField DataField="Code"
HeaderText="コード" >
<HeaderStyle CssClass="FreezingCol" />
<ItemStyle CssClass="FreezingCol" />
</asp:BoundField>
<asp:BoundField DataField="Name"
HeaderText="商品名" >
<HeaderStyle CssClass="FreezingCol" />
<ItemStyle CssClass="FreezingCol" />
</asp:BoundField>
<asp:BoundField DataField="Price"
HeaderText="単価" >
</asp:BoundField>
<asp:BoundField DataField="Qty"
HeaderText="数量" >
</asp:BoundField>
<asp:BoundField DataField="Amount"
HeaderText="合価" >
</asp:BoundField>
<asp:BoundField DataField="Remarks"
HeaderText="備考" >
</asp:BoundField>
</Columns>
</asp:GridView>
</div>
<asp:Button ID="Button1"
runat="server"
Text="PostBack" />
</form>
</body>
</html>
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>