WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

GridView で INSERT 操作

by WebSurfer 2013年10月30日 17:27

GridView で INSERT 操作(データベースにレコードを追加)する方法の紹介です。そもそも、GridView にはデータベースにレコードを追加する機能はありませんが、そこを無理やりその機能を追加するという話です。

GridView で INSERT 操作

参考にさせていただいたのは GridView からデータを追加する です。参考にしたと言うより、アイデアはほぼそのままコピーさせていただいたのですが。(汗)

データベースにレコードを追加するには、TextBox などのユーザー入力のためのコントロールを GridView のどこかに配置し、ポストバックで送信されてきた値を処置する必要があります。

ユーザー入力のためのコントロールを GridView のどこに配置できるかと言えば、フッター以外に適当な場所はないです。上の画像を見てください。フッター(一番下の行)に[追加]という LinkButton と空白の TextBox が 3 つ配置されているのが分かると思います。

フッターにコントロールを配置するには、デザイン画面の GridView のテンプレートの編集メニューで、各列の FooterTemplate を編集します。

コマンドを送る[追加]ボタンの CommandName プロパティを "Insert" に設定するのがポイントです。そうしておけば、[追加]ボタンがクリックされたことを、GridView.RowCommand イベントのハンドラで判断して処置ができます。

GridView.ShowFooter プロパティを true に設定するのを忘れないようにしてください(デフォルトでは false です)。

この状態で、[追加]ボタンをクリックするとポストバックが発生し、ユーザーが TextBox に入力した値がサーバーに送信されます。

GridView 内のボタンクリックでポストバックされると、サーバー側では GridView.RowCommand イベントが発生しますので、そのイベントハンドラでレコードの追加処置を行います。

具体的には、クリックされたのが[追加]ボタンかどうかを CommandName プロパティから判断し、SqlDataSource の InsertParameters(InsertCommand プロパティで使用されるパラメータを格納するパラメータ コレクション)の内容を、ユーザー入力の TextBox をバインド先に設定した ControlParameter に書き換えます。その後、SqlDataSource.Insert メソッドで挿入操作を実行します。

ここで、ポイントは、ControlParameter コンストラクタの第三引数(パラメータのバインド先のコントロールの名前)に UniqueID を使うことです。そうしないとコントロールが見つからないというエラーになります。

ポストバックで、ユーザーが TextBox(html では <input type="text"... />)に入力した値がサーバーに送信される際、key(name 属性の値)と value(value 属性の値)がペアで送信されますが、この key に UniqueID が使用されますのでそれを利用します。

サーバー側では HttpRequest.Form.AllKeys で key のコレクションが取得できます。コレクションの中の key を一つ一つ調べて、key の値に当該コントロールの ID が含まれていれば、その key の値を ControlParameter コンストラクタの第三引数に使用します。

それが下のサンプルの中の GridView1_RowCommand メソッドのコードです。ここまでで、レコードが一行でも存在すればフッターが表示されますので、レコードの追加を行うことができます。

しかし、レコードが一行も存在しない場合が問題です。何故なら、その場合はフッターも表示されないからです。

その対応としては、レコードが一行も存在しない場合は EmptyDataTemplate が表示されますので、その中に LinkButton や TextBox を配置して使用します。

その際のポイントは、LinkButton の CommandName プロパティを "Insert" に設定するのに加えて、TextBox の ID をフッターに配置したものと同一にしておくことです。

そうすれば、GridView.RowCommand イベントのハンドラ(GridView1_RowCommand メソッド)には一切手を加えることなく、フッターに配置した LinkButton と TextBox と同様に、レコードの追加処置ができます。

具体的には以下のサンプルコードを見てください。

<%@ 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">
   
  // ControlParameter コンストラクタの第三引数(パラメータの
  // バインド先のコントロールの名前)に UniqueID を使わないと
  // コントロールが見つからないというエラーになる。
  protected void GridView1_RowCommand(object sender, 
    GridViewCommandEventArgs e)
  {
    if (e.CommandName == "Insert")
    {
      SqlDataSource1.InsertParameters.Clear();
            
      foreach (string key in Request.Form.AllKeys)
      {
        if (key.Contains("TextBox4"))
        {
          SqlDataSource1.InsertParameters.Add(
                        new ControlParameter(
                            "name", 
                            TypeCode.String, 
                            key, 
                            "Text"));
        }
        if (key.Contains("TextBox5"))
        {
          SqlDataSource1.InsertParameters.Add(
                        new ControlParameter(
                            "price", 
                            TypeCode.Decimal, 
                            key, 
                            "Text"));
        }
        if (key.Contains("TextBox6"))
        {
          SqlDataSource1.InsertParameters.Add(
                        new ControlParameter(
                            "memo", 
                            TypeCode.String, 
                            key, 
                            "Text"));
        }
      }
            
      SqlDataSource1.Insert();
    } 
  }

</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:MyDB %>" 
      DeleteCommand="DELETE FROM [Table] WHERE [id] = @id" 
      InsertCommand="INSERT INTO [Table] ([name], [price], [memo]) 
          VALUES (@name, @price, @memo)" 
      SelectCommand="SELECT [id], [name], [price], [memo] 
          FROM [Table]" 
      UpdateCommand="UPDATE [Table] 
          SET [name] = @name, [price] = @price, [memo] = @memo 
          WHERE [id] = @id">
      <DeleteParameters>
        <asp:Parameter Name="id" Type="Int32" />
      </DeleteParameters>
      <InsertParameters>
        <asp:Parameter Name="name" Type="String" />
        <asp:Parameter Name="price" Type="Decimal" />
        <asp:Parameter Name="memo" Type="String" />
      </InsertParameters>
      <UpdateParameters>
        <asp:Parameter Name="name" Type="String" />
        <asp:Parameter Name="price" Type="Decimal" />
        <asp:Parameter Name="memo" Type="String" />
        <asp:Parameter Name="id" Type="Int32" />
      </UpdateParameters>
    </asp:SqlDataSource>

    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="id" 
      DataSourceID="SqlDataSource1" 
      ShowFooter="True" 
      OnRowCommand="GridView1_RowCommand">
      <Columns>
        <asp:TemplateField ShowHeader="False">
          <EditItemTemplate>
            <asp:LinkButton ID="LinkButton1" 
              runat="server" 
              CausesValidation="True" 
              CommandName="Update" 
              Text="更新">
            </asp:LinkButton>
             
            <asp:LinkButton ID="LinkButton2" 
              runat="server" 
              CausesValidation="False" 
              CommandName="Cancel" 
              Text="キャンセル">
            </asp:LinkButton>
          </EditItemTemplate>
          <FooterTemplate>
            <asp:LinkButton ID="LinkButton3" 
              runat="server" 
              CommandName="Insert">
              追加
            </asp:LinkButton>
          </FooterTemplate>
          <ItemTemplate>
            <asp:LinkButton ID="LinkButton1" 
              runat="server" 
              CausesValidation="False" 
              CommandName="Edit" 
              Text="編集">
            </asp:LinkButton>
             
            <asp:LinkButton ID="LinkButton2" 
              runat="server" 
              CausesValidation="False" 
              CommandName="Delete" 
              Text="削除">
            </asp:LinkButton>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="id" 
          InsertVisible="False" 
          SortExpression="id">
          <EditItemTemplate>
            <asp:Label ID="Label1" 
              runat="server" 
              Text='<%# Eval("id") %>'>
            </asp:Label>
          </EditItemTemplate>
          <ItemTemplate>
            <asp:Label ID="Label4" 
              runat="server" 
              Text='<%# Bind("id") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="name" 
          SortExpression="name">
          <EditItemTemplate>
            <asp:TextBox ID="TextBox1" 
              runat="server" 
              Text='<%# Bind("name") %>'>
            </asp:TextBox>
          </EditItemTemplate>
          <FooterTemplate>
            <asp:TextBox ID="TextBox4" 
              runat="server">
            </asp:TextBox>
          </FooterTemplate>
          <ItemTemplate>
            <asp:Label ID="Label1" 
              runat="server" 
              Text='<%# Bind("name") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="price" 
          SortExpression="price">
          <EditItemTemplate>
            <asp:TextBox ID="TextBox2" 
              runat="server" 
              Text='<%# Bind("price") %>'>
            </asp:TextBox>
          </EditItemTemplate>
          <FooterTemplate>
            <asp:TextBox ID="TextBox5" 
              runat="server">
            </asp:TextBox>
          </FooterTemplate>
          <ItemTemplate>
            <asp:Label ID="Label2" 
              runat="server" 
              Text='<%# Bind("price") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="memo" 
          SortExpression="memo">
          <EditItemTemplate>
            <asp:TextBox ID="TextBox3" 
              runat="server" 
              Text='<%# Bind("memo") %>'>
            </asp:TextBox>
          </EditItemTemplate>
          <FooterTemplate>
            <asp:TextBox ID="TextBox6" 
              runat="server">
            </asp:TextBox>
          </FooterTemplate>
          <ItemTemplate>
            <asp:Label ID="Label3" 
              runat="server" 
              Text='<%# Bind("memo") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>
      </Columns>

      <EmptyDataTemplate>
        <table style="width:100%;">
          <tr>
            <th> </th>
            <th>id</th>
            <th>name</th>
            <th>price</th>
            <th>memo</th>
          </tr>
          <tr>
            <td>
              <asp:LinkButton ID="LinkButton3" 
                runat="server" 
                CommandName="Insert">
                追加
              </asp:LinkButton>
            </td>
            <td>
               
            </td>
            <td>
              <asp:TextBox ID="TextBox4" 
                runat="server">
              </asp:TextBox>
            </td>
            <td>
              <asp:TextBox ID="TextBox5" 
                runat="server">
              </asp:TextBox>
            </td>
            <td>
              <asp:TextBox ID="TextBox6" 
                runat="server">
              </asp:TextBox>
            </td>
          </tr>
        </table>
      </EmptyDataTemplate>
    </asp:GridView>
  </div>
  </form>
</body>
</html>

Tags:

ASP.NET

コンパイル済みアセンブリの保存場所

by WebSurfer 2013年9月30日 14:13

ASP.NET ベースの web アプリケーションで、コンパイル済みのアセンブリが HDD のどこに保存されるかについて、調べたことを備忘録として書いておきます。

コンパイルの方法も保存場所も、Web サイトプロジェクトか Web アプリケーションプロジェクトのどちらで作っているかによって違ってきます。

Web サイトプロジェクト

Web サイトプロジェクトでは、ソースファイルをそのまま Web サーバー (IIS) の仮想ディレクトリに配置します。(プリコンパイルをしない場合です)

ユーザーが Web サイトの ASP.NET ページ (.aspx ファイル) などのリソースを最初に要求したときに、ページやコードファイルが Web サーバー (IIS) によって動的にコンパイルされます。

コンパイルされたコードは Temporary ASP.NET Files フォルダーにキャッシュされ、同じページに対するその後の要求ではキャッシュのコンパイル済みコードを使用するので、効率良く処理されます。

その詳細については、MSDN ライブラリ ASP.NET の動的コンパイルの概要 を参照してください。

Web アプリケーションプロジェクト

Web アプリケーションプロジェクトの場合は、Visual Studio を使用してプロジェクトの Bin フォルダーに、単一のアセンブリを、手動で作成します。(Web サイトプロジェクトのように Web サーバーによって自動的にコンパイルされない)

Web サイトプロジェクトと Web アプリケーションプロジェクトとの違いについては、MSDN ライブラリ Visual Studio での Web アプリケーション プロジェクトと Web サイト プロジェクト を見てください。

そのページにも書いてありますが、日本語訳がイマイチなので、原文の Compilation のセクションの説明を以下に抜粋します。

"For web application projects, you typically build the project in Visual Studio or by using the ASP.NET batch compiler on a computer that is not the production IIS server. All code-behind class files and standalone class files in the project are compiled into a single assembly, which is then put in the web application project's Bin folder. (The .aspx and .ascx files are compiled dynamically in a manner similar to what is done for web site projects.)"

つまり、コードビハンド(.aspx.cs)や独立したクラスファイルは、Visual Studio(または ASP.NET バッチ コンパイラ)を使用して、手動で単一アセンブリにコンパイルして、アプリケーションルート直下の Bin フォルダに配置します。運用環境では、その単一アセンブリを Web サーバーにコピーして使います。

一方、.aspx と .ascx(ページディレクティブと HTML デザインブロック)は、Web サイトプロジェクトと同様に、Web サーバーで動的にアセンブリにコンパイルされ、Temporary ASP.NET Files フォルダに保存されます。

コードの継承関係を見れば分かると思いますが、Page ← .aspx.cs ← .aspx となっており(矢印の方向が継承元)、実行時に両方メモリーにロードされ、合体して動作するようになっています。

Tags: ,

ASP.NET | ASP.NET

GridView 内の RadioButton

by WebSurfer 2013年8月30日 14:02

GridView の 1 行のみを選択する目的で、GridView の各行に RadioButton を配置する場合の注意点を備忘録として書いておきます。

GridView 内の RadioButton

あるラジオボタンのグループで、単一のラジオボタンしか選択できないようにするには、そのグループの中のラジオボタンの name 属性の値を同一にします。

ところが GridView に 配置した RadioButton の場合、サーバー側のコードで name 属性が同一になるように設定しても、それからレンダリングされる input 要素の name 属性の値は ASP.NET によって書き換えられ、全て異なった値になります。結果、ラジオボタンなのに複数の行が選択できてしまいます。

理由は以下の通りです。

名前付けコンテナー(INamingContainer インターフェイスを実装するコントロール)内にコントロールが配置されていると、ASP.NET は名前付けコンテナーの ID をコントロールの ID に追加して ClientID 値を生成します。

GridView の場合、その個別の行をあらわす GridViewRow が名前付けコンテナーであり、それから HTML の繰り返しブロックがレンダリングされますが、その中に配置された RadioButton には連続番号を含むプレフィックスが追加され、ページの中で一意になる(同じ ID が無い)ように ClientID 値が生成されます。

RadioButton の場合は name 属性も生成され、その値もページの中で一意になるよう、名前付けコンテナーの ID がプレフィックスとして追加されます。(ClientID とは命名規則が異なりますが、ページ内で一意になる点は同じです)

例えば、今回の記事の例の場合、GridView 内のラジオボタンの name は GridView1$ctl02$RadioButton1 というようになり、その中で ctl02 の部分が行によって異なります。

ASP.NET が name の値を生成するタイミングは、PreRender イベントが完了した後、html コードをレンダリングする時のようです。従って、それ以前にサーバー側で name 属性をいかように設定しても、ブラウザに送信される html コードは名前付けコンテナーの ID が追加されたものに書き換えられてしまいます。

なので、RadioButton.GroupName プロパティを設定するとか、GridView の RowCreated や PreRender イベントなどのハンドラで全ての RadioButton の name 属性を同一に書き換えるなどしても効果はありません。

何か方法はないか探してみましたが、サーバー側で設定する方法は見つけられませんでした。

結局、ブラウザが html コードの読み込みを完了した後、クライアントスクリプトで当該ラジオボタンの name 属性を同一に書き換えてやることで対応しました。

以下のサンプルコードのような感じです。jQuery を利用すると比較的簡単にできます。ただし、当然ですが、ブラウザで JavaScript が無効になっているとダメです。

自分が見つけてない、サーバー側のコードで対応できる、もっと簡単な方法があるかもしれません。

2013/8/31 追記:
愚直に name 属性の値を書き換えるより簡単な方法がありました。詳しくはこの記事の下の方の「------ 2013/8/31 追記 ------」を見てください。また。サーバー側だけで対応する方法も書いておきました。

<%@ 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 Button1_Click(object sender, EventArgs e)
  {
    string ids = "";
    for (int i = 0; i < GridView1.Rows.Count; i++)
    {
      GridViewRow row = GridView1.Rows[i];
      RadioButton rb = 
        (RadioButton)row.FindControl("RadioButton1");
      if (rb != null)
      {
        if (rb.Checked == true)
        {
          ids += GridView1.DataKeys[i].Value.ToString() + " ";
        }
      }
    }
    Label1.Text = "Setected product(s): " + ids;
  }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <script src="Scripts/jquery-1.8.3.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
    // サーバー側で設定されたラジオボタンの name 属性
    // のオリジナル値を保存しておくための配列。
    var originalNames;

    // ラジオボタンの name 属性の元の値を配列に保存した
    // 上で、同一(この例では radiobutton1)に書き換え。
    $(function () {
      originalNames = new Array();
      $('#<%=GridView1.ClientID%> input:radio').each(
        function () {
          originalNames.push($(this).attr('name'));
          $(this).attr('name', 'radiobutton1');
        });
    });

    // ポストバックする前に、ラジオボタンの name 属性を
    // オリジナル値に戻すためのメソッド。オリジナル値に
    // 戻さないとチェックされたラジオボタンをサーバー側
    // で取得できず、かつ、再描画されたときにチェックマ
    // ークがつかない。
    // この例では Button1.OnClientClick プロパティにこの
    // メソッドを設定している。
    function RetrieveOriginalNames() {
      $('#<%=GridView1.ClientID%> input:radio').each(
        function (n) {
          $(this).attr('name', originalNames[n]);
        });
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice] 
        FROM [Products] 
        WHERE ([CategoryID] = @CategoryID)">
      <SelectParameters>
        <asp:Parameter DefaultValue="1" 
          Name="CategoryID" Type="Int32" />
      </SelectParameters>
    </asp:SqlDataSource>

    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="ProductID" 
      DataSourceID="SqlDataSource1">
      <Columns>
        <asp:TemplateField HeaderText="選択">
          <ItemTemplate>
            <asp:RadioButton ID="RadioButton1"
              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" />
        <asp:BoundField DataField="UnitPrice" 
          HeaderText="UnitPrice" 
          SortExpression="UnitPrice" />
      </Columns>
    </asp:GridView>

    <asp:Button ID="Button1" runat="server" 
      Text="Button" 
      OnClick="Button1_Click" 
      OnClientClick="RetrieveOriginalNames();" />
    <br />
    <asp:Label ID="Label1" runat="server"></asp:Label>
  </div>
  </form>
</body>
</html>

------ 2013/8/31 追記 ------

name 属性は書き換えないでそのままにしておいて、あるラジオボタンをチェックしたら他のラジオボタンのチェックを外すようにする方が簡単でした。以下のような感じです。

<script src="Scripts/jquery-1.8.3.js" type="text/javascript">
</script>
<script type="text/javascript">
//<![CDATA[
  // ラジオボタンがチェックされたら、一旦全てのラジオ
  // ボタンのチェックを外し、チェックされたラジオボタ
  // ンに改めてチェックを入れ直す方がはるかに簡単。
  $(function () {
    $('#<%=GridView1.ClientID%> input:radio').change(
      function () {
        $('#<%=GridView1.ClientID%> input:radio').
          removeAttr("checked");
        $(this).attr('checked', 'checked');
      });
  });

  // Button1 クリックで name を元の値に書き戻す必要は
  // なくなる。
//]]>
</script>

その他、クライアントスクリプトを使わないでサーバー側だけで対応する方法として、RadioButton を継承したカスタムコントロールを作る方法があります。その例は以下のページを見てください。

データバインドコントロール内で使用できるカスタムラジオボタンの作成

DataGrid内のラジオボタンでグループに出来ない問題の回避方法

Tags: ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar