WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

DropDownList への SelectList の渡し方

by WebSurfer 25. February 2019 14:37

ASP.NET MVC5 の Html ヘルパーの DropDownList および DropDownListFor に表示するデータを、アクションメソッドからビューにどのように渡すかということについて書きます。

DropDownList

上の画像は、先の記事「スキャフォールディング機能」で書いた通りにスキャフォールディング機能を使って自動生成させたコードで、その中の Edit 画面を表示したものです。

SupplierID と CategoryID が Html ヘルパーの DropDownList を使ってドロップダウン形式で表示されるようになっています。上の画像は CategoryID を展開したところで、CategoryName の一覧が表示されています。

スキャフォールディング機能で自動生成されたコードが基本になるでしょうから、それがどうなっているかを書きます。

まずアクションメソッド Edit で SelectList オブジェクトを生成し ViewBag に設定しています。以下のコードの通りです。

public ActionResult Edit(int? id)
{
  NORTHWINDEntities db = new NORTHWINDEntities();
  Products products = db.Products.Find(id);

  ViewBag.CategoryID = 
    new SelectList(db.Categories, "CategoryID", 
                   "CategoryName", products.CategoryID);

  ViewBag.SupplierID = 
    new SelectList(db.Suppliers, "SupplierID", 
                   "CompanyName", products.SupplierID);

  return View(products);
}

コンストラクタに SelectList(IEnumerable, String, String, Object) を使って、第 4 引数に selectedValue を設定しているところに注目してください。これによりビューの DropDownList が html に変換された際、select 要素内の当該 option 要素に selected 属性が付与されます。

ビューの DropDownList のコードは以下のようになります。第 1 引数がアクションメソッドで設定した ViewBag のキー名、第 2 引数が null になっているところに注目してください。

@Html.DropDownList("SupplierID", null, 
        htmlAttributes: new { @class = "form-control" })

@Html.DropDownList("CategoryID", null, 
        htmlAttributes: new { @class = "form-control" })

DropDownList の第 2 引数が null となっていますが、第 2 引数の設定に関わらず ViewData / ViewBag から型が IEnumerable<SelectListItem> でキー名が第 1 引数と同じものを探してきます。

(例えば、上記のアクションメソッドで ViewBag.SupplierID の設定を削除すると、ビューの DropDownList のコードで「キー 'SupplierID' を持つ ViewData 項目の型は 'System.Int32' ですが、'IEnumerable<SelectListItem>' でなければなりません」というエラーになります)

DropDownList の第 1 引数を元に ViewBag で渡されたデータ(アクションメソッドで設定された SelectList オブジェクト)を取得するので、上の画像の通りドロップダウン形式で表示できるようになります。さらに、SelectList コンストラクタの第 4 引数に設定した selectedValue によって当該 option 要素に selected 属性が設定された結果が表示されます。

なお、DropDownList の第 2 引数を (SelectList)ViewBag.Supplier としたりすると、SelectList のコンストラクタで第 4 引数に設定した selectedValue が無視されるので注意してください。理由は不明です。

ViewData / ViewBag に DropDownList の第 1 引数と同じキー名がない場合は、DropDownList の第 2 引数の設定が有効になるようです。例えば、アクションメソッドで ViewBag.SupplierID を ViewBag.Supplier に変更した場合、DropDownList("SupplierID", (SelectList)ViewBag.Supplier, ...) として selected の設定を含めて期待した結果が得られます。

ViewData / ViewBag を探す順序ですが、検証してみると、まず最初に ViewData を、それになければ ViewBag を探すという結果になりました。ViewData / ViewBag に同じキー名があると、ViewData のデータが使われます。その際、もし ViewData のデータが不正ですと(IEnumerable<SelectListItem> 型でないと)エラーになります。

以上は DropDownListFor を使っても同様です。第 1 引数は model => model.SupplierID のようになりますが、プロパティ名 SupplierID から ViewData / ViewBag を探して設定してくれます。

Tags: , ,

MVC

DropDownList での NULL の処置

by WebSurfer 23. October 2015 17:19

下の画像のように、GridView などでデータベースの更新操作を行う際 DropDownList を利用してユーザー入力に便宜を図ることがあると思います。その時、NULL を DropDownList でどう対処するかという話を書きます。

GridView 上の 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 に対応します。

  1. SELECT クエリで INNER JOIN 句に替えて LEFT OUTER JOIN 句を使い CategoryID が NULL のレコードも抽出する。
  2. 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>

Tags: , ,

ASP.NET

GridView 上の DropDownList に ToolTip

by WebSurfer 2. November 2013 18:33
注意:
以下の記事の例では、Products テーブルの CategoryID が NULL の場合、および更新の際に NULL を入力する場合の対応は考えていません。NULL 対応は別の記事 DropDownList での NULL の処置 を見てください。

GridView などで更新操作を行う際、DropDownList を利用してユーザー入力に便宜を図ることがあります。その際、ツールチップを利用して DropDownList 上の各項目の説明を表示するという話です。

GridView 上の DropDownList に ToolTip 表示

ここで紹介する例には Microsoft が提供している Northwind サンプルデータベースの Products テーブルと Categories テーブルを使用しています。

Products テーブルの中の ProductName, CategoryID フィールドを GridView 上で更新する際、CategoryID の列に DropDownList を表示するようにします。

DropDownList には、ユーザーが見ても何だか分からない ID (CategoryID) を表示するのでははなくて、ユーザーが読んで理解できる名前 (CategoryName) を表示します。

さらに DropDownList を開いて項目一覧を表示し、それにマウスカーソルを当てると各項目の説明 (Description) をツールチップに表示するようにします。上の画像を見てください。

DropDownList が閉じている時に DropDownList にマウスカーソルを当てると、選択された項目の説明がツールチップに表示されるようにしています。

上の画像を表示したサンプルコードは以下の通りです。詳細はコメントに説明を書きましたので、それを参考にしてください。手抜きでスミマセン。(汗)

<%@ 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">

  // DropDownList には RowDataBound や ItemDataBound に該当す
  // るイベントがないので、各項目にツールチップを追加するには
  // 以下のような方法を取らざるを得ない。
  // 
  // GridView の[編集]ボタンがクリックされるとポストバック
  // され、当該行内に DropDownList がレンダリングされる。その
  // 際に DropDownList.DataBound イベントが発生するので、そこ
  // で DropDownList およびその中の ListItem にツールチップを
  // 設定する。
  protected void DropDownList1_DataBound(object sender, 
        EventArgs e)
  {
      DropDownList ddl = (DropDownList)sender;

      // 再度 DB にクエリを投げてデータを取得。(他に方法が
      // 見つからない)
      DataView dv = (DataView)SqlDataSource2.
            Select(DataSourceSelectArguments.Empty);

      foreach (ListItem item in ddl.Items)
      {
          dv.RowFilter = 
                String.Format("CategoryID='{0}'", item.Value);
          string description = (string)dv[0]["Description"];
            
          // ListItem には ToolTip プロパティはないので、直接
          // title 属性に description を設定する。
          item.Attributes["title"] = description;

          // DropDownList の ToolTip には、選択されている項目の
          // description を設定する。
          if (item.Selected)
          {
              ddl.ToolTip = description;
          }
      }

      // ユーザーが DropDownList 中の項目をクリックして選択され
      // ている項目を変更した場合、それに応じて DropDownList の
      // ToolTip を書き換えるためのスクリプトを追加        
      string csname1 = "jQuery1.8.3";
      string csurl = "~/Scripts/jquery-1.8.3.js";
      string csname2 = "ChangeDropDownListToolTipScript";
      Type cstype = this.GetType();
      ClientScriptManager cs = Page.ClientScript;

      // jQuery を利用するので参照を追加。
      if (!cs.IsClientScriptIncludeRegistered(cstype, csname1))
      {
          cs.RegisterClientScriptInclude(
                cstype, csname1, ResolveClientUrl(csurl));
      }

      // DropDownList の ToolTip 書き換え用スクリプト追加。
      if (!cs.IsClientScriptBlockRegistered(cstype, csname2))
      {
          StringBuilder cstext = new StringBuilder();
          cstext.Append("$(function () {");
          cstext.Append("$('#" + ddl.ClientID + "').change(");
          cstext.Append("function () {");
          cstext.Append("$(this).attr('title', $('#" +
                ddl.ClientID +
                " option:selected').attr('title'));");
          cstext.Append("});");
          cstext.Append("});");
          cs.RegisterClientScriptBlock(
              cstype, csname2, cstext.ToString(), true);
      }
  }
</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:Northwind %>" 
      SelectCommand=
        "SELECT TOP 10 
          p.[ProductID], p.[ProductName], 
          p.[CategoryID], c.[CategoryName] 
        FROM [Products] AS p 
        INNER 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:Northwind %>" 
      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") %>' 
              OnDataBound="DropDownList1_DataBound">
            </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>

Tags: , ,

ASP.NET

About this blog

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

Calendar

<<  December 2019  >>
MoTuWeThFrSaSu
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar