WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

DetailsView 中の連動 DropDownList

by WebSurfer 4. December 2010 16:23

GridView や DetailsView などのデータバインドコントロールを使ってレコードの更新や挿入を行う場合、データを DropDownList の一覧から選択してもらうことがあります。

DetailsView 中の連動 DropDownList

このとき、DropDownList の SelectedValue プロパティに '<%# Bind("xxx") %>' のようにデータバインドメソッドを設定すると、最初に更新画面を表示するときはオリジナルのデータで DropDownList のアイテムが選択されて表示され、異なるアイテムを選択して更新をかけるとそのアイテムの Value でデータベースを更新できます。

ただし、2 つの DropDownList を配置して、それを連動させる場合は以下のような問題があり、注意が必要です。

例えば、上の画像に示すように、DetailsView に 2 つの DropDownList を配置し、1 つめの DropDownList で飲み物というカテゴリーを選択すると、2 つめの DropDownList には飲み物に分類される商品が絞り込まれて表示されるケースを考えます。

2 つの DropDownList の SelectedValue プロパティに、それぞれ '<%# Bind("CategoryID") %>' および '<%# Bind("ProductID") %>' というようなデータバインドメソッドが設定してあるとします。

ReadOnly モードで表示された状態から[編集]ボタンをクリックして Edit モードに移った直後は、DropDownList に正しくアイテムが選択されて表示されますが、1 つめの DropDownList のアイテムの選択を変更すると、"Eval()、XPath()、および Bind() のようなデータバインド メソッドは、データバインドされたコントロールのコンテキストでのみ使用することができます。" というエラーになります。

(ArgumentOutOfRangeException で "項目一覧に存在しないため、'DropDownList2' に SelectedValue を指定することは無効です。" になるのかと思いましたがそうではなかったです。詳細は不明ですが、Page.GetDataItem メソッドでデータ バインディングコンテキストを取得できないのが原因らしいです。)

エラーを回避するには、2 つめの DropDownList の SelectedValue='<%# Bind("ProductID") %>' を削除すればよいです。

ただし、削除すると、オリジナルのデータが DropDownList で選択・表示されず、かつ、データベースの更新ができなくなります。

その問題の解決策は、DetailsView の DataBound イベントハンドラで DropDownList の SelectedValue を設定すること、および、SqlDataSource の Updating イベントハンドラで SqlParameter に直接 SelectedValue を代入することです。

以下に、DetailsView に 2 つの DropDownList を配置し、それらを連動させた場合のサンプルコードをアップしておきます。Products と Categories テーブルは Northwind サンプルデータベースのものです。Sales テーブルは自作でスキーマは以下の通りです。

CREATE TABLE [dbo].[Sales](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [CategoryID] [int] NOT NULL,
  [ProductID] [int] NOT NULL,
 CONSTRAINT [PK_Sales] PRIMARY KEY CLUSTERED 
(
  [ID] ASC
)WITH (PAD_INDEX  = OFF ・・・中略・・・)
)

aspx ページのコードは以下の通りです。

<%@ 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">
  protected void DetailsView1_DataBound(object sender, EventArgs e)
  {
    // DropDownList2 の SelectedValue プロパテ
    // ィに '<%# Bind("ProductID") %>' を設定
    // しない代わりに以下のようにする。

    if (DetailsView1.CurrentMode == DetailsViewMode.Edit)
    {
      DropDownList ddl =
        (DropDownList)DetailsView1.FindControl("DropDownList2");
      ddl.SelectedValue =
        ((DataRowView)DetailsView1.DataItem)["ProductID"].ToString();
    }
        
    // Insert モードでは DetailsView1.DataItem が
    // null なので SelectedValue の設定はできない。
    // と言うより、そもそも Insert モードでは設定
    // する意味はない。
  }
    
  protected void SqlDataSource1_Updating(object sender, 
    SqlDataSourceCommandEventArgs e)
  {
    // SelectedValue='<%# Bind("ProductID") %>'
    // としていないので、以下のようにパラメータ
    // に直接 SelectedValue を代入する。
    DropDownList ddl = 
      (DropDownList)DetailsView1.FindControl("DropDownList2");
    e.Command.Parameters["@ProductID"].Value = ddl.SelectedValue;
  }

  protected void SqlDataSource1_Inserting(object sender, 
    SqlDataSourceCommandEventArgs e)
  {
    DropDownList ddl =
      (DropDownList)DetailsView1.FindControl("DropDownList3");
    e.Command.Parameters["@CategoryID"].Value = ddl.SelectedValue;
    ddl = (DropDownList)DetailsView1.FindControl("DropDownList4");
    e.Command.Parameters["@ProductID"].Value = ddl.SelectedValue;
  }
</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:Northwind %>" 
      DeleteCommand="DELETE FROM [Sales] WHERE ([ID] = @ID)" 
      InsertCommand="INSERT INTO [Sales] ([CategoryID], [ProductID]) 
        VALUES (@CategoryID, @ProductID)" 
      SelectCommand= "SELECT s.ID, s.CategoryID, s.ProductID, c.CategoryName, p.ProductName 
        FROM Sales AS s 
        INNER JOIN Categories AS c ON s.CategoryID = c.CategoryID 
        INNER JOIN Products AS p ON s.ProductID = p.ProductID" 
      UpdateCommand="UPDATE [Sales] 
        SET [CategoryID] = @CategoryID, [ProductID] = @ProductID 
        WHERE ([ID] = @ID)" 
      OnUpdating="SqlDataSource1_Updating" 
      OnInserting="SqlDataSource1_Inserting">
      <DeleteParameters>
        <asp:Parameter Name="ID" Type="Int32" />
      </DeleteParameters>
      <InsertParameters>
        <asp:Parameter Name="CategoryID" Type="Int32" />
        <asp:Parameter Name="ProductID" Type="Int32" />
      </InsertParameters>
      <UpdateParameters>
        <asp:Parameter Name="CategoryID" Type="Int32" />
        <asp:Parameter Name="ProductID" Type="Int32" />
        <asp:Parameter Name="ID" Type="Int32" />
      </UpdateParameters>
    </asp:SqlDataSource>
    <asp:DetailsView ID="DetailsView1" 
      runat="server" AllowPaging="True" 
      AutoGenerateRows="False" 
      DataKeyNames="ID" 
      DataSourceID="SqlDataSource1" 
      EnableModelValidation="True" 
      OnDataBound="DetailsView1_DataBound">
      <Fields>
        <asp:BoundField DataField="ID" 
          HeaderText="ID" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="ID" />

        <asp:TemplateField HeaderText="CategoryName" 
          SortExpression="CategoryName">
          <EditItemTemplate>
            <asp:SqlDataSource ID="SqlDataSource2" 
              runat="server" 
              ConnectionString="<%$ ConnectionStrings:Northwind %>" 
              SelectCommand="SELECT [CategoryID], [CategoryName] 
                FROM [Categories]">
            </asp:SqlDataSource>
            <asp:DropDownList ID="DropDownList1" 
              runat="server" 
              DataSourceID="SqlDataSource2" 
              DataTextField="CategoryName" 
              DataValueField="CategoryID" 
              AutoPostBack="True"
              SelectedValue='<%# Bind("CategoryID") %>'>
            </asp:DropDownList>
          </EditItemTemplate>
          <InsertItemTemplate>
            <asp:SqlDataSource ID="SqlDataSource4" 
              runat="server" 
              ConnectionString="<%$ ConnectionStrings:Northwind %>" 
              SelectCommand="SELECT [CategoryID], [CategoryName] 
                FROM [Categories]">
            </asp:SqlDataSource>
            <asp:DropDownList ID="DropDownList3" 
              runat="server" 
              DataSourceID="SqlDataSource4" 
              DataTextField="CategoryName" 
              DataValueField="CategoryID" 
              AutoPostBack="True">
            </asp:DropDownList>
          </InsertItemTemplate>
          <ItemTemplate>
            <asp:Label ID="Label1" 
              runat="server" 
              Text='<%# Bind("CategoryName") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="ProductName" 
          SortExpression="ProductName">
          <EditItemTemplate>
            <asp:SqlDataSource ID="SqlDataSource3" 
              runat="server" 
              ConnectionString="<%$ ConnectionStrings:Northwind %>" 
              SelectCommand="SELECT [ProductID], [ProductName] 
                FROM [Products] 
                WHERE ([CategoryID] = @CategoryID)">
              <SelectParameters>
                <asp:ControlParameter 
                  ControlID="DropDownList1" 
                  Name="CategoryID" 
                  PropertyName="SelectedValue" 
                  Type="Int32" />
              </SelectParameters>
            </asp:SqlDataSource>

            <%-- SelectedValue='<%# Bind("ProductID") %>' 
                 と設定するのは NG --%>
            <asp:DropDownList ID="DropDownList2" 
              runat="server" 
              DataSourceID="SqlDataSource3" 
              DataTextField="ProductName" 
              DataValueField="ProductID">
            </asp:DropDownList>
          </EditItemTemplate>
          <InsertItemTemplate>
            <asp:SqlDataSource ID="SqlDataSource5" 
              runat="server" 
              ConnectionString="<%$ ConnectionStrings:Northwind %>" 
              SelectCommand="SELECT [ProductID], [ProductName] 
                FROM [Products] 
                WHERE ([CategoryID] = @CategoryID)">
              <SelectParameters>
                <asp:ControlParameter 
                  ControlID="DropDownList3" 
                  Name="CategoryID" 
                  PropertyName="SelectedValue" 
                  Type="Int32" />
              </SelectParameters>
            </asp:SqlDataSource>
            <asp:DropDownList ID="DropDownList4" 
              runat="server" 
              DataSourceID="SqlDataSource5" 
              DataTextField="ProductName" 
              DataValueField="ProductID">
            </asp:DropDownList>
          </InsertItemTemplate>
          <ItemTemplate>
            <asp:Label ID="Label2" 
              runat="server" 
              Text='<%# Bind("ProductName") %>'>
            </asp:Label>
          </ItemTemplate>
        </asp:TemplateField>
        <asp:CommandField 
          ShowDeleteButton="True" 
          ShowEditButton="True" 
          ShowInsertButton="True" />
      </Fields>
    </asp:DetailsView>
  </div>
  </form>
</body>
</html>

Tags: ,

ASP.NET

About this blog

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

Calendar

<<  October 2020  >>
MoTuWeThFrSaSu
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar