WebSurfer's Home

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

DataGridView に ComboBox を表示

by WebSurfer 2014年1月23日 15:26

DataGridView の特定の列を ComboBox を使って表示する方法を備忘録として書いておきます。

DataGridView に ComboBox を表示

Northwind サンプルデータベースの Products テーブルを例に使い、上の画像のように Category 列と Supplier 列を ComboBox を使って表示します。

Products テーブルを DataGridView に表示して更新操作ができるようにするだけなら、自分でコードは一行も書かなくても、Visual Studio のウィザードを使ってドラッグ&ドロップ操作だけで Windows Form アプリを作成できます。

ただし、Products テーブルの中の CategoryID, SupplierID は ID(数字)で表示されるので、そのままではユーザーにとっては分かりにくいです。

ID に代えて、名前を Categories, Suppliers テーブルから取得して表示した方がユーザーにとっては分かりやすいはずです。

さらにユーザーがデーターベースを編集・更新する場合は、ComboBox を使用してドロップダウン形式で一覧を表示し、その中からユーザーが選択できるようにするとさらにユーザーフレンドリーになると思います。

今回の例では、CategoryID, SupplierID 列に ComboBox を使い、各行の CategoryID, SupplierID に該当する名前(CategoryName, CompanyName)を Categories, Suppliers テーブルから取得して ComboBox に表示するようにします。

Category 列の方は Visual Studio のウィザードを利用して、自力では一行もコードを書かずに ComboBox を実装してみます。

Supplier 列の方は自力でコードを書いて DataTable を作成し、それを ComboBox の DataSource に設定することにします。

以下にその手順を書きます。

  1. Visual Studio の[ファイル(F)]⇒[新しいプロジェクト(P)...]メニューから[Windows フォームアプリケーション]テンプレートを使って空のプロジェクトを作成する。
  2. ソリューションエクスプローラーでプロジェクトのルートを右クリック。[追加(D)]⇒[新しい項目(W)...]⇒[データセット]で適当な名前(例えば NorthwindDataSet.xsd)をつけて空の xsd ファイルを作成。(注:下の画像はステップ 12 まで進んだ時の完成済みのデータセットで、このステップの時点では何も表示されていません)

NorthwindDataSet.xsd ファイル

  1. ツールボックスから TableAdapter をドラッグ&ドロップすると TableAdapter 構成ウィザードが立ち上がる。
  2. クエリビルダで以下の SELECT クエリを作り、これをベースに DataTable + TableAdapter を作る。

    SELECT ProductID, ProductName, CategoryID, SupplierID,
    UnitPrice, Discontinued FROM Products

    DataGridView に表示するとともに編集・更新操作を行うのはこれなので、TableAdapter 内に作成する更新系のメソッド等の生成オプションはフルにチェックを入れておく。
  3. TableAdapter 構成ウィザードでの操作が完了すると、ProductsDataTable と ProductsTableAdapter が自動生成されているはず。(上の NorthwindDataSet.xsd ファイルの画像で、左側の箱)
  4. Form1.cs を開きそのデザイン画面を表示する。データソースウィンドウを開くとその中に Products という項目があるはずなので、それを Form にドラッグ&ドロップする。
  5. 上のステップまでで、Products テーブルの表示、編集、更新ができるアプリが完成するはず。とりあえずこの段階で DataGridView への表示、レコードの INSERT, DELETE, UPDATE が問題なくできることを確認しておく。
  6. 上記が確認できたら、最初に Category の方を ComboBox で表示するための設定を行う。
  7. 自力でコードを書くのは必要最小限にするため、TableAdapter 構成ウィザードを利用して DataTable + TableAdapter を作り、これを利用する。
  8. NorthwindDataSet.xsd ファイルを開き、ツールボックスからもう一つ TableAdapter をドラッグ&ドロップする。
  9. TableAdapter 構成ウィザードが立ち上がるので、クエリビルダで以下の SELECT クエリを作り、これをベースに DataTable + TableAdapter を作成する。

    SELECT CategoryID, CategoryName FROM Categories

    こちらは、CategoryID に該当する CategoryName を ComboBox を使って表示するためのデーターソースとして利用するだけなので、TableAdapter に更新系のメソッドは不要。
  10. TableAdapter 構成ウィザードでの操作が完了すると、CategoriesDataTable と CategoriesTableAdapter が自動生成されているはず。(上の NorthwindDataSet.xsd ファイルの画像で、右側の箱)
  11. DataGridView の列の編集画面で、CatrgoryID 列の ColumnType を DataGridViewComboBoxColumn に変更する。

列の編集画面その1

  1. 上の画面で、DataSource を[他のデータソース]⇒[プロジェクトデータソース]⇒[NorthwindDataSet]⇒[Catrgories]に設定すると、自動的に categoriesBindingSource が生成され、DataSource に設定される。
  2. 次に、DisplayMember を CategoryName に、ValueMember を CategoryID に設定する。

列の編集画面その2

  1. ここまでの操作で、Category の方は ComboBox で表示され、編集する際は ComboBox をドロップダウンしてその中から選択して更新をかけることが可能になるはず。
  2. 次に、Supplier の方を ComboBox に表示するための設定を行う。こちらは TableAdapter 構成ウィザードを使用しないで、自力でコードを書いて DataTable を作り、それをデーターソースに利用してみる。
  3. Category の場合と同様に、DataGridView の列の編集画面で、SupplierID 列の ColumnType を DataGridViewComboBoxColumn に変更する。
  4. その先は自力でコードを書いて DataTable を作成し、それを DataGridViewComboBoxColumn の DataSource に設定する。
  5. Form1.cs のコード画面を開く。
  6. ADO.NET のライブラリを使い、Suppliers テーブルから SupplierID, CompanyName を取得し DataTable を作成するコードを追加する。具体例は以下のサンプルコードの CreateSuppliersTable を参照。
  7. Form1_Load メソッド(自動生成されているはず)の中に、上記 18 の手順で作成した DataGridViewComboBoxColumn の DataSource プロパティを CreateSuppliersTable メソッドが返す DataTable に、DisplayMember プロパティを "CompanyName" に、ValueMember プロパティを "SupplierID" に設定する。具体例は以下のサンプルコードを参照。
  8. 以上の操作で、一番上の画像のアプリが完成するはずです。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;  // 要追加

namespace DataGridViewComboBox
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void productsBindingNavigatorSaveItem_Click(
        object sender, EventArgs e)
    {
      this.Validate();
      this.productsBindingSource.EndEdit();
      this.tableAdapterManager.UpdateAll(
          this.northwindDataSet);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      this.categoriesTableAdapter.Fill(
          this.northwindDataSet1.Categories);
      this.productsTableAdapter.Fill(
          this.northwindDataSet.Products);
      // ここまでのコードは Visual Studio のウィザードで
      // 自動生成される。


      // ここから下のコードを自力で書いて追加する。

      // DataSource に設定するのは Supplier の ComboBox に
      // 表示するデータを含む DataTable。下のヘルパーメソッ
      // ド CreateSuppliersTable で作成。
      this.dataGridViewTextBoxColumn4.DataSource = 
          CreateSuppliersTable();
      this.dataGridViewTextBoxColumn4.DisplayMember = 
          "CompanyName";
      this.dataGridViewTextBoxColumn4.ValueMember = 
          "SupplierID";
    }

    // Suppliers テーブルから SupplierID, CompanyName を抽出
    // して DataTable を作るためのヘルパーメソッド。
    private DataTable CreateSuppliersTable()
    {
      DataTable table = new DataTable("Suppliers");
      table.Columns.Add(
            new DataColumn("SupplierID", typeof(int)));
      table.Columns.Add(
            new DataColumn("CompanyName", typeof(string)));

      string connString = DataGridViewComboBox.Properties.
          Settings.Default.NORTHWNDConnectionString;
      string query = 
          "SELECT SupplierID, CompanyName FROM Suppliers " + 
          "ORDER BY SupplierID";

      using (SqlConnection conn = new SqlConnection(connString))
      {
        conn.Open();
        using (SqlCommand cmd = new SqlCommand(query, conn))
        {
          using (SqlDataReader reader = cmd.ExecuteReader())
          {
            if (reader != null)
            {
              while (reader.Read())
              {
                DataRow row = table.NewRow();
                row["SupplierID"] = reader.GetInt32(0);
                row["CompanyName"] = reader.GetString(1);
                table.Rows.Add(row);
              }
            }
          }
        }
      }
      return table;
    }

  }
}

Tags: , ,

.NET Framework

ASP.NET の ID オブジェクト

by WebSurfer 2014年1月20日 16:36

ASP.NET Web Forms アプリケーションの Page 内では、以下の 3 つのプロパティ/メソッドで ID オブジェクト(WindowsIdentity または GenericIdentity)を取得できます。

それぞれどのように違うかを調べましたので、備忘録として書いておきます。


HttpRequest.LogonUserIdentity

現在 IIS が HTTP 要求を受けているユーザーの WindowsIdentity(Windows ユーザーを表す)オブジェクトを取得します。

匿名アクセス(Page.User.Identity.IsAuthenticated が false)の場合は常に IUSR となります。IUSR とは、匿名認証が有効化されている場合は常に IIS によって使用される既定の ID です。

ログイン済みユーザー(Page.User.Identity.IsAuthenticated が true)の場合は認証方式によって異なり、以下の通りとなります。

  • フォーム認証:ワーカープロセス(例:IIS7 では NETWORK SERVICE)
  • Windows 認証:ログインユーザーの Windows アカウント

ASP.NET 偽装の有効・無効には影響を受けず、常に上記の通りとなります。


WindowsIdentity.GetCurrent

ASP.NET 偽装が無効になっている場合は、ワーカープロセスを実行しているアカウント(IIS7 では NETWORK SERVICE)の WindowsIdentity オブジェクトを取得します。

ASP.NET 偽装が有効になっている場合は、偽装の設定方法、認証方式、匿名アクセスかログイン済みかによって、取得される WindowsIdentity オブジェクトは異なります(「偽装」しているだけで、実際にワーカープロセスを実行しているアカウントは変わらないのかもしれません・・・未確認です)。

web.config での偽装の設定を、単純に <identity impersonate="true" /> とした場合には以下のようになります。

フォーム認証

  • 匿名アクセス:IUSR
  • ログイン済み:ワーカープロセス(例:IIS7 では NETWORK SERVICE)

Windows 認証

  • 匿名アクセス:IUSR
  • ログイン済み:ログインユーザーの Windows アカウント

上記の偽装の設定に userName 属性、password 属性の設定を追加し、特定の Windows ユーザーアカウントを偽装した場合は、認証方式や認証済みか否かに関わらず、常に userName 属性に設定したユーザーアカウントになります。


Page.User.Identity

現在ページ要求を行っているユーザーを表す ID オブジェクトを取得します。

Page.User プロパティは、Windows 認証が有効になっている場合は WindowsPrincipal を、Windows 認証が無効の場合は RolePrincipal(ロール メンバシップを含む現在の HTTP 要求のセキュリティ情報)を取得します。

そして、WindowsPrincipal.Identity からは WindowsIdentity オブジェクトが、RolePrincipal.Identity からは GenericIdentity(標準ユーザーを表す)が取得されます。

ただし、それらの中身は ASP.NET が書き換えており、特に Name は、匿名アクセスの場合は IUSR ではなく空文字になること、フォーム認証のログイン済みユーザーの場合は Windows アカウント名ではなくフォーム認証のユーザー ID となる点に注意してください。

具体的には、IsAuthenticated, AuthenticationType, Name プロパティは以下の通りとなります。

フォーム認証、匿名アクセス

  • IsAuthenticated: false
  • AuthenticationType:(空文字)
  • Name:(空文字)

フォーム認証、ログイン済み

  • IsAuthenticated: true
  • AuthenticationType: Forms
  • Name: フォーム認証のユーザー ID

Windows 認証、匿名アクセス

  • IsAuthenticated: false
  • AuthenticationType:(空文字)
  • Name:(空文字)

Windows 認証、ログイン済み

  • IsAuthenticated: true
  • AuthenticationType: Negotiate
  • Name: ログインユーザーの Windows アカウント

Tags:

ASP.NET

Validator の Display="Dynamic" 時の注意点

by WebSurfer 2014年1月16日 17:45

ASP.NET の検証コントロール(RequiredFieldValidator, RegularExpressionValidator など)を使用して、Display プロパティを Dynamic に設定したときの注意点です。

Validator の Display="Dynamic" 時の注意点

この件は stackoverflow の記事 でも報告されています。ただ、実は自分はつい最近までこの問題は知らなかったです。(汗)

例えば、以下のように RequiredFieldValidator を配置して、その直後に Button を配置したとします。

<%@ 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)
  {
    if (Page.IsValid)
    {
      // 何らかの処置。
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
      runat="server" 
      ErrorMessage="エラーメッセージ"  
      ControlToValidate="TextBox1" 
      Display="Dynamic">
    </asp:RequiredFieldValidator>
    <asp:Button ID="Button1" 
      runat="server" 
      Text="Button" 
      OnClick="Button1_Click" />                
  </div>
  </form>
</body>
</html>

上記のコードでは以下の手順で問題を再現できます。

  1. TextBox は空のまま Button をクリックする。
  2. クライアント側で検証がかかり、ErrorMessage プロパティに設定した「エラーメッセージ」が表示される。
  3. TextBox に文字を入力して Button をクリックする。 ⇒ 「エラーメッセージ」は消えるがポストバックがかからない。
  4. 再度 Button をクリックする。 ⇒ ポストバックがかかる。

期待される動きは、上記 3 で「エラーメッセージ」が消えるとともにポストバックがかかるということのはずですが、そうはなりません。

上記のコードにおける解決策は、Button の直前に改行 ( <br /> ) を入れることです。そうしないとうまく行かない理由は以下の通りです。

Validator のエラーメッセージは html コードでは span 要素となり、JavaScript による検証結果により表示/非表示を切り替えています。

Display プロパティが Dynamic に設定されている場合は、当該 span 要素の style 属性を "display:none;" または "display:inline;" に設定することにより表示/非常時を切り替えます。

検証対象の TextBox にフォーカスを当ててからフォーカスを外す(例えば、TextBox に入力してから form を submit するために Button をクリックする)と、 そのタイミングで JavaScript による検証がかかるようになっています。

検証結果によって "display:inline;" が "display:none;" に(またはその逆に)書き換えられるので、エラーメッセージの部分のページレイアウトが変わることになります。

従って、上記のコードのように、RequiredFieldValidator の直後に Button が配置されているような場合、エラーメッセージが表示/非表示になる分だけ画面上でボタンが左右に移動します。

ボタンが移動すると、<input type="submit" ... /> タイプのボタンをクリックしたにもかかわらず form が submit されません。 即ち、ポストバックされないという期待に反する動作になります。

なお Button が動くのは左右でなくても、例えばエラーメッセージを p 要素に入れると上下に移動しますが、その場合でも同じくform は submit されません。

このことは、ASP.NET の検証コントロールを使った場合に限った話ではなく、html 要素と JavaScript だけでも再現できます。 以下のサンプルコードは、html 要素と JavaScript だけでこの問題(ボタンが動くと submit されない)を再現する例です。

サンプルコードを実際に動かして試すことができるよう 実験室 にアップしました。興味のある方は試してみてください。

<%@ 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 runat="server">
  <title>WebSurfer's Page - 実験室</title>
  <script src="/scripts/jquery-1.8.3.min.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
    function toggleDisplay() {
      var validator = $('#RequiredFieldValidator1');
      if (validator.css("display") == "none") {
        validator.css("display", "inline");
      } else {
        validator.css("display", "none");
      }            
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server" 
    onsubmit="javascript:return confirm('Submit しますか?');">

  <div>
    <input name="textbox1" type="text" id="1extbox1" 
      onblur="javascript: toggleDisplay();" />
    <span id="RequiredFieldValidator1" 
      style="display:none;">エラーメッセージ</span>
    <input type="submit" name="button1" value="POST" 
      id="button1" />    
  </div>
  </form>
</body>
</html>

ちなみに、Display プロパティが Static に設定されている場合は、style 属性を "visibility:hidden;" または "visibility:visible" に設定するので、 表示/非表示を切り替えてもページのレイアウトは変わりません。結果、ボタンは動かないのでこの問題は起こりません。

Tags: ,

Validation

About this blog

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

Calendar

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

View posts in large calendar