▼ 2007/12/11(火) ASP.NET2.0とかの手探りなあれこれ
OS | Windows Vista |
VisualStudio | 2005 + Vistaパッチ |
.NET | 2.0 |
ブラウザ | IE7 |
言語 | VB.NET |
知識ゼロから手探りでやっていたので,色々間違いがある可能性がいつもにも増して高いです.
ASP.NETと関係無い話も入っています.
■ マスタページ適用状態で独自のスタイルシートを追加
要するに,マスタページを使ってコンテンツページを作っている時にそのページだけで使うスタイルシートを読み込むようにしようと思ってもHTMLのヘッダ部にContentPlaceHolderは入れられないしどうしようということ.次のページに答えが.
@Prog! - ASP.NET(C#) - - マスターページのヘッダーにスタイルシートファイルや Javascript ファイルを追加する
説明が載っていないので補足すると,次の手順.
SampleMasterPage.master(マスタページ)
<%@ Master Language="VB" CodeFile="SampleMasterPage.master.vb" Inherits="MasterPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Header" runat="server"> <title>サンプルマスタページ</title> <link rel="stylesheet" href="default.css" type="text/css" media="all" /> </head> <body> . . .ヘッダタグにidを追加.(原文はコードがC#)
Contents.aspx.vb(コンテンツページのコードビハインドファイル) protected Sub Page_Load(object sender, EventArgs e) Dim literal As Literal = new Literal() literal.Text = "<link rel='stylesheet' type='text/css' href='StyleSheet.css' />" Dim hh As HtmlHead = DirectCast(Page.Master.FindControl("Header"),HtmlHead) hh.Controls.Add(literal); End Subマスタページは,Page.Masterでアクセスできるので,そこにリテラルを追加.
本当は,こんな方法よりも全体の構成を考えてスタイルシートの割り振りなどを考えた方が良かったと後で思いました.
ただ,それができない状態でもあったのでそういう時に使えるのかもしれません.
■ GridViewを使う
便利だけれど,初見だととても使いにくいDataGridView.ASP.NET版DataGridViewと言えるGridViewは,便利さも使いにくさもDataGridViewと同じ感じです.
しかし,ぱっと見ただけでも魅力的な機能が満載なので使わないのはもったいない!
私が便利だと思ったのは……
- SqlDataSourceと関連づけるとコーディングレスでDB内のデータを一覧表示可能
- ページ送り機能をワンクリックで実装可能
- ソート機能もワンクリックで実装可能
- TemplateFieldを使えば表示の柔軟性を高くできる
私が使ったバッドノウハウ的なものをいくつか書いておきます.
チェックボックスを配置
TemplateFieldにチェックボックスを載せる.<asp:TemplateField HeaderText="選択"> <ItemTemplate> <asp:CheckBox ID="chkProc" runat="server" /> </ItemTemplate> </asp:TemplateField>
全チェックボックスを選択状態にする/解除する(JavaScript)
Japan.internet.com デベロッパー - GridView上のすべてのチェックボックスをオンにする方法このサイトに載っています.
チェックされている行を全取得
' IDがSampleGridViewのGridViewのTemplateField内に ' IDがchkProcのCheckBoxが配置されているとします Public Function getCheckedRows() As DataRow() Dim result As New List(Of DataRow)() ' 全行を対象にする For Each line As GridViewRow In Me.SampleGridView.Rows ' ヘッダやフッタは対象外 If line.RowType = DataControlRowType.DataRow Then ' 行内のチェックボックスを取得 Dim checkbox As CheckBox = DirectCast(line.FindControl("chkProc"), CheckBox) ' チェックが入っていたなら,その行と関連づけられているDataRowを取得 If checkbox.Checked = True Then lines.Add(DirectCast(line.DataItem, DataRowView).Row) End If End If Next ' 配列として返す Return result.ToArray() End Function非常にシンプルです.
SqlDataSourceとGridViewが結び付いている場合は,
行に関連づけられたDataRowを取り出せるので処理が楽になります.
逆に,DataRowが取り出せなかったりDataRowには入っていないけれど行内で保持している情報を返したい場合は,
lines.Add(DirectCast(line.DataItem, DataRowView).Row)この部分を変えることでどんな風にでもできます.
HTMLの自動エスケープを停止
自由にタグを入れられないのが不便な場合はそのカラムの自動エスケープを停止することで回避します.該当カラムのHtmlEncodeプロパティをFalseにすればOK.
ただし必要箇所に自分でエスケープ処理を入れるのはお忘れなく.
データベースから引っ張ってきた値を加工して表示する
GridView イベント (System.Web.UI.WebControls)例えばテーブル名SAMPLEのカラムSAMPLE_NUMBERの値が1なら「晴天」,2なら「雨天」をGridViewのカラムに表示することを考えます*1.
まず,自分で勝手に値を表示するためにDataSourceと結び付いていないカラムを用意します.
この場合は,テンプレートフィールドにラベルを仕込むと良さそうです.
<asp:TemplateField HeaderText="お天気"> <ItemTemplate> <asp:Label ID="lblWEATHER" runat="server" /> </ItemTemplate> </asp:TemplateField>GridViewのRowDataBoundイベントを利用すると,
GridViewの各行とDataSourceが結び付いた時に
その行とその行に結び付くデータを参照しながら作業ができるので簡単です.
と,言葉で書いてもわかりにくいのでコードを見て下さい.
#{' RowDataBoundイベント} Protected Sub SampleGridView_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles SampleGridView.RowDataBound #{' e.Rowには,対象となるGridViewの行が入っています} Dim line As GridViewRow = e.Row #{' イベントが発生するのは,DataSourceと結び付いた直後なので,このようにしてデータを取り出せます} Dim row As DataRow = DirectCast(line.DataItem, System.Data.DataRowView).Row #{' 値によって表示をわける} Select Case row.Item("SAMPLE_NUMBER") Case 1 DirectCast(line.FindControl("lblWEATHER"), Label).Text = "晴天" Case 2 DirectCast(line.FindControl("lblWEATHER"), Label).Text = "雨天" End Select End Subちなみにこれ,row.Item("SAMPLE_NUMBER")がDbNullだったら多分落ちます.
こんな感じでHiddenFieldに何かの値を入れておいてJavaScriptで読み取って使うとかもできますね*2.
ソート状態を知らせる
この辺参照で.GridView.SortExpression プロパティ (System.Web.UI.WebControls)
How to sort GridView?
■ Webユーザーコントロール
既存のWebコントロールを組み合わせてロジックを追加するなどした独自のコントロールを作れます.「新しい項目の追加」>「Webユーザーコントロール」
適当に配置.
Web ユーザー コントロールと Web カスタム コントロール
@IT:.NET TIPS [ASP.NET]サイト共通のレイアウト部分を部品化するには? - C# VB.NET Webフォーム
作ったユーザーコントロールを使うには,使いたいページに次の形式で宣言を記します.
<%@ Register TagPrefix="タグ接頭辞" TagName="タグ名" Src="Webユーザーコントロールへのパス(相対パスと絶対パスどちらでもOK)" %>実際は次のような感じ.
マスターページは例で載せているだけなので,マスターページを使わないときは当然マスターページ宣言は抜きで.
<%@ Page Language="VB" MasterPageFile="~/SampleMasterPage.master" AutoEventWireup="false" CodeFile="sample.aspx.vb" Inherits="sample" title="サンプルページ" %> <%@ Register TagPrefix="uc" TagName="SampleWebUserControl" Src="~/SampleWebUserControl.ascx" %> <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server"> <uc:SampleWebUserControl runat="server" ID="sample" /> </asp:Content>このように,<TagPrefix:TagName ~ />といった感じで使います.
方法 : ASP.NET Web ページにユーザー コントロールを含める
属性設定
Labelなんかだと,次のような形でコントロールに値をセットできます.<asp:Label runat="server" Text="ラベルに表示する文字列" ID="sample" />ユーザーコントロールで次のようにLabelと同じことをするにはどうするか.
<uc:SampleWebUserControl runat="server" ID="sample" sampleField="5" />@IT:.NET TIPS [ASP.NET]ユーザー・コントロールで属性を設定するには? - C# VB.NET Webフォーム
PublicなPropertyをユーザーコントロールのクラスで宣言すればOK.
Propertyの宣言例
Protected _foo As Integer Public WriteOnly Property foo() As Integer Set(ByVal value As Integer) Me._foo = value End Set End PropertyWriteOnlyである必要はありませんが,値をセットするだけならGetterは必要無いので.
■Validator
何らかの入力を処理するプログラムは,その入力がシステムで想定した条件を満たしているかを検証してから処理を行う必要があります.
Webフォームの場合においても同様に入力値の検証は非常に重要です.
しかし,全ての項目に対して行う必要があるので実装に手間がかかり
不具合が起こると大きな問題に繋がりやすいという問題があります*3.
Validatorは,Webフォームの検証を半自動化することによってこの問題を軽減することが可能なコントロールです.
ASP.NET2.0には,目的別に5種類のValidatorがあります.
Validator種別 | 概要 |
---|---|
RequiredFieldValidator | 必須チェック |
RangeValidator | 数値の範囲チェック |
RegularExpressionValidator | 正規表現チェック |
CompareValidator | 数値の比較チェック |
CustomValidator | ユーザが処理内容を定義できるValidator |
- 入力された値をチェックする方法
- http://www.microsoft.com/japan/msdn/asp.net/tips/InputCheck/
- RequiredFieldValidator
- http://www.microsoft.com/japan/msdn/asp.net/tips/InputCheck/
- 入力された値をより高度にチェックする方法
- http://www.microsoft.com/japan/msdn/asp.net/tips/InputCheck2/
- CompareValidator,RangeValidator,RegularExpressionValidator
- http://www.microsoft.com/japan/msdn/asp.net/tips/InputCheck2/
- @IT:解説:ASP.NETで学ぶVisual Studio .NETの魅力 第2回 Visual Studio.NETでプログラム・レス開発を学ぶ(前半)
- http://www.atmarkit.co.jp/fdotnet/aspandvs/aspandvs02/aspandvs02_04.html
CustomValidator
CustomValidatorは,他のValidatorでは対応できないようなチェック.例えば文字列の長さチェックなんかをチェック用のロジックを埋め込むだけで実装できるValidatorです.
使い方は簡単で,クライアントサイドとサーバサイドでチェック用の関数を作り,それを指定する以外は他のValidatorと同じです.
クライアントサイドでのチェック用関数は,JavaScriptの関数をWebページから利用可能な場所に書きます.
.jsファイルに書いて読み込ませてもいいですし,.aspxファイルに直書きしてもいいです.
このコードは,次のページを参照しました.
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=41062&forum=7
// 文字列長をチェックするValidator function customValidator_StringLength(source, arguments) { var val = arguments.Value; if (val == "") { arguments.IsValid = true; return; } var len = 0; var c; for (i = 0; i < val.length; i++) { c = escape(val.charAt(i)); } if(c.length > 3) { //len += 2; len += 1; } else { len += 1; } // CustomValidatorオブジェクトのcontroltovalidateフィールドで検証対象のIDを取得している. // ここでは,検証対象(<input type="text"などを想定)のmaxlengthを許可文字列長の閾値としている. var limit = document.getElementById(source.controltovalidate).maxLength; if (len <= limit) { arguments.IsValid = true; } else { arguments.IsValid = false; } }arguments.Valueに入力値が入っているので,この値に対して何らかの検証を行い,
検証の結果問題が無いと判断したならarguments.IsValidをTrueに.
問題があるならFalseにすることで検証結果をASP.NETの検証システムに通知します.
引数のsourceは,CustomValidatorのオブジェクトが入っています.
クライアントサイドにおいて,CustomValidatorの実体はspan要素になっています.
サーバサイドでは,次のシグネチャを持つメソッドを定義します.
' 文字列長の検証 Protected Sub customValidator_StringLength(ByVal source As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs) ' ここでもJavaScriptと同じように検証対象のMaxLengthから許容文字列長を取得している Dim limit As Integer = DirectCast(Me.FindControl(source.ControlToValidate), TextBox).MaxLength If limit >= args.Value.Length Then args.IsValid = True Else args.IsValid = False End If End SubJavaScriptと同じく,第二引数のIsValidにTrueを設定すれば検証OK,Falseを設定すれば問題ありとなります.
CustomValidatorの実際の利用は,次のようになります.
<asp:TextBox ID="txtSample" runat="server" MaxLength="30"/> <asp:CustomValidator ID="sampleCutomValidator" runat="server" ClientValidationFunction="customValidator_StringLength" OnServerValidate="customValidator_StringLength" ErrorMessage="文字列が長すぎます" ControlToValidate="txtSample" />
検証処理を実行してから任意のスクリプトを走らせる
ボタンをクリック→Validatorによって入力チェック→確認ダイアログを出す(OK/キャンセル)→OKならsubmitという流れを想定するとします.
ボタンのOnClientClick属性を次のようにしてみると良さそうに見えます.
OnClientClick="if (confirm('登録します.よろしいですか?') == false) return false;"しかし,実はこれだと次のようになってしまいます.
ボタンをクリック→確認ダイアログを出す(OK/キャンセル)→Validatorによって入力チェック→OKならsubmitこれを回避するには,自力で上の処理を書く必要があります.
具体的には,次のコードのようにします.
OnClientClick="if (Page_ClientValidate('mainGroup')) { if(confirm('登録します.よろしいですか?') == false) return false; }"'mainGroup'には,検証対象のValidationGroupを指定して下さい.
if ( (function(){ return confirm('登録します.よろしいですか?');})() == false ) return false;
*3 : Webフォーム固有の問題というわけではないですが,構築するシステムの多くが個人情報を扱うデータベースと連携していたりしている上に入力項目が非常に多くて実装が大変かつ不具合が混入しやすく,しかも狙われやすかったりと大きな問題です.
■Validatorの動的追加
Validatorは,普段表のコード(.aspx)に"<asp:CustomValidator"みたいなタグを書いて設定しますが,ビハインドコード(.aspx.vb|aspx.cs)の実行中にValidatorを追加するにはどうすればいいのでしょうか.
普通は,検証対象とどのように検証するかははっきりしているので使わないことが多いような気もしますが,
例えばWebユーザーコントロールを作っているとこれが必要なことが結構あります.
Webユーザーコントロールは,色々な所で色々な目的で呼ばれます.
例えば,年月日のコンボボックスを提供するWebユーザーコントロールは,
あるところでは単に年月日を表示するだけで検証は必要ないかもしれないし,
別のところでは他の入力要素と連動して何かを検証する必要があるかもしれません.
書いてきて息切れしてきたので例だけ載せます.
今回の例はユーザーコントロールにCustomValidatorを動的に追加するなら?という題材ですが,
他の状況でも同様にできる筈です.
ユーザーコントロールに次のような公開メソッドを追加します.
//ユーザーコントロールに公開メソッドを追加 Public Sub registerValidator(ByRef sourceValidator As CustomValidator) // validationTargetControlは,検証対象のコントロール sourceValidator.ControlToValidate = Me.validationTargetControl.ID // Validatorコントロールを検証対象のコントロールの次に追加 Me.Controls.AddAt(Me.Controls.IndexOf(Me.validationTargetControl) + 1, sourceValidator) End Subユーザーコントロールを使う側に次のような処理を追加します.
//変数名 Validator: "sampleValidator" ユーザーコントロール: "sampleUserControl" //ユーザーコントロールに追加するValidator Dim sampleValidator As New CustomValidator() //通常のVaildatorと同じように検証用の設定をする sampleValidator.ID = "sampleValidatorID" sampleValidator.ErrorMessage = "<br />検証結果が不正です" sampleValidator.ClientValidationFunction = "customValidator_Function" sampleValidator.ValidationGroup = "SampleValidationGroup" sampleValidator.EnableClientScript = True sampleValidator.Enabled = True sampleValidator.Visible = True //サーバーサイドの検証メソッドを関連づける AddHandler sampleValidator.ServerValidate, AddressOf Me.customValidator_Function //ユーザーコントロールにValidatorを追加 Me.sampleUserControl.registerValidator(sampleValidator)
■ADO.NET
DbNull?
DbNullをDbParameterのValueに渡そうとして次のようなコードを書くとエラーになります.DbNullはクラスであって値ではないからです.
objParam.Value = DbNull次のようにすればOK
objParam.Value = DbNull.Value
DbCommandとDbParameterでクエリを発行
注意:超はしょってます.DbCommandとDbParameterを使うとSQL文をわかりやすく書けます.具体的には次のような感じ.
' SQL文の作成 sql += "UPDATE SAMPLE" sql += " SET " sql += " FOO = #{@FOO}" sql += " ,BAR = #{@BAR}" sql += " WHERE FOOBAR = #{@FOOBAR}" ' コマンドをセット objCommand.CommandText = sql ' パラメータをセット objParam = objCommand.CreateParameter() objParam.ParameterName = "@FOO" objParam.Value = True objParam.DbType = Data.DbType.Boolean objCommand.Parameters.Add(objParam) objParam = objCommand.CreateParameter() objParam.ParameterName = "@TO_PAYMENT_DATE" objParam.Value = "対応する値" objParam.DbType = String objCommand.Parameters.Add(objParam) objParam = objCommand.CreateParameter() objParam.ParameterName = "@FOOBAR" objParam.Value = id objParam.DbType = Data.DbType.Int32 objCommand.Parameters.Add(objParam)で,このDbCommandをDB接続オブジェクトに渡すとクエリが発行できるというわけです.
これは,ちょうど次のようにSQL文を作成する作業に置き換えられます.
sql += "UPDATE T_ORDERS" sql += " SET " sql += " TO_PAYMENT_FLAG = 'True'" sql += " ,TO_PAYMENT_DATE = '対応する値'" sql += " WHERE TO_ID = '" + id.toString() + "'"行数はこっちのが少なくて見通しが良さそうですが,
文が複雑になってきたり変数の数が多くなってくるとDbCommandを使った方が管理しやすくなる場合もあるようです.
あと,DbParameterを使うと入っちゃいけない文字列なんかも監視してくれるみたい.
有名なシングルクォーテーションとか.
■Transact-SQL
文字列連結
SELECT FOO + BAR AS FOOBAR, FOO, BAR FROM SAMPLE文字列結合には+演算子を使います.
+ (文字列連結) (Transact-SQL)
NULL値対策
文字列結合時にFOOの値がNULLだと,例えBARがNULLでなくてもFOO+BARはNULLになってしまいます.(NULLに何かを足してもNULLにしかなりません)
これを回避するには,次のようにしろとリファレンスには書いてあります.
現在のセッションの CONCAT_NULL_YIELDS_NULL の設定を変更することにより、この動作を変更できます。しかし,設定まで変えなくても次のようにすることで回避可能です.
SELECT #{ISNULL(FOO,'')} + BAR AS FOOBAR, FOO, BAR FROM SAMPLEISNULL関数は,第一引数に対象の列名を取り,この列の値がNULLだった場合に第二引数の値を返します.
つまり,例文のようにすることでFOOがNULLの場合は空文字を返すようにすれば問題がなくなるわけです.
ISNULL (Transact-SQL)
▼ コメント(0件)
- TB-URL http://mitc.s279.xrea.com/diary/055/tb/