by WebSurfer
2010年11月14日 17:42
Calendar コントロールに CheckBox を配置して、それにチェックを入れた日付をポストバックしてサーバー側で取得するサンプルです。
CheckBox にチェックを入れる/外すたびにポストバックして、チェックを入れた/外した日付を ViewState に追加/削除し、Show selected dates ボタンクリックでチェックが入っている日付を Label に表示します。
「チェックを入れる/外すたびにポストバック」というのがちょっとわずらわしい感じですね。(汗)
でも、月をまたがって日付にチェックを入れる(例えば、1 月 15 日と 3 月 15 日にチェックを入れる)場合も対応できるようにするには、この方法しか思いつかないです。
チェックを入れる/外すたびにいちいちポストバックしないでもすむ方法を考え付いたら、別途記事を書きます・・・が、期待はしないでください。(笑)
2013/12/12 追記:今さらながらですが、チェックを入れる/外すたびにいちいちポストバックしないでもすむサンプルも作ってみました。CheckBox 付き Calendar(その2)を見てください。月を移動する時、ちょっと(かなり?)重い感じです。(汗)
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Implements Interface="System.Web.UI.IPostBackEventHandler" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected List<DateTime> checkedDates = new List<DateTime>();
protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
{
CheckBox cb = new CheckBox();
cb.ID = e.Day.Date.ToShortDateString();
string script =
Page.ClientScript.GetPostBackEventReference(this, cb.ID);
cb.Attributes.Add("onclick", script);
foreach (DateTime day in checkedDates)
{
if (e.Day.Date == day)
{
cb.Checked = true;
break;
}
cb.Checked = false;
}
e.Cell.Controls.Add(cb);
}
public void RaisePostBackEvent(string eventArgument)
{
foreach (DateTime day in checkedDates)
{
if (eventArgument == day.ToShortDateString())
{
checkedDates.Remove(DateTime.Parse(eventArgument));
ViewState["CheckedDates"] = checkedDates;
return;
}
}
checkedDates.Add(DateTime.Parse(eventArgument));
ViewState["CheckedDates"] = checkedDates;
}
protected void Page_Load(object sender, EventArgs e)
{
object obj = ViewState["CheckedDates"];
if (obj != null)
{
checkedDates = (List<DateTime>)obj;
}
}
protected void Button1_Click(object sender, EventArgs e)
{
string str = "Selected Dates:";
foreach (DateTime day in checkedDates)
{
str += "<br />" + day.ToShortDateString();
}
Label1.Text = str;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Calendar with CheckBox</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Calendar ID="Calendar1"
runat="server"
ondayrender="Calendar1_DayRender"
SelectionMode="None" >
</asp:Calendar>
<asp:Button ID="Button1"
runat="server"
Text="Show selected dates"
onclick="Button1_Click" />
<br />
<asp:Label ID="Label1" runat="server" />
</div>
</form>
</body>
</html>
------------ 2010/4/24 追記 ------------
この記事で紹介したコードを実際に動かして試せるよう 実験室 にアップしました。興味のある方は試してみてください。
by WebSurfer
2010年11月7日 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
2010年11月4日 21:47
JavaScript の window.opener を利用すると、例えばページ A から window.open でページ B を開いたとすると、ページ B からページ A にポストバックをかけることが可能です。
先の記事 ModalPopup で編集・更新操作 で書いた、「一覧画面にレコード一覧を表示してユーザーに選択させ、一覧画面は開いたまま別に編集画面を開いてそこでレコードを編集してもらい、編集画面の[更新]ボタンクリックで DB を更新すると共に編集画面は閉じて、更新結果を一覧画面に反映する」というシナリオに使えます。
それを知らなかったので、先の記事では「開いたままの一覧画面に、編集画面による更新結果を反映するのは難しいです(というよりほとんど無理と思います)。」と書きましたが、そんなことはなかったです。(汗)
以下に、備忘録として例を書いておきます。
検証用に、一覧表示のための GridView を持つ親ページ (RecordList.aspx) と、編集・更新のための DetailsView を持つ子ページ (EditAndUpdate.aspx) を用いてアプリを作ってみました。手順は以下のようになります。
-
まず親ページを要求する。
-
親ページは、DB からデータを取得し、GirdView にレコード一覧を表示。
-
GridView 上のレコードを選択すると、親ページは開いたまま別ウィンドウを開いて子ページを要求。
-
子ページは、選択されたレコードを DB から取得し DetailsView に Edit モードで表示。
-
子ページでレコードを編集し、ボタンクリックでポストバックして DB を更新。同時に、親ページにポストバックをかけるための JavaScript を追加する。
-
子ページが再描画された時、追加した JavaScript が働いて親ページにポストバックをかける。
-
親ページはサーバー側で更新後のレコードを取得し GridView に表示する。
-
子ページを自動的に閉じる。
コードは以下のとおりです。
RecordList.aspx(親ページ)
<%@ 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">
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton lb = (LinkButton)e.Row.FindControl("LinkButton1");
DataRowView rowView = (DataRowView)e.Row.DataItem;
string script = String.Format(
"javascript:window.open('EditAndUpdate.aspx?customerId={0}', " +
"null, 'width=400, height=300'); return false;",
rowView["CustomerID"]);
lb.OnClientClick = script;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
GridView1.DataBind();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>一覧画面</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>一覧画面</h1>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand=
"SELECT [CustomerID], [CompanyName], [ContactName],
[ContactTitle], [Phone]
FROM [Customers]">
</asp:SqlDataSource>
<asp:GridView ID="GridView1"
runat="server"
AutoGenerateColumns="False"
DataKeyNames="CustomerID"
DataSourceID="SqlDataSource1"
EnableModelValidation="True"
OnRowDataBound="GridView1_RowDataBound"
AllowPaging="True">
<Columns>
<asp:BoundField DataField="CustomerID"
HeaderText="CustomerID"
ReadOnly="True"
SortExpression="CustomerID" />
<asp:BoundField DataField="CompanyName"
HeaderText="CompanyName"
SortExpression="CompanyName" />
<asp:BoundField DataField="ContactName"
HeaderText="ContactName"
SortExpression="ContactName" />
<asp:BoundField DataField="ContactTitle"
HeaderText="ContactTitle"
SortExpression="ContactTitle" />
<asp:BoundField DataField="Phone"
HeaderText="Phone"
SortExpression="Phone" />
<asp:TemplateField ShowHeader="False">
<ItemTemplate>
<asp:LinkButton ID="LinkButton1"
runat="server"
CausesValidation="False"
CommandName="Select"
Text="編集">
</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
EditAndUpdate.aspx(子ページ)
<%@ 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 Page_Load(object sender, EventArgs e)
{
string customerid = Request.QueryString["customerId"];
if (!String.IsNullOrEmpty(customerid))
{
DetailsView1.DefaultMode = DetailsViewMode.Edit;
Panel1.Visible = true;
Panel2.Visible = false;
Label1.Text = customerid;
}
else
{
Panel1.Visible = false;
Panel2.Visible = true;
}
}
protected void LinkButton1_Click(object sender, EventArgs e)
{
DetailsView1.UpdateItem(false);
string csname = "PostBackSourceWindowScript";
Type cstype = this.GetType();
ClientScriptManager cs = Page.ClientScript;
if (!cs.IsStartupScriptRegistered(cstype, csname))
{
String cstext =
@"window.onload = function() {
if (!window.opener || window.opener.closed) {
self.close();
}
else {
window.opener.document.getElementById('form1').submit();
self.close();
}
};";
cs.RegisterStartupScript(cstype, csname, cstext, true);
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>編集画面</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel ID="Panel1" runat="server">
<h1>編集画面</h1>
<span>
選択された Customer ID: <asp:Label ID="Label1" runat="server" />
</span>
<asp:SqlDataSource ID="SqlDataSource2" runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand=
"SELECT [CustomerID], [CompanyName], [ContactName],
[ContactTitle], [Phone]
FROM [Customers]
WHERE ([CustomerID] = @CustomerID)"
UpdateCommand=
"UPDATE [Customers]
SET [CompanyName]=@CompanyName, [ContactName]=@ContactName,
[ContactTitle]=@ContactTitle, [Phone]=@Phone
WHERE [CustomerID] = @CustomerID">
<SelectParameters>
<asp:ControlParameter ControlID="Label1"
Name="CustomerID"
PropertyName="Text"
Type="String" />
</SelectParameters>
<UpdateParameters>
<asp:Parameter Name="CompanyName" Type="String" />
<asp:Parameter Name="ContactName" Type="String" />
<asp:Parameter Name="ContactTitle" Type="String" />
<asp:Parameter Name="Phone" Type="String" />
<asp:Parameter Name="CustomerID" Type="String" />
</UpdateParameters>
</asp:SqlDataSource>
<asp:DetailsView ID="DetailsView1"
runat="server"
AutoGenerateRows="False"
DataKeyNames="CustomerID"
DataSourceID="SqlDataSource2"
EnableModelValidation="True">
<Fields>
<asp:BoundField DataField="CustomerID"
HeaderText="CustomerID"
ReadOnly="True"
SortExpression="CustomerID" />
<asp:BoundField DataField="CompanyName"
HeaderText="CompanyName"
SortExpression="CompanyName" />
<asp:BoundField DataField="ContactName"
HeaderText="ContactName"
SortExpression="ContactName" />
<asp:BoundField DataField="ContactTitle"
HeaderText="ContactTitle"
SortExpression="ContactTitle" />
<asp:BoundField DataField="Phone"
HeaderText="Phone"
SortExpression="Phone" />
</Fields>
</asp:DetailsView>
<div>
<asp:LinkButton ID="LinkButton1"
runat="server"
onclick="LinkButton1_Click"
Text="Save" />
<asp:LinkButton ID="LinkButton2"
runat="server"
CausesValidation="False"
Text="Cancel"
OnClientClick="self.close(); return false;" />
</div>
</asp:Panel>
<asp:Panel ID="Panel2" runat="server">
<p>クエリ文字列 customerId が指定されていません。</p>
</asp:Panel>
</div>
</form>
</body>
</html>
先の記事 ModalPopup で編集・更新操作 で紹介したアプリと比べると、一覧画面の[編集]ボタンの二度押し防止と、更新された行を GirdView 上でハイライトする操作が実装できていません。解決策がないか考えてみましたが、思いつきません。というわけで、先の記事の例の方がよさそうです。