none
ListViewでの選択行変更キャンセルについて RRS feed

  • 質問

  • 初めて質問させていただきます。

    ListViewで、選択行が変更されたことをSelectedIndexChangedイベントで判断して、「選択を変更してよろしいですか?」という確認ダイアログを表示し、
    このダイアログに対してキャンセルlボタンが押下された場合は選択を変更前の行に戻すという処理を行いたいと思っています。

    選択を戻すために次のような処理を行っていますが、listView1.Items[index].Selected = trueのところでもう一度SelectedIndexChangedイベントが発生してしまい、
    選択を変更してよろしいですか?」のダイアログが2度表示されてしまいます。

    private void listView1_SelectedIndexChanged(object sender, EventArgs e)
    {
        ---- 略 ----
        if (MessageBox.Show("選択を変更してよろしいですか?", "確認", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
        {
            listView1.Items[previousIndex].Selected = true;
            listView1.Items[previousIndex].Focused = true;
        }
    }

    ListViewのMultiSelectはfalse、
    開発環境はVisualC#2008です。

    ダイアログが一度しか表示されないようにするための、
    何か方法がございましたらアドバイスいただけると幸いです。
    よろしくお願い致します。
    2009年10月8日 11:21

回答

  • ComboBox.SelectionChangeCommitted イベント
    http://msdn.microsoft.com/ja-jp/library/system.windows.forms.combobox.selectionchangecommitted(VS.80).aspx

    に、

    「SelectedIndexChanged イベントと SelectedValueChanged イベントは、プログラム上で選択項目が変更されたときにも発生するため、ユーザーによる変更をキャプチャする目的でこれらのイベントを使用しないでください。」

    と書かれています。ListViewでも同様の動作をするため、SelectedIndexChangedイベント内で選択行を操作するのは推薦されないでしょう。
    以下にItemActivateイベントを使った例がありますので、参考になるのではないかと思います。

    ListViewで行選択変更をキャンセルしたい
    http://bbs.wankuma.com/index.cgi?mode=al2&namber=11713&KLOG=25


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク 三本柱 2009年10月21日 3:58
    • 回答としてマークされていない 三本柱 2009年10月21日 3:59
    • 回答としてマーク 三本柱 2009年10月21日 3:59
    2009年10月9日 2:57
    モデレータ

すべての返信

  • 選択を戻すために次のような処理を行っていますが、listView1.Items[index].Selected = trueのところでもう一度SelectedIndexChangedイベントが発生してしまい、
    選択を変更してよろしいですか?」のダイアログが2度表示されてしまいます。
    イベントの中で、previousIndex と今回選択されたインデックスが一致しているかどうか判定したらどうでしょうか?(2度目のイベントでは一致しているはず)
    なお、if ブロックの中では return と書いておいて、previousIndex が変化しないようにする工夫が必要かも知れません。
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年10月8日 15:06
    モデレータ
  • ご回答ありがとうございます。

    ご教授いただきました方法を参考にさせていただき試してみましたが、ダイアログは二度表示されてしまいました…。

    キーボード操作での選択変更だと一度しか表示されず、マウスでクリックしての選択変更だと二度表示されることから、
    クリックの場合は、選択が入る時と外れるときの二回イベントが呼ばれていることが原因かと考えています。

    何か思い当たることなどございましたら、
    引き続きアドバイスの程よろしくお願い致します。
    2009年10月9日 1:37
  • ComboBox.SelectionChangeCommitted イベント
    http://msdn.microsoft.com/ja-jp/library/system.windows.forms.combobox.selectionchangecommitted(VS.80).aspx

    に、

    「SelectedIndexChanged イベントと SelectedValueChanged イベントは、プログラム上で選択項目が変更されたときにも発生するため、ユーザーによる変更をキャプチャする目的でこれらのイベントを使用しないでください。」

    と書かれています。ListViewでも同様の動作をするため、SelectedIndexChangedイベント内で選択行を操作するのは推薦されないでしょう。
    以下にItemActivateイベントを使った例がありますので、参考になるのではないかと思います。

    ListViewで行選択変更をキャンセルしたい
    http://bbs.wankuma.com/index.cgi?mode=al2&namber=11713&KLOG=25


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク 三本柱 2009年10月21日 3:58
    • 回答としてマークされていない 三本柱 2009年10月21日 3:59
    • 回答としてマーク 三本柱 2009年10月21日 3:59
    2009年10月9日 2:57
    モデレータ
  • > クリックの場合は、選択が入る時と外れるときの二回イベントが呼ばれていることが原因かと考えています。

    これについて少し考えてみました。
    (結局わからなかったんですが。。)

    イベントの発生について実験してみると
    イベントメソッド内での設定はコメント化しても
    SelectedIndexChanged が2回発生します。
    ↓イベント発生順。
    listView1_MouseClick
    listView1_MouseDown
    listView1_SelectedIndexChanged
    listView1_SelectedIndexChanged
    listView1_MouseUp

    似たようなイベントで ItemSelectionChanged があります。
    こちらは引数で Select が false か true かがわかります。
    これについて実験してみると。
    false の true の組み合わせが 2回発生します。(計4回)

    なんとなく、セレクト (アイテムの周囲が点で囲まれる)と
    フォーカス(アイテムが反転表示)の2つの状態が存在していることが
    原因として考えられるかと思いましたが、
    それだとカーソルキーで移動したときとの動作の違いが説明できません。。

    2009年10月9日 3:42
  • こんばんは。
    処理中かどうか判断するフィールドを使用するという手はどうでしょう?


    private bool _isChanging = false;

    ---- 略 ----

    private void listView1_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (this._isChanging)
        {
            return;
        }

        this._isChanging = true;

        ---- 略 ----

        if (MessageBox.Show("選択を変更してよろしいですか?", "確認", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
        {
            listView1.Items[previousIndex].Selected = true;
            listView1.Items[previousIndex].Focused = true;
        }

        ---- 略 ----

        this._isChanging = false;
    }



    あるいは、試していないので不確かではあるのですが
    listView1.BeginUpdate()で描画を中断しておけば、SelectedIndexChangedイベントが
    2度発生することがなかったりしませんか?


    private void listView1_SelectedIndexChanged(object sender, EventArgs e)
    {
        listView1.BeginUpdate();

        ---- 略 ----

        if (MessageBox.Show("選択を変更してよろしいですか?", "確認", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
        {
            listView1.Items[previousIndex].Selected = true;
            listView1.Items[previousIndex].Focused = true;
        }

        ---- 略 ----

        listView1.EndUpdate();
    }

    2009年10月9日 14:00
  • ご回答ありがとうございます。
    返信が遅くなってしまい申し訳ありません。

    trapemiya様のご返信に記載されている参考リンク先で投稿されていました
    タイマーを使った方法で、仮に解決とすることにしました。
    2009年10月21日 4:02