DOBON.NET DOBON.NETプログラミング掲示板過去ログ

SelectedIndexChangedが2回呼ばれる

環境/言語:[ASP.NET 2.0  VisualStudio2005(VB)  SqlServer2008]
分類:[ASP.NET]

皆様、いつもご参考にさせて頂いております。

ロード時にVB.NET側からSQL(SELECT) を実施し、
ASP側のasp:GridViewへ出力しているのですが、
そこで出力した情報GridView内のキャンセルボタンを設定しVB.NET側でキャンセル更新処理を実施する。
といった流れをとっているのですが、gvCancelResult_SelectedIndexChangedが2回呼ばれます。

・VB.NET側の処理がなぜか2回呼ばれませす。
上記の点に関してご教示頂ければと思い
書き込みをいたしました。宜しくお願い致します。

1)画面表示には、エラー表示内容は表示されず
2)GridViewには2行表示され、2行目をキャンセル処理実施
3)GridViewからキャンセルボタンをクリックし、
 対象データをDB更新・登録処理は正常に行われている。


【エラー内容】

【ASP情報】
<asp:GridView ID="gvCancelResult" runat="server" BackColor="White" 
BorderColor="#336666" BorderStyle="Double" BorderWidth="3px" CellPadding="4" 
GridLines="Horizontal" Width="100%" AutoGenerateColumns="False" 
DataSourceID="dsOrders" AllowPaging="True" 
Font-Size="Small" OnSelectedIndexChanged="gvCancelResult_SelectedIndexChanged">

<EmptyDataTemplate >※何も表示されない場合は発注前です。</EmptyDataTemplate>
<PagerSettings Position="TopAndBottom" />
<RowStyle BackColor="White" ForeColor="#333333" />           
<Columns>
    <asp:TemplateField HeaderText="行">
    <ItemTemplate><%#Container.DataItemIndex + 1%></ItemTemplate>
    </asp:TemplateField>
    <asp:BoundField DataField="明細番号" HeaderText="明細番号" SortExpression="明細番号" />
    <asp:BoundField DataField="商品CD" HeaderText="商品CD" SortExpression="商品CD" />
    <asp:BoundField DataField="商品名" HeaderText="商品名" SortExpression="商品名" />
    <asp:BoundField DataField="発注数" HeaderText="発注数" SortExpression="発注数" />
    <asp:BoundField DataField="入荷日" HeaderText="入荷日" SortExpression="入荷日" />
    <asp:BoundField DataField="入荷文言" HeaderText="入荷文言" SortExpression="入荷文言" />
    <asp:BoundField DataField="完了情報" HeaderText="完了情報" SortExpression="完了情報" />
    <asp:BoundField DataField="出荷情報" HeaderText="出荷情報" SortExpression="出荷情報" />
    <asp:BoundField DataField="キャンセル情報" HeaderText="キャンセル情報" SortExpression="キャンセル情報" />
    <asp:BoundField DataField="FAX不可情報" HeaderText="FAX不可情報" SortExpression="FAX不可情報" />
    <asp:ButtonField CommandName="Select" HeaderText="キャンセル" ShowHeader="True" 
        Text="キャンセル" />
</Columns>
<FooterStyle BackColor="White" ForeColor="#333333" />
<PagerStyle BackColor="#336666" ForeColor="White" HorizontalAlign="Center" />
<SelectedRowStyle BackColor="#339966" Font-Bold="True" ForeColor="White" />
<HeaderStyle BackColor="#336666" Font-Bold="True" ForeColor="White" />
</asp:GridView>

【VB.NET情報】

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

     ''初期化
    LabelMsg.Text = ""
    LabelErrMsg.Text = ""
    LabelInputErrorMsg.Text = ""

    If IsPostBack = False Then
        ''引継情報
        Dim OrderId As String = Request.QueryString("orderId")
        ''キャンセルデータ情報検索(主:order_headerTBL)
        PrepareSearchCancelDataSource(OrderId)
    End If

End Sub

''受注情報検索(主:viewNojimaNoukiTBL) 抜粋
Protected Sub PrepareSearchCancelDataSource(ByVal orderId As String)

    Dim arguments As DataSourceSelectArguments
    Dim dtView As DataView
    Dim sSql As String = ""
    Dim errOrderDetail As String = "発注キャンセル情報取得エラー"

    Try
        arguments = New DataSourceSelectArguments()

        sSql = ""
        sSql = sSql & " SELECT" & vbCrLf
        sSql = sSql & "     vnn.order_sub_id AS 明細番号," & vbCrLf
        sSql = sSql & "     vnn.product_cd AS 商品CD," & vbCrLf
        sSql = sSql & "     vnn.product_name AS 商品名," & vbCrLf
        sSql = sSql & "     vnn.hac_qty AS 発注数," & vbCrLf
        sSql = sSql & "     vnn.nyukaYoteiDate AS 入荷日," & vbCrLf
        sSql = sSql & "     vnn.nyukaYoteiText AS 入荷文言," & vbCrLf
        sSql = sSql & "     CASE vnn.kanryoFlg" & vbCrLf
        sSql = sSql & "         WHEN '1' THEN '生産完了'" & vbCrLf
        sSql = sSql & "         ELSE ''" & vbCrLf
        sSql = sSql & "     END AS 完了情報," & vbCrLf
        sSql = sSql & "     CASE WHEN vnn.shipping_date" & vbCrLf
        sSql = sSql & "          IS NOT NULL THEN '出荷済'" & vbCrLf
        sSql = sSql & "         ELSE ''" & vbCrLf
        sSql = sSql & "     END AS 出荷情報," & vbCrLf
        sSql = sSql & "     CASE WHEN vnn.orderCancelDate" & vbCrLf
        sSql = sSql & "          IS NOT NULL THEN '登録済'" & vbCrLf
        sSql = sSql & "         ELSE ''" & vbCrLf
        sSql = sSql & "     END AS キャンセル情報," & vbCrLf
        sSql = sSql & "     CASE ns.cancelFlg" & vbCrLf
        sSql = sSql & "         WHEN '1' THEN 'FAX不可'" & vbCrLf
        sSql = sSql & "         ELSE ''" & vbCrLf
        sSql = sSql & "     END AS FAX不可情報" & vbCrLf
        sSql = sSql & " FROM" & vbCrLf
        sSql = sSql & "     XXXXXXXXXX vnn(nolock)," & vbCrLf
        sSql = sSql & "     WWWWWWWWW ns(nolock)" & vbCrLf
        sSql = sSql & " WHERE" & vbCrLf
        sSql = sSql & "     vnn.torisk_code = ns.supplierCd And " & vbCrLf
        sSql = sSql & "     order_id = '" & orderId & "'" & vbCrLf
        sSql = sSql & " " & vbCrLf

        Me.dsOrders.SelectCommand = sSql
        dtView = CType(Me.dsOrders.Select(arguments), DataView)

        If dtView.Count = 0 Then
            LabelErrMsg.Text = errOrderDetail & "<br/>"
        Else
            gvCancelResult.DataBind()
        End If

    Catch ex As Exception
        LabelErrMsg.Text = "エラーが発生。処理を中止しました。<br />" & _
            ex.Message & "<br />" & vbCrLf

    End Try

End Sub


''キャンセルボタン処理(DB更新・登録) 抜粋
Protected Sub gvCancelResult_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles gvCancelResult.SelectedIndexChanged

    ''明細番号取得
    Dim row As GridViewRow = gvCancelResult.SelectedRow

    ''キャンセル処理(DB更新)
    If rsUpdateCancelInfo(strRowId, strProCd).Trim = "" Then
        ''登録完了メッセージ表示
        LabelErrMsg.Text = "発注キャンセル登録完了しました。<br />" & _
                           "発注キャンセル確認画面でご確認ください。" & "<br />" & vbCrLf

        ''最新情報取得
        PrepareSearchCancelDataSource(OrderId)

        Exit Sub
    Else
        ''エラーの場合
        Exit Sub
    End If

以上、ご教示、アドバイスの程宜しくお願い致します。
イベントを制御するようなフラグで操作するのが一般的なのでしょうか?
パブリック宣言して、イベント起動にFalseに変更する
''制御フラグ
Dim eventFlg As Boolean = True

''Trueの時のみ
If eventFlg = True Then
 ''Falseに変更
 eventFlg = False
 …
 …
 目的の処理を実施
 …
 …
End IF
■No28011に返信(はちまきさんの記事)
> 皆様、いつもご参考にさせて頂いております。
>
> ロード時にVB.NET側からSQL(SELECT) を実施し、
> ASP側のasp:GridViewへ出力しているのですが、
> そこで出力した情報GridView内のキャンセルボタンを設定しVB.NET側でキャンセル更新処理を実施する。
> といった流れをとっているのですが、gvCancelResult_SelectedIndexChangedが2回呼ばれます。
>
> ・VB.NET側の処理がなぜか2回呼ばれませす。
> 上記の点に関してご教示頂ければと思い
> 書き込みをいたしました。宜しくお願い致します。
>
> 1)画面表示には、エラー表示内容は表示されず
> 2)GridViewには2行表示され、2行目をキャンセル処理実施
> 3)GridViewからキャンセルボタンをクリックし、
>  対象データをDB更新・登録処理は正常に行われている。
■No28012 に返信(はちまきさんの記事)
> そこで出力した情報GridView内のキャンセルボタンを設定しVB.NET側でキャンセル更新処理を実施する。
> といった流れをとっているのですが、gvCancelResult_SelectedIndexChangedが2回呼ばれます。

GridView の選択ボタンを押下すると GridView.SelectedIndexChanged イベントが2回発生
するということですか?
うーむ、見当がつきません。GridView.SelectedIndexChanged イベントが2回発生することは
どのように確認しましたか?

> イベントを制御するようなフラグで操作するのが一般的なのでしょうか?

GridView.SelectedIndexChanged イベントは2回発生しないのが一般的だと思います。
SelectedIndexChanged で処理してる意味と、キャンセルボタンとの関係が
良くわからないんだけど。。。
''明細番号取得 の row と strRowId は無関係?私が WEB のプログラムあまり
触ってないからかもしれませんが、何を処理しているのか理解できませんでした。
違う行にキャンセルボタンがあるのですか?
1回呼ばれるのは問題ないのですか?

普通に前の行の選択解除で発生しているだけではないのかな?
2回のイベントで、SelectedRow は何から何に変更されていますか?

> GridView.SelectedIndexChanged イベントは2回発生しないのが一般的だと思います。
GridView だとそういう動きなんですか?ローカルのコントロール(ListView, DataGridView)
では選択解除があるので、選択行を変更したのであれば2回発生する必要があると思いますが。
■No28012に返信(はちまきさんの記事)

> gvCancelResult.DataBind()
SelectedIndexChangedでこれが動くのでSelectedIndexChangedが
2回発生しているのだと思います。
こういう処理をされるのならフラグを使って処理する方法で良いかと思います。
2011/01/17(Mon) 13:34:16 編集(投稿者)

■No28025に返信(るしぇさんの記事)

私への返信ということでよいのですかね。

> SelectedIndexChanged で処理してる意味と、キャンセルボタンとの関係が
> 良くわからないんだけど。。。

<asp:GridView 
  ...
  OnSelectedIndexChanged="gvCancelResult_SelectedIndexChanged">

<asp:ButtonField CommandName="Select" HeaderText="キャンセル" ShowHeader="True" Text="キャンセル" />

ご掲示いただいたコードを見る限り、選択ボタンの Text プロパティにキャンセルを設定しています。
これをキャンセルボタンと称されているのではないかと思います。
キャンセルボタンは選択ボタンであり、GridView.SelectedIndexChanged イベントは GridView 
の選択ボタンを押下したときに発生するイベントです。
キャンセルボタンを押下したときに SelectedIndexChanged イベントが発生するという関係かと
思います。

> ''明細番号取得 の row と strRowId は無関係?私が WEB のプログラムあまり
> 触ってないからかもしれませんが、何を処理しているのか理解できませんでした。

コードは gvCancelResult_SelectedIndexChanged が2回呼ばれることと関係ありそうなところを
抜粋されているようなので、すべてを理解するのは無理かと思われます。文章を読む限りでは
キャンセルボタン押下でキャンセル処理をされているのだと思います。

> 違う行にキャンセルボタンがあるのですか?

なにと違う行なのかわかりませんが、GridView は行、列のあるいわゆる表形式の出力を行い、
キャンセルボタンは GridView のヘッダー行、フッター行、ページャー行を除くすべての行に
あります。

> 1回呼ばれるのは問題ないのですか?

SelectedIndexChanged イベントのハンドラーがということでよいですか。
SelectedIndexChanged イベントは GridView の選択ボタンを押下したときに発生します。
SelectedIndexChanged イベントのハンドラー gvCancelResult_SelectedIndexChanged は
GridView コントロールの OnSelectedIndexChanged 属性に設定されて関連付けられています。
<asp:GridView 
  ...
  OnSelectedIndexChanged="gvCancelResult_SelectedIndexChanged">

キャンセルボタンを押下したときに gvCancelResult_SelectedIndexChanged が1回呼ばれるの
は問題ありません。そして、1回呼ばれるべきです。

よくみると Handles 句でもイベントハンドラーの設定がされており、
もしかすると、ハンドラーが2重に登録されてしまうのかもしれません。

Protected Sub gvCancelResult_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles gvCancelResult.SelectedIndexChanged

>>GridView.SelectedIndexChanged イベントは2回発生しないのが一般的だと思います。
> GridView だとそういう動きなんですか?ローカルのコントロール(ListView, DataGridView)
> では選択解除があるので、選択行を変更したのであれば2回発生する必要があると思いますが。

GridView では SelectedIndexChanged イベントが選択解除をトリガーとして発生することは
ありません。以前の選択行を得たい場合には、選択行が切り替わる前に発生する
SelectedIndexChanging イベントを利用します。
ポストバックごとに表示全体をレンダリングし直すので、選択している行のインデックスを参照して、
該当行に選択行専用のスタイルを適用するということになります。
選択行の変更に伴い SelectedIndexChanged イベントが2回発生する必要はありません。

Windows Form の知識は皆無なので、すこしずれた回答になっているかもしれませんが、
私の GridView コントロールの認識としてはこんな感じです。
2011/01/17(Mon) 13:35:09 編集(投稿者)

■No28026に返信(shuさんの記事)
■No28027に返信(もりおさんの記事)

> 私の GridView コントロールの認識としてはこんな感じです。
分かり易い説明ありがとうございます。やはりお作法がかなり
違うのですね。

ということは、shu さんご指摘の
>> gvCancelResult.DataBind()
>SelectedIndexChangedでこれが動くので
や、もりお さんご指摘の
> よくみると Handles 句でもイベントハンドラーの設定がされており、
> もしかすると、ハンドラーが2重に登録されてしまうのかもしれません。
あたりを再確認して原因が明確になってから対策ですね。


# フラグで PrepareSearchCancelDataSource 実行時は処理しない
# のも選択肢に上がってきそうな感じですか。。。
■No28026 に返信(shuさんの記事)

>>gvCancelResult.DataBind()
> SelectedIndexChangedでこれが動くのでSelectedIndexChangedが
> 2回発生しているのだと思います。

GridView.DataBind メソッドを呼ぶことで、GridView.SelectedIndexChanged イベントが発生
するということですか。
GridView.SelectedIndexChanged イベントは Button.Click イベントが GridView に伝播して
発生するものなので、GridView.DataBind メソッドを呼ぶことによっても発生するとは考え
にくいです。

フラグを使うという結論を導くのは尚早なように思います。
■No28029に返信(もりおさんの記事)

aspx側のイベントは見落としていました。どちらかを
わざわざ記述したということかな?


> GridView.DataBind メソッドを呼ぶことで、GridView.SelectedIndexChanged イ> ベントが発生
> するということですか。
> GridView.SelectedIndexChanged イベントは Button.Click イベントが GridView に伝播して
> 発生するものなので、GridView.DataBind メソッドを呼ぶことによっても発生す> るとは考え
> にくいです。
リストを書き換えているのがここだけで、WinForm系のコントロールではこういうことは発生するので同じ事かと思ってました。
皆様、ご意見、指摘事項、アドバス有難う御座います。
体調を崩し、返信出来ずに申し訳御座いません。

キャンセル処理とは
<asp:ButtonField CommandName="Select" HeaderText="キャンセル" ShowHeader="True" Text="キャンセル" />
で発生するイベントの事をさしてました。

イベント的には
gvCancelResult_SelectedIndexChanged のイベントになります。

そして2回呼ばれるとは、デバッグモードで上記のイベントにブレイクポイントを指定し確認しました。

上記のgvCancelResult_SelectedIndexChanged処理でDB登録を終えて
Exit Subで処理を抜けていると思ったのですが、
再度、gvCancelResult_SelectedIndexChanged処理が実施されて、
ASP側へ処理が戻っている動きをしております。

ASPでMasterPageFile="~/Master/Basic.master"マスターページを指定している。
など関係があるのでしょうか。

宜しくお願い致します。

謎が解決できずすごく気持ち悪のですが、
暫定的にフラグを設定し処理をする事に致しました。
■No28034に返信(はちまきさんの記事)

> OnSelectedIndexChanged="gvCancelResult_SelectedIndexChanged">


> Handles gvCancelResult.SelectedIndexChanged


指摘があったようにこれらのどちらかを削ってみてはどうでしょう?
>> OnSelectedIndexChanged="gvCancelResult_SelectedIndexChanged">

上記の箇所をASPから削除しました。
そしたらCallされる回数が1回になりました。
ボタンタグとグリッド処理内から1回づつ呼ばれていたみたいです。

皆様、ご教示、アドバイス有難う御座いました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板