by WebSurfer
2015年11月2日 18:08
CheckBoxList をデータバインドする際、その子コントロールの ListItem の Text, Value, Selected プロパティを同時に設定する方法について書きます。
たぶん Text と Value プロパティをデーターベースから取得するなどして設定しておき、Selected プロパティ(チェックマーク)はユーザーが設定するというシナリオが多いと思います。
それに加えて Selected プロパティもデーターベースから取得した値であらかじめ設定しておきたいという場合を考えます。
ListItem の Text と Value プロパティは、Visual Studio のデザイン画面で CheckBoxList の DataTextField と DataValueField プロパティにデーターソースのフィールド名を指定しておけば自動的に設定されます。
Selected プロパティの方は CheckBoxList.DataBound イベントのハンドラで設定するのがよさそうです。
以下に Microsoft が提供しているサンプルデータベース Northwind の Products テーブルの ProdctName (int), ProductID (nvarchar), Discontinued (bit) を ListItem の Value, Text, Selected プロパティに設定するサンプルを作ってみました。
オマケに、GridView に CheckBox を実装して同様な形になるものも追加しておきました。どちらがいいかは使い方によると思います。
詳しい説明はサンプルのコメントに書きましたのでそれを見てください。手抜きでスミマセン。(汗)
<%@ 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">
// CheckBoxList の ListItem の Text, Value, Selected プロパ
// ティそれぞれに Products テーブルの ProdctName, ProductID,
// Discontinued を設定する。
// Page_Load で Products テーブルの ProdctName, ProductID,
// Discontinued から DataView を作成して保持する。
DataView dataView;
// DataView を作成して手動で CheckBoxList に DataBind。
// そこで各 ListItem の Text, Value プロパティは自動的に設
// 定される。
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
dataView = (DataView)SqlDataSource1.Select(
DataSourceSelectArguments.Empty);
CheckBoxList1.DataSource = dataView;
CheckBoxList1.DataBind();
}
}
// Page_Load での CheckBoxList1.DataBind() により DataBound
// イベントが発生する。Page_Load で取得した DataView を利用
// して ListItem の Selected プロパティに Discontiued の値
// を設定する。
protected void CheckBoxList1_DataBound(
object sender, EventArgs e)
{
CheckBoxList list = (CheckBoxList)sender;
foreach (ListItem item in list.Items)
{
dataView.RowFilter =
String.Format("ProductID='{0}'", item.Value);
bool auth = (bool)dataView[0]["Discontinued"];
item.Selected = auth;
}
}
// GridView の場合は RowDataBound イベントで DataRowView が
// 取得できる。なので、DataView を保持しておく必要はなく、
// GridView.DataSourceID に SqlDataSource の ID を設定して
// データバインドは自動的に行うようにすることが可能。
protected void GridView1_RowDataBound(
object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView drv = (DataRowView)e.Row.DataItem;
CheckBox cb =
(CheckBox)e.Row.FindControl("CheckBox1");
if (cb != null)
{
int id = (int)drv["ProductID"];
// Attributes ではなく InputAttributes を使う
cb.InputAttributes.Add("value", id.ToString());
}
}
}
</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 TOP 10
[ProductID], [ProductName], [Discontinued]
FROM [Products]">
</asp:SqlDataSource>
<h3>CheckBoxList</h3>
<asp:CheckBoxList
ID="CheckBoxList1"
runat="server"
DataTextField="ProductName"
DataValueField="ProductID"
OnDataBound="CheckBoxList1_DataBound">
</asp:CheckBoxList>
<hr />
<h3>GridView</h3>
<asp:GridView ID="GridView1"
runat="server"
AutoGenerateColumns="False"
DataKeyNames="ProductID"
DataSourceID="SqlDataSource1"
ShowHeader="False"
OnRowDataBound="GridView1_RowDataBound"
GridLines="None">
<Columns>
<asp:TemplateField HeaderText="Discontinued"
SortExpression="Discontinued">
<ItemTemplate>
<asp:CheckBox ID="CheckBox1"
runat="server"
Checked='<%# Bind("Discontinued") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="ProductName"
HeaderText="ProductName"
SortExpression="ProductName" />
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
by WebSurfer
2015年10月24日 14:18
ユーザーコントロール(.ascx)の中に配置した子コントロールの ClientID を、そのユーザーコントロールを使う .aspx ページで取得するにはどうしたらいいかという話を書きます。
上の画像は、ユーザーコントロール(子に TextBox を持つ)の html ソースがブラウザにレンダリングされた後、クライアント側でその TextBox(html では input 要素)を JavaScript の getElementById メソッドで取得し、input 要素の change イベントにリスナをアタッチして入力された値を取得して alert で表示したものです。(ブラウザは Chrome です)
input 要素 <input type="text" id="xxx" ... /> を getElementById メソッドで取得するためには id の値 xxx が必要です。サーバー側のコードでは ClientID が xxx になりますので TextBox.Client プロパティで容易に取得できますが、クライアント側ではどのようにするのでしょうか?
一旦 html ソースをレンダリングし、その中の xxx を調べてそれを JavaScript のコードにハードコーディングする・・・というのは避けた方がよさそうです。将来 ClientID の命名規則が変わると動かなくなりますし、コードの可読性もよくないので保守の問題もありそうです。
なので、getElementById("<%=TextBox.ClientID%>") のようにコードブロック(即ち、<% ... %>)で取得するのがよさそうですが、TextBox はユーザーコントロールの中の子コン��ロールとして配置されているのでそうはいきません。
いろいろ解決法はあると思いますが、ユーザーコントロールのコードビハインドに TextBox の ClientID を取得するためのパブリックプロパティを追加し、.aspx ページではそのパブリックプロパティをコードブロックで埋め込んでやればいいはずです。
ユーザーコントロール(.ascx)
ユーザーコントロール(.ascx)のコードは具体的には以下のようになります。本題とは関係ないコードがいろいろと入っていますが、TextBoxClientID プロパティの定義に注目してください。
<%@ Control Language="C#"
ClassName="_0014_ClientIDInWebUserControl" %>
<%@ Register Assembly="AjaxControlToolkit"
Namespace="AjaxControlToolkit"
TagPrefix="Ajax" %>
<script runat="server">
public string TextBoxClientID
{
get { return Control_Date.ClientID; }
}
</script>
<asp:TextBox ID="Control_Date"
runat="server"
Width="130px"
MaxLength="1"
CssClass="imeDisabled"
style="text-align:justify"
autocomplete="on"
ValidationGroup="MKE" />
<Ajax:MaskedEditExtender
ID="Control_DateMaskEditExtender"
runat="server"
TargetControlID="Control_Date"
Mask="9999/99/99"
MessageValidatorTip="true"
OnFocusCssClass="MaskedEditFocus"
OnInvalidCssClass="MaskedEditError"
MaskType="Date"
DisplayMoney="Left"
AcceptNegative="Left"
ErrorTooltipEnabled="True" />
<Ajax:MaskedEditValidator
ID="Control_DateMaskEditValidator"
runat="server"
ControlExtender="Control_DateMaskEditExtender"
ControlToValidate="Control_Date"
Display="Dynamic"
EmptyValueBlurredText="*"
ValidationGroup="MKE" />
<Ajax:CalendarExtender
ID="Control_CalendarExtender"
Format="yyyy/MM/dd"
runat="server"
TargetControlID="Control_Date"
PopupButtonID="ImgBntCalc" />
.aspx ページ
上記のユーザーコントロールを使用する .aspx ページは以下のようになります。下の方のインラインの JavaScript のコードで、コートブロックによって thismonthBegin.TextBoxClientID から id を取得しているところに注目してください。
<%@ Page Language="C#" %>
<%@ Register TagPrefix="Ctrl" TagName="DateCtrl"
Src="~/0014-ClientIDInWebUserControl.ascx" %>
<%@ 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">
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:ToolkitScriptManager
ID="ToolkitScriptManager1" runat="server">
</asp:ToolkitScriptManager>
<div>
<Ctrl:DateCtrl ID="thismonthBegin" runat="server" />
</div>
</form>
</body>
</html>
<script type="text/javascript">
//<![CDATA[
window.onload = function () {
var element = document.getElementById(
"<%=thismonthBegin.TextBoxClientID%>");
element.onchange = function () {
alert(element.value);
}
}
//]]>
</script>
注意:
AJAX Control Toolkit の TabContainer や Calendar を使用する際、head 要素内にコードブロック(<% ... %>)を定義すると、"System.Web.HttpException: コントロールにコード ブロック (<% ... %>) が含まれているため、コントロールのコレクションを変更できません。" という例外がスローされるので注意してください。その理由等は先の記事
ACT の TabContainer を見てください。回避策は、上のコード例のように、コードブロックを head タグの外に移動するだけです。
by WebSurfer
2015年10月23日 17:19
下の画像のように、GridView などでデータベースの更新操作を行う際 DropDownList を利用してユーザー入力に便宜を図ることがあると思います。その時、NULL を DropDownList でどう対処するかという話を書きます。
ここで紹介する例には Microsoft が提供している Northwind サンプルデータベースの Products テーブルと Categories テーブルを使用しています。
Products テーブルの中の ProductName, CategoryID フィールドを GridView 上で更新する際、CategoryID の列に DropDownList を表示するようにします。
DropDownList には、ユーザーが見ても何だか分からない数字 (CategoryID) を表示するのでははなくて、ユーザーが読んで理解できる名前 (CategoryName) を Categories テーブルから取得して表示します。
Products テーブルの CategoryID には以下のような外部キー制約がかかっていますので、Categories テーブルの CategoryID の値(Northwind サンプルデータベースでは 1 から 8)以外のものは入力できません。
ALTER TABLE [dbo].[Products]
ADD CONSTRAINT [FK_Products_Categories]
FOREIGN KEY([CategoryID])
REFERENCES [dbo].[Categories]
([CategoryID])
ただし、NULL は許可されていますので、1 から 8 以外に NULL が入っていることがあります。また、更新の際に NULL を入力したいというケースもあるかもしれません。
先の記事 GridView 上の DropDownList に ToolTip でも同様に DropDownList を使用していますが、そこでは NULL 対応は考えていませんでした。それを以下ように修正して NULL に対応します。
-
SELECT クエリで INNER JOIN 句に替えて LEFT OUTER JOIN 句を使い CategoryID が NULL のレコードも抽出する。
-
DropDownList で AppendDataBoundItems="True" とし、以下のような NULL 用の ListItem を追加する。
<asp:ListItem Value="">NULL</asp:ListItem>
Value は空白("")にしてください。DropDownList に表示される文字列 NULL は任意のものに変えていいです。データベース上での NULL と ASP.NET コントロール上での空白("")の変換は、ASP.NET の組み込み機能を利用します。その機能についての説明は MSDN ライブラリの記事データ ソース コントロールを使用した、データベースの Null 値の処理を見てください。
具体的なコードは以下の通りです。(分かりやすくするため、先の記事 GridView 上の DropDownList に ToolTip のコードの ToolTip を設定する部分は省いています)
<%@ 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 id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:SqlDataSource ID="SqlDataSource1"
runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind2 %>"
SelectCommand=
"SELECT TOP 10
p.[ProductID], p.[ProductName],
p.[CategoryID], c.[CategoryName]
FROM [Products] AS p
LEFT OUTER JOIN [Categories] AS c
ON p.[CategoryID] = c.[CategoryID]
ORDER BY p.[ProductID]"
UpdateCommand=
"UPDATE [Products]
SET [ProductName] = @ProductName,
[CategoryID] = @CategoryID
WHERE [ProductID] = @ProductID">
<UpdateParameters>
<asp:Parameter Name="ProductID" Type="Int32" />
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="CategoryID" Type="Int32" />
</UpdateParameters>
</asp:SqlDataSource>
<asp:SqlDataSource ID="SqlDataSource2"
runat="server"
ConnectionString="<%$ ConnectionStrings:Northwind2 %>"
SelectCommand=
"SELECT [CategoryID], [CategoryName], [Description]
FROM [Categories]">
</asp:SqlDataSource>
<asp:GridView ID="GridView1"
runat="server"
AutoGenerateColumns="False"
DataKeyNames="ProductID"
DataSourceID="SqlDataSource1">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:BoundField DataField="ProductID"
HeaderText="ID"
InsertVisible="False"
ReadOnly="True"
SortExpression="ProductID" />
<asp:BoundField DataField="ProductName"
HeaderText="Product Name"
SortExpression="ProductName" />
<asp:TemplateField HeaderText="Category"
SortExpression="CategoryName">
<EditItemTemplate>
<asp:DropDownList ID="DropDownList1"
runat="server"
DataSourceID="SqlDataSource2"
DataTextField="CategoryName"
DataValueField="CategoryID"
SelectedValue='<%# Bind("CategoryID") %>'
AppendDataBoundItems="True">
<asp:ListItem Value="">NULL</asp:ListItem>
</asp:DropDownList>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label1"
runat="server"
Text='<%# Bind("CategoryName") %>'>
</asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>