[PRB] コントロール値を変更していないにもかかわらず Change イベントが発生する

現象

特定の状況において、コントロールを変更していないにもかかわらず、ASP.NET Web コントロールの Change イベントが発生します。

原因

DropDownList コントロールの SelectedIndexChanged イベントや TextBox コントロールの TextChanged イベントなど、一部のコントロールは、Change イベントの登録があるかどうかに応じてビューステートを使用します。この問題は、イベントのフック後にこれらのコントロールを動的に作成したときに発生します。


ASP.NET がブラウザにページを表示するとき、動的に作成されたコントロールには関連する Change イベントがありません。イベントが存在しないため、ASP.NET はビューステートを保存しません。これを "ビューステートの最適化" と呼びます。


ページがポストバックされてイベントがフックされると、ASP.NET はビューステートを探しますが、ビューステートは存在しません。この相違によって、コントロールが変更されていないにもかかわらず Change イベントが発生します。

解決方法

ASP.NET がページを表示する前に、動的に作成されたコントロールの Change イベントをフックするようにします。これにより、コントロールのビューステートが存在することが保証されます。

状況

この動作は仕様です。

詳細

現象の再現手順

Web フォームの作成

  1. 新しい Visual Basic ASP.NET アプリケーションを ChangeFired という名前で作成します。
  2. デザイナ モードで WebForm1 を開きます。DataList コントロール、Button コントロール、および DataSet コントロールを 1 つずつフォームにドラッグします。
  3. [データセットの追加] ダイアログ ボックスで [型指定のないデータセット] をクリックし、[OK] をクリックします。コントロールはすべてデフォルト名のままにしておきます。
  4. DataSet1 の Tables コレクションを編集します。Table1 という名前の新しいテーブルを追加します。
  5. Table1 の Columns コレクションを編集します。Column1 という名前の新しい列を追加します。
  6. [DataList1] をクリックし、DataSource プロパティを DataSet1 に変更します。
  7. [DataList1] を右クリックし、[テンプレートの編集] をポイントして、[項目テンプレート] をクリックします。DropDownList コントロールを ItemTemplate セクションにドラッグします。
  8. [DropDownList1] をクリックし、Items コレクションに 5 つのアイテムを追加します。これらのアイテムの Text プロパティと Value プロパティをそれぞれ、one、two、three、four、および five に設定します。
  9. DropDownList1 の AutoPostBack プロパティを true に設定します。
  10. DropDownList1 の DataBindings コレクションを編集します。SelectedIndex プロパティに次のようなカスタム連結式を追加します。
    DataBinder.Eval(Container, "DataItem.Column1")
  11. HTML ビューに切り替えます。@ Page ディレクティブの trace 属性を次のように true に設定します。
    <%@ Page Language="vb" AutoEventWireup="false" Codebehind="WebForm1.aspx.vb" 
    Inherits="ChangeFired.WebForm1" trace="true" %>

コード ビハインド ファイルの追加

  1. WebForm1.aspx を右クリックし、[コードの表示] をクリックします。
  2. 次のコードを Page_Load イベントに追加します。
    DataSet1.Tables(0).Rows.Add(New Object() {"1"})
    DataSet1.Tables(0).Rows.Add(New Object() {"2"})
    DataSet1.Tables(0).Rows.Add(New Object() {"3"})
    DataSet1.Tables(0).Rows.Add(New Object() {"4"})

    If Not IsPostBack Then
    DataBind()
    End If

    Dim objItem As DataListItem
    For Each objItem In DataList1.Items
    AddHandler CType(objItem.FindControl("DropDownList1"), _
    DropDownList).SelectedIndexChanged, AddressOf DropDownList1_SelectedIndexChanged
    If Not objItem.FindControl("DropDownList1") Is Nothing Then
    Trace.Write("Added SelectedIndexChanged Handler")
    End If
    Next
  3. 以下に示す、SelectedIndexChanged イベントを処理する新しい Sub プロシージャを Page_Load イベントの下に作成します。
    Private Sub DropDownList1_SelectedIndexChanged(ByVal sender As System.Object, _
    ByVal e As System.EventArgs)
    DataBind()
    Trace.Write("SelectedIndexChanged, DataBind()")

    'Uncomment the following code to resolve this problem.
    'Dim objItem As DataListItem
    'For Each objItem In DataList1.Items
    ' AddHandler CType(objItem.FindControl("DropDownList1"), _
    ' DropDownList).SelectedIndexChanged, AddressOf DropDownList1_SelectedIndexChanged
    'Next
    End Sub

サンプルの実行

  1. ページを実行します。コントロール ツリーのトレース情報で、動的に作成された DropDownList コントロールにビューステートがあることを確認します。


    これは、DataBind メソッドへの呼び出しによって DropDownList コントロールが動的に作成され、Page_Load イベントの DataBind 呼び出し後にイベント ハンドラをフックしたために発生します。コントロールにイベント ハンドラがあるため、ビューステートの最適化は実行されません。
  2. ページのボタンをクリックします。ポストバックが発生し、Page_Load イベントの中のコードのみが実行されることを確認します。イベント ハンドラがコントロールに関連付けられているため、DropDownList コントロールのビューステートが存在します。
  3. DropDownList コントロールのいずれかの値を変更します。ポストバックが発生することを確認してください。また、ASP.NET によって Page_Load イベントが発生し、DropDownList1_SelectedIndexChanged イベントが実行されることを確認します。
  4. DropDownList1_SelectedIndexChanged イベントで、再度 DataBind メソッドを呼び出して新しい DropDownList コントロールを作成します。


    これらの新しい DropDownList コントロールには SelectedIndexChanged イベントがフックされていないため、ASP.NET はビューステートの最適化を使用し、新しい DropDownList コントロールのビューステートを保存しません。
  5. コントロールのツリーのトレース情報を確認します。DropDownList コントロールにビューステートが存在しないことを確認してください。
  6. ページのボタンをクリックします。ポストバックが発生し、ASP.NET によって Page_Load イベントが実行されることを確認してください。SelectedIndexChanged イベントを DropDownList コントロールにフックしたため、ASP.NET はビューステートがコントロールに関連付けられているものと見なします。


    しかし、ビューステートが存在しないため、アイテムを選択していなくても ASP.NET によって SelectedIndexChanged イベントが発生します。

解決方法

この問題を解決するには、DropDownList1_SelectedIndexChanged イベントのコードからコメントを外して、新しく作成されたコントロールにイベント ハンドラがフックするようにします。
プロパティ

文書番号:314809 - 最終更新日: 2008/07/14 - リビジョン: 1

フィードバック