DB の更新などで、まず一覧画面にレコード一覧を表示してユーザーに選択させ、一覧画面は開いたまま別に編集画面を開いてそこでレコードを編集してもらい、編集画面の[更新]ボタンクリックで DB を更新すると共に編集画面は閉じて、更新結果を一覧画面に反映したいといった要求を時々聞きます。
1 ページで行うか、2 ページ使う場合は一覧画面 → 編集画面 → 一覧画面と遷移していけば難しくありませんが、上記のように開いたままの一覧画面に、編集画面による更新結果を反映するのは難しいです(というよりほとんど無理と思います)。
という訳で、実際は 1 ページで行って、見かけは 2 画面で行うようにする方法を紹介します。
具体的にどうするかというと、一覧を表示する GridView と、レコードの編集を行う DetailsView を 1 ページに配置し、DetailsView を ASP.NET AJAX Control Toolkit の ModalPopup で表示するというものです。
GridView の各行に配置した[編集]ボタンをクリックすると、ModalPopup の中の DetailsView が表示され、当該行のレコードを編集できます。その間、バックグラウンドは暗く表示され、GridView の操作はできません。
DetailsView の編集が終わったら、[更新]ボタン(上の画像、下のコードでは[Save]ボタン)をクリックすると DB の当該レコードが更新され、ModalPopup が非表示になります。
そして GridView 上で更新された行がハイライトされます。(以下のコードでは、バックグラウンドが黄色に変わり、6 秒間で3 回点滅した後、元のスタイルに戻ります)。
コードは以下のとおりです。Microsoft 提供のサンプルデータベース Northwind の Customers テーブルを使用しています。
なお、Opera 10.63 では[Save]ボタンをクリックしたとき ModalPopup が消えないという問題があります。IE8, Firefox 3.6.10, Safari 5.0.2 は問題なしでした。
<%@ Page Language="C#" %>
<%@ Register Assembly="AjaxControlToolkit"
Namespace="AjaxControlToolkit"
TagPrefix="asp" %>
<!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_SelectedIndexChanged(object sender, EventArgs e)
{
DetailsView1.DefaultMode = DetailsViewMode.Edit;
UpdatePanel2.Update();
ModalPopupExtender1.Show();
}
protected void LinkButton1_Click(object sender, EventArgs e)
{
DetailsView1.UpdateItem(false);
GridView1.DataBind();
UpdatePanel1.Update();
ModalPopupExtender1.Hide();
if (ScriptManager.GetCurrent(this).IsInAsyncPostBack)
{
ScriptManager.GetCurrent(this).RegisterDataItem(GridView1,
GridView1.SelectedIndex.ToString());
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
<style type="text/css">
/*Modal Popup*/
.modalBackground {
background-color: Gray;
filter: alpha(opacity=70);
opacity: 0.7;
}
tr.updated td {
background-color: yellow;
}
.detail {
background-color: #ffffff;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">
</asp:ToolkitScriptManager>
<script type="text/javascript">
<!--
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoaded);
function pageLoaded(sender, args) {
var updateRowIndex = args.get_dataItems()["GridView1"];
if (updateRowIndex) {
var tr = $get("GridView1").rows[parseInt(updateRowIndex) + 1];
$(tr).addClass('updated').children('td')
.fadeTo(1000, 0.33).fadeTo(1000, 1.0)
.fadeTo(1000, 0.33).fadeTo(1000, 1.0)
.fadeTo(1000, 0.33).fadeTo(1000, 1.0);
window.setTimeout(function () {
$(tr).removeClass('updated');
}, 6000);
}
}
//-->
</script>
<div>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind %>"
SelectCommand=
"SELECT [CustomerID], [CompanyName], [ContactName],
[ContactTitle], [Phone]
FROM [Customers]">
</asp:SqlDataSource>
<asp:UpdatePanel ID="UpdatePanel1"
runat="server"
UpdateMode="Conditional">
<ContentTemplate>
<asp:GridView ID="GridView1"
runat="server"
AutoGenerateColumns="False"
DataKeyNames="CustomerID"
DataSourceID="SqlDataSource1"
EnableModelValidation="True"
OnSelectedIndexChanged="GridView1_SelectedIndexChanged"
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:CommandField SelectText="編集"
ShowSelectButton="True" />
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
</div>
<div>
<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="GridView1" Name="CustomerID"
PropertyName="SelectedValue" 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:Panel ID="Panel1" runat="server" CssClass="detail">
<asp:UpdatePanel ID="UpdatePanel2"
runat="server"
UpdateMode="Conditional">
<ContentTemplate>
<asp:Button ID="DummyButton"
runat="server"
style="display: none;" />
<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" />
</div>
<asp:ModalPopupExtender ID="ModalPopupExtender1"
runat="server"
PopupControlID="Panel1"
TargetControlID="DummyButton"
CancelControlID="LinkButton2"
BackgroundCssClass="modalBackground">
</asp:ModalPopupExtender>
</ContentTemplate>
</asp:UpdatePanel>
</asp:Panel>
</div>
</form>
</body>
</html>
ネタは先の記事 jQuery の本を買いました で紹介した本のサンプルです(サンプルそのままではなく、不要な部分の削除、誤りの修正を行っています)。
jQuery の本なのに、jQuery を使っているのは GridView 上で更新された行がハイライトされるところだけです。もっと使えるのかと思っていたのですが、更新操作のサーバーとの連携が難しいようで、ちょっと期待はずれでした。
---------------- 2011/5/29 追記 ----------------
Opera では[Save]ボタンをクリックしたとき ModalPopup が消えないという問題があると書きましたが、UpdatePanel で囲うのを GridView と DetailsView に分けず一つにまとめるとその問題が回避できます。
その場合、GridView1_SelectedIndexChanged メソッドの UpdatePanel2.Update(); および LinkButton1_Click メソッドの UpdatePanel1.Update(); は不要です。