WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

CustomValidator のクライアント側での検証

by WebSurfer 27. June 2020 12:03

ASP.NET Web Forms アプリでユーザー入力の検証に用いられる CustomValidator のクライアント側での検証について調べたことをまとめて備忘録として書いておきます。

CustomValidator のクライアント側での検証

CustomValidator は、プログラマが独自の検証ロジックをコーディングして検証メソッドとしてページに実装し、ユーザー入力の検証を行うために利用されます。複数の入力コントロールにまたがって検証を行うことも可能です。

また、RegularExpressionValidator などと異なり、TextBox の他に CheckBox, RadioButton, DropDownList, FileUpload などのユーザー入力コントロールの検証に利用できます。

クライアント側での検証は、JavaScript を使って検証用メソッドを自力でコーディングし、それをページに実装することで可能になります(サーバー側でなければ検証できない場合は話は別です。ajax を使う手はいろいろ問題がありそうです。詳しくは先の記事「CustomValidator で jQuery.ajax 利用」を見てください)。

この記事の下の方に TextBox, CheckBox, RadioButton, DropDownList, FileUpload を対象として、CustomValidator によるクライアント側での検証を実装したサンプルコードを書いておきます。上の画像を表示したものです。

自分的に注意が必要と思う点を以下に箇条書きにしておきます。

  1. クライアント側での検証は html 要素の change イベントでかかるようになっています。CustomValidator を change イベントで動くようにするには ControlToValidate プロパティの設定が必要です。(注: submit でも検証がかかります。というか、change で検証がかかるのはユーザビリティ向上のためで、submit 時の検証がメインです)
  2. CheckBox, RadioButton コントロールに対しては CustomValidator の ControlToValidate プロパティを設定できません。設定すると HttpException がスローされ、例えば CheckBox の場合は「'CustomValidator' の ControlToValidate プロパティで参照されたコントロール 'CheckBox' を検証できません。」というエラーメッセージが表示されます。

    エラーとなる直接の理由は、ASP.NET 内部で CheckControlValidationProperty メソッドによる検証対象コントロールのチェックを行っていますが、CheckBox RadioButton コントロールには ValidationPropertyAttribute 属性が付与されてないためないためです。

    そもそもの理由は、Microsoft のドキュメントによると「ControlToValidate プロパティを設定せずに CustomValidator コントロールを使用することもできます。 これは、複数の入力コントロールを検証する場合や、CheckBox コントロールなどの検証コントロールで使用できない入力コントロールを検証する場合に一般的に行われます」とのことで、もともと CheckBox や RadioButton は検証コントロールを使う対象外のように読めます。
  3. TextBox, DropDownList, FileUpload コントロールについては、CustomValidator の ControlToValidate プロパティを検証対象コントロールの ID に設定すれば change イベントで検証がかかります。

    なお、ControlToValidate プロパティを設定しなくても submit で検証はかかりますので、change イベントでいちいち検証がかかるのは煩わしいという場合は設定しない方がよさそうです。(RequiredFieldValidator など他の検証コントロールは ControlToValidate プロパティを設定しないとエラーになりますので注意してください。CustomValidator だけ特別です)。
  4. どういう html 要素がどのタイミングで change イベントを発生させるかについては MDN の記事 HTMLElement: change event を見てください。その記事に書いてある通り、TextBox はユーザーが入力してフォーカスを外した時、DropDownList はユーザーが選択を変更したとき、FileUpload はユーザーがファイルを選択したとき change イベントが発生し、CustomValidator の ControlToValidate プロパティが設定されていれば検証がかかります。

    CheckBox (input type="checkbox"), RadioButton (input type="radio") も change イベントは発生しますが、上に述べたように CustomValidator の ControlToValidate プロパティを設定できないので、change イベントでは CustomValidator による検証はかかりません(submit で検証されます)。
  5. クライアント側での検証用 JavaScript のメソッドは CustomValidator の ClientValidationFunction プロパティに設定します。メソッド名が例えば Validate(sender, args) とすると、sender には CustomValidator が html に変換された span 要素が渡されます。args には IsValid, Value プロパティを持つ JavaScript オブジェクトが渡されます。

    CustomValidator の ControlToValidate プロパティが検証対象コントロールに対して設定してある場合は、args.Value には検証対象の入力コントロールが html に変換された input 要素の value 属性の値が渡されます。ControlToValidate プロパティが設定されてない場合は args.Value は空 "" になります。

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" 
    AutoEventWireup="true" CodeBehind="WebForm6.aspx.cs" 
    Inherits="WebApplication1.WebForm6" %>

<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" 
    runat="server">

    <script type="text/javascript">
        //<![CDATA[

        // ControlToValidate が設定されてない場合、引数 args には
        // テキストボックスの値が渡されないので注意。args に頼らず
        // 以下のようにしておくのがよさそう
        function TextBoxValidate(sender, args) {
            var tb =
                document.getElementById('<%= TextBox1.ClientID%>');

            var membership = tb.value.toLowerCase();
            if (membership === "gold" || membership === "silver") {
                args.IsValid = true;
            } else {
                args.IsValid = false;
            }
        }

        function CheckBoxValidate(sender, args) {
            var cb =
                document.getElementById('<%= CheckBox1.ClientID%>');

            if (cb.checked == true) {
                args.IsValid = false;
            } else {
                args.IsValid = true;
            }
        }

        function RadioButtonValidate(sender, args) {
            var rb =
                document.getElementById('<%= RadioButton1.ClientID%>');

            if (rb.checked == true) {
                args.IsValid = false;
            } else {
                args.IsValid = true;
            }
        }

        // ControlToValidate が設定されてない場合、引数 args には
        // テキストボックスの値が渡されないので注意。args に頼らず
        // 以下のようにしておくのがよさそう
        function DropDwonListValidate(sender, args) {
            var ddl =
                document.getElementById('<%= DropDownList1.ClientID%>');

           if (ddl.value == "2") {
                args.IsValid = false;
            } else {
                args.IsValid = true;
            }
        }

        function FileUploadValidate(sender, args) {
            if (window.File && window.FileList) {
                var fileUpload =
                    document.getElementById("<%=FileUpload1.ClientID%>");

                if (fileUpload.files[0] == null) {
                    args.IsValid = false;
                    return;
                }

                if (fileUpload.files[0].type != "image/jpeg") {
                    args.IsValid = false;
                    return;
                }


            } else {
                args.IsValid = true;
            }
            
        }

        //]]>
    </script>

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" 
    runat="server">

    <h1>CustomValidator</h1>
    <p>CustomValidator のクライアント側での検証のタイミング</p>

    <table>
        <tr>
            <td>
                TextBox
            </td>
            <td>
                <asp:TextBox ID="TextBox1" runat="server">
                </asp:TextBox>
            </td>
            <td>
                <asp:CustomValidator ID="CustomValidator1" 
                    runat="server" 
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="Gold または Silver でない" 
                    ClientValidationFunction="TextBoxValidate" 
                    ControlToValidate="TextBox1">
                </asp:CustomValidator>
            </td>
        </tr>
        <tr>
            <td>
                CheckBox
            </td>
            <td>
                <asp:CheckBox ID="CheckBox1" runat="server" />
            </td>
            <td>
                <%--ControlToValidate="CheckBox1" を設定すると
                    HttpException がスローされる。理由は CheckBox
                    には ValidationPropert 属性が付与されてない
                    から。なので submit しないと検証はかからない
                    --%>
                <asp:CustomValidator ID="CustomValidator2" 
                    runat="server"
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="チェック不可" 
                    ClientValidationFunction="CheckBoxValidate">
                </asp:CustomValidator>
            </td>
        </tr>
        <tr>
            <td>
                RadioButton
            </td>
            <td>
                <asp:RadioButton ID="RadioButton1" runat="server" />
            </td>
            <td>
                <%--ControlToValidate="RadioButton1" を設定すると
                    HttpException がスローされる。理由は RadioButton
                    には ValidationPropert 属性が付与されてないから。
                    なので submit しないと検証はかからない
                    --%>
                <asp:CustomValidator ID="CustomValidator3" 
                    runat="server"
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="選択不可" 
                    ClientValidationFunction="RadioButtonValidate">
                </asp:CustomValidator>
            </td>
        </tr>
        <tr>
            <td>
                DropDownList
            </td>
            <td>
                <asp:DropDownList ID="DropDownList1" runat="server">
                    <asp:ListItem>0</asp:ListItem>
                    <asp:ListItem>1</asp:ListItem>
                    <asp:ListItem>2</asp:ListItem>
                </asp:DropDownList>
            </td>
            <td>
                <asp:CustomValidator ID="CustomValidator4" 
                    runat="server"
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="2 は選択不可" 
                    ClientValidationFunction="DropDwonListValidate" 
                    ControlToValidate="DropDownList1">
                </asp:CustomValidator>
            </td>
        </tr>
        <tr>
            <td>
                FileUpload
            </td>
            <td>
                <asp:FileUpload ID="FileUpload1" runat="server" />
            </td>
            <td>
                <asp:CustomValidator ID="CustomValidator5" 
                    runat="server"
                    ForeColor="Red"
                    Display="Dynamic"
                    ErrorMessage="jpg ファイル以外不可" 
                    ClientValidationFunction="FileUploadValidate" 
                    ControlToValidate="FileUpload1">
                </asp:CustomValidator>
            </td>
        </tr>
    </table>
    <asp:Button ID="Button1" runat="server" Text="Submit" />

</asp:Content>

上のコードはマスターページを利用しています。先の記事「ASP.NET 4.5 ScriptManager」で書きましたように、ASP.NET 4.5 以降でクライアントスクリプトを利用するサーバーコントロールが正しく機能するには、必要なクライアントスクリプトの ScriptManager への登録と全ページでの ScriptManager の配置が必要です。マスターページを使ってそのあたりを解決しています。

Tags: , , ,

Validation

複数の CheckBox の状態を取得

by WebSurfer 2. July 2017 17:02

ASP.NET MVC アプリで、複数(数は不定)の CheckBox をレンダリングし、ユーザーがチェックを入れて POST したとき、どのチェックボックスがチェックされているかの状態を取得する方法を備忘録として書いておきます。

CheckBoxList

ユーザーがチェックを入れた CkeckBox からは true を、チェックしてない CheckBox からは false を取得できるようにするのが条件です。

ASP.NET Web Forms アプリですと CheckBoxList というサーバーコントロールがあって、それを使えば複数の CheckBox を実装するのは容易ですが、サーバーコントロールのない ASP.NET MVC アプリでは少々工夫が必要なようです。

html の input type="checkbox" ではチェックされた項目の name=value しか送ってこないのでかえって使いにくいです。Html.EditorFor を使うのがお勧めです。

List<bool> 型のオブジェクトをメンバーとして持つ Model を View に型付け、それから Html.EditorFor を使って CheckBox をレンダリングできます。

その CheckBox は html では input type="checkbox" だけでなく、それとペアで同じ name 属性を持つ隠しフィールド input type="hidden" value="false" も生成されます。(input type="hidden" を使うテクニックは MDN のドキュメント <input type="checkbox"> のメモ欄にも書かれています)

ユーザーがチェックを入れて POST すると、例えば name 属性が Item[0] だった場合、Items[0]=true&Items[0]=false というデータがフォームに含まれて送信されます。(チェックを入れない場合は隠しフィールドの Items[0]=false のみ)

ASP.NET は、モデルバインディングの際、それを見て List<bool> 型のオブジェクトの各要素を true / false に設定しているようです。

以下に、上の画像を表示したサンプルのコードとその説明を書いておきます。

Model と Controller

以下のサンプルではチェックされた項目は ture が、チェックされて無い項目は false がアクションメソッドの引数にバインドされるようにしています。(注:Model と Controller が一緒になっているのは単に分けるのが面倒だったからです)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Mvc4App.Models;

namespace Mvc4App.Controllers
{
  // Model
  public class CheckboxListModel
  {
    public CheckboxListModel()
    {
      Items = new List<bool>();
    }

    public IList<bool> Items { get; set; }
  }
    
  // Controller
  public class ComplexController : Controller
  {        
    [HttpGet]
    public ActionResult Checkboxes()
    {
      // この例では CheckBox を 5 つ作成。初期値は false とし
      // CheckBox はチェックされてない状態とする。
      bool[] defaultItems = 
          new bool[] { false, false, false, false, false };
      CheckboxListModel model = 
          new CheckboxListModel() { Items = defaultItems };
      return View(model);
    }

    [HttpPost]
    // POST されてきたデータは引数の m, items のいずれにもモデル
    // バインドされる。items の方は Model のプロパティ名と一致さ
    // せる必要がある。モデルバインディングの際は大文字・小文字
    // が区別されないので引数名には小文字を使ってプロパティ 
    // Items に以下のように代入できる。
    public ActionResult Checkboxes(CheckboxListModel m, 
                                        IList<bool> items)
    {
      CheckboxListModel model = 
          new CheckboxListModel() { Items = items };
      return View(model);
    }
  }
}

View

EditorFor の引数で m => m.Items[i] としているところがキモです。そうするとレンダリングされる html 要素の name 属性が連番のインデックスを含むようになり、モデルバインディングがうまくいきます。

@model Mvc4App.Controllers.CheckboxListModel

@{
    ViewBag.Title = "Checkboxes";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Checkboxes</h2>

@using (Html.BeginForm()) {
    for (int i = 0; i < Model.Items.Count; i++)
    {
        @Html.EditorFor(m => m.Items[i])
        <br />
    }
    <p>
        <input type="submit" value="Send" />
    </p> 
}

Tags:

MVC

Paging 機能付 GridView の行選択

by WebSurfer 19. December 2013 16:59

GridView に CheckBox を配置し、ユーザーに複数行を選択してもらうというシナリオはよくあると思います。この場合、GridView にページング機能が実装されていると問題です。

Paging 機能付 GridView の行選択結果取得

例えば、Page 1 でいくつかの行を選択して CheckBox にチェックを入れてから Page 2 に移動し、再び Page 1 に戻った場合、先に Page 1 で CheckBox に入れたはずのチェックが消えてしまいます。

この問題に対処するためには、以前のチェック情報を ViewState に保持しておき、ページが変わったら ViewState からそのページのチェック情報を取得して CheckBox.Checked プロパティを true に設定してやるというような操作が必要になります。

ページャーがクリックされるとポストバックが発生しますので、そのタイミングでクライアントスクリプトによってチェック結果を取得して隠しフィールドに格納し、それをサーバーに送信して ViewState に保存するようにしてみました。

CheckBox の ID には、GridView.DataKeys プロパティから主キー値を取得し、それを設定しています。それゆえ、クライアントスクリプトでもチェックされた CheckBox の name 属性から当該レコードの主キー値が分かります。

ただし、ID から name への名付けルールが変わるとうまく行かなくなる可能性がありますので、できれば主キー値を GridView に表示して、それから取得するようにした方がいいかもしれません。

以下のソースコードは主キー値を name 属性から取得する場合の例です。上の画像がソースコードの実行結果です。

注意事項はソースコード内のコメントに書きましたので、詳しくはそれを見てください。手抜きですみません。(汗)

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Collections.Generic" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  // CheckBox をクリックするたびポストバックしないで、ページン
  // グでポストバックした際に、まとめてチェック結果を取得し、
  // チェックされた行の主キー (ID) リストを更新する。

  // クライアント側で、input type="checkbox" 要素の id 属性また
  // は name 属性から、その要素のある行の主キー (ID) を取得する
  // ため、CheckBox.ID に主キー値を設定。主キー値が 17 の場合
  // id="GridView1_17_0" name="GridView1$ctl02$17" のようになる。
  // このサンプルでは、クライアントスクリプトで name 属性から
  // 主キー値を取得している。名前付けルールが変わってうまくいか
  // なくなる可能性があるので注意。
    
    
  // チェック入り行の主キー (ID) のリスト。ViewState に保持。    
  protected List<String> checkedIds;
    
  protected void Page_Load(object sender, EventArgs e)
  {
    // ViewState から ID リスト checkedIds を取得。
    if (checkedIds == null)
    {
      object obj = ViewState["CheckedIds"];
      if (obj != null)
      {
        checkedIds = (List<String>)obj;
      }
      else
      {
        // ViewState が未設定の場合は新たに初期化。
        checkedIds = new List<String>();
      }
    }

    // form を送信する際、CheckBox のチェック有無を調べて、
    // その結果をサーバーに送信するスクリプトを設定。
    String csname = "OnSubmitScript";
    Type cstype = this.GetType();
    ClientScriptManager cs = Page.ClientScript;
    if (!cs.IsOnSubmitStatementRegistered(cstype, csname))
    {
      String cstext = 
          "getCheckedIDs('" + GridView1.ClientID + "');";
      cs.RegisterOnSubmitStatement(cstype, csname, cstext);
    }
  }

  // ページャクリックの場合のイベント発生順序に注意:
  // 現ページの GridView.RowCreated ⇒ Page.Load ⇒ GridView.
  // PageIndexChanging ⇒ 次ページの GridView.RowCreated

  // ちなみに、ボタンクリックによるイベント発生順序は:
  // 現ページの GridView.RowCreated ⇒ Page.Load ⇒ 
  // Button.Click
    
  // CheckBox.ID を動的に設定するのを RowDataBound イベント
  // で行うのは NG。ボタンクリックではデータバインドが起こ
  // らないので、CheckBox.ID が設定されず(ASP.NET が勝手に
  // 独自の ID を生成する)、うまくいかない。
  protected void GridView1_RowCreated(object sender, 
        GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
      // ViewState から ID リスト checkedIds を取得。
      // ボタンクリックまたはページングによるポストバック
      // では Page.Load よりこちらのイベントが先に発生す
      // るのでここでも必要。
      if (checkedIds == null)
      {
        object obj = ViewState["CheckedIds"];
        if (obj != null)
        {
          checkedIds = (List<String>)obj;
        }
        else
        {
          // ViewState が未設定の場合は新たに初期化。
          checkedIds = new List<String>();
        }
      }
            
      // この行の id(主キー)値を取得。
      string id = ((GridView)sender).
          DataKeys[e.Row.RowIndex].Value.ToString();

      // e.Row.Cells[0] の中から CheckBox を探す。
      foreach (Control control in e.Row.Cells[0].Controls)
      {
        if (control is CheckBox)
        {
          CheckBox cb = (CheckBox)control;

          // CheckBox.ID に id を設定。クライアントスクリプト
          // で CheckBox の name 属性から id を取得できるよう
          // にするため。
          // このサンプルでは name は GridView1$ctl02$17 のよ
          // うになり、最後の 17 が id に該当。
          cb.ID = id;

          // id がリスト checkedIds にあればチェックを入れる。
          foreach (string checkedid in checkedIds)
          {
            if (checkedid == id)
            {
              cb.Checked = true;
              return;
            }
          }

          cb.Checked = false;
          break;
        }
      }
    }
  }

  // ページが変更される際にリスト checkedIds を更新する。
  protected void GridView1_PageIndexChanging(object sender, 
        GridViewPageEventArgs e)
  {
    // form の onsubmit イベントで、クライアントスクリプトが
    // CheckBox のチェック有無の情報を取得し、当該行の id を
    // 隠しフィールドに格納するので、それから id を取得する。
    char[] chars = new char[] { ',' };
    string s = Request.Form["__CHECKEDIDLIST"].Trim(chars);
    string[] check = s.Split(chars);
    s = Request.Form["__UNCHECKEDIDLIST"].Trim(chars);
    string[] uncheck = s.Split(chars);

    // 重複を避けるため、一旦、GridView 上のすべての ID を
    // リスト checkedIds から削除してから、
    foreach (string id in check)
    {
      checkedIds.Remove(id);
    }

    foreach (string id in uncheck)
    {
      checkedIds.Remove(id);
    }

    // チェック済の ID をリスト checkedIds に加える。
    foreach (string id in check)
    {
      if (!String.IsNullOrEmpty(id))
      {
        checkedIds.Add(id);
      }
    }

    // リスト checkedIds を ViewState に保持する。
    ViewState["CheckedIds"] = checkedIds;
  }
        
  // ボタンクリックでチェックされた全行の id を書き出す。
  protected void Button1_Click(object sender, EventArgs e)
  {
    // ボタンクリック直前のチェック結果をここで取得。
    char[] chars = new char[] { ',' };
    string s = Request.Form["__CHECKEDIDLIST"].Trim(chars);
    string[] check = s.Split(chars);
    s = Request.Form["__UNCHECKEDIDLIST"].Trim(chars);
    string[] uncheck = s.Split(chars);
        
    // チェック入り行の主キー (ID) リスト checkedIds を更新。
    foreach (string id in check)
    {
      checkedIds.Remove(id);
    }

    foreach (string id in uncheck)
    {
      checkedIds.Remove(id);
    }
        
    foreach (string id in check)
    {
      if (!String.IsNullOrEmpty(id))
      {
        checkedIds.Add(id);
      }
    }
        
    ViewState["CheckedIds"] = checkedIds;

    // リスト checkedIds の内容を書き出す。
    string str = "Selected Ids:";
    foreach (string id in checkedIds)
    {
      str += " " + id;
    }
    Label1.Text = str;
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title></title>
  <script src="Scripts/jquery-1.8.3.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
    // CheckBox のチェック有無を調べて、その結果を隠し
    // フィールドに設定するスクリプト。
    function getCheckedIDs(gridViewClientId) {
      var checked = "";
      var unchecked = "";

      $('#' + gridViewClientId + ' input:checkbox').each(
        function () {
          // このサンプルでは name は GridView1$ctl02$17 
          // のようになり、最後の 17 が id に該当。
          // ただし MasterPage を使うなど、名前付コンテナ
          // に GridView を入れると違ってくるので注意。
          var name = $(this).attr('name');
          var arrayOfString = name.split('$');
          var id = arrayOfString[arrayOfString.length - 1];
          if ($(this).attr('checked') == "checked") {
            checked += ',' + id;
          } else {
            unchecked += ',' + id;
          }
        });

      $('#__CHECKEDIDLIST').val(checked);
      $('#__UNCHECKEDIDLIST').val(unchecked);
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server">

  <%--チェック結果をサーバに送信する隠しフィールド。--%>
  <input type="hidden" 
    name="__CHECKEDIDLIST" 
    id="__CHECKEDIDLIST" 
    value="" />
  <input type="hidden" 
    name="__UNCHECKEDIDLIST" 
    id="__UNCHECKEDIDLIST" 
    value="" />

  <div>        
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand= "SELECT [ProductID], [ProductName] 
          FROM [Products] ORDER BY [ProductID]">
    </asp:SqlDataSource>
    <asp:GridView ID="GridView1" 
      runat="server" 
      AllowPaging="True" 
      AutoGenerateColumns="False" 
      DataKeyNames="ProductID" 
      DataSourceID="SqlDataSource1" 
      OnPageIndexChanging="GridView1_PageIndexChanging" 
      OnRowCreated="GridView1_RowCreated">
      <Columns>                
        <asp:TemplateField HeaderText="Select">                    
          <ItemTemplate>
            <asp:CheckBox runat="server" />
          </ItemTemplate>                    
        </asp:TemplateField>                
        <asp:BoundField DataField="ProductID" 
          HeaderText="ProductID" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" 
          HeaderText="ProductName" 
          SortExpression="ProductName" />
      </Columns>
    </asp:GridView>
    <asp:Button ID="Button1" 
      runat="server" 
      Text="Show Checked Ids" 
      OnClick="Button1_Click" />
    <asp:Label ID="Label1" runat="server"></asp:Label>
  </div>
  </form>
</body>
</html>

Tags: ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  July 2020  >>
MoTuWeThFrSaSu
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

View posts in large calendar