C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[10]: ListViewで行選択変更をキャンセルしたい


(過去ログ 25 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■11713 / inTopicNo.1)  ListViewで行選択変更をキャンセルしたい
  
□投稿者/ もんもん (1回)-(2007/12/20(Thu) 13:43:59)

分類:[C#] 

はじめて質問させて頂きます。
VS2005 C#で開発しています。
ListView(View=Details MultiSelect=false)のSelectedIndexChangedイベントハンドラで行変更の可否を判断して、NOの場合は選択を元に戻す処理をしたいと思っています。

次のようなコードを書いていますが、SelectedIndexChangedイベントハンドラが2度呼ばれてしまいます。

private int prevSelectedIndex = -1;

private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
int selectedIndex = listView1.SelectedIndices.Count > 0 ? listView1.SelectedIndices[0] : -1;
if (selectedIndex == -1)
return;
if (prevSelectedIndex != -1)
{
if (MessageBox.Show("行を変更しますか", "", MessageBoxButtons.YesNo) == DialogResult.No)
{
listView1.SelectedIndexChanged -= listView1_SelectedIndexChanged;
listView1.Items[prevSelectedIndex].Focused = true;
listView1.Items[prevSelectedIndex].Selected = true;
listView1.SelectedIndexChanged += listView1_SelectedIndexChanged;
return;
}
}
prevSelectedIndex = selectedIndex;
}

よい方法がありましたらご教授お願いします。

引用返信 編集キー/
■11725 / inTopicNo.2)  Re[1]: ListViewで行選択変更をキャンセルしたい
□投稿者/ オノデラ (27回)-(2007/12/20(Thu) 15:04:50)
オノデラ さんの Web サイト
2007/12/20(Thu) 15:07:29 編集(投稿者)

 もんもんさんはじめまして、オノデラです。

 ListView の SelectedIndexChanged イベントは「アイテムが選択されたとき」と「アイテムの選択が解除されたとき」に発生するので、1度の選択で1回〜複数回イベントが呼ばれるようになっています。(直前の選択状況による)

 アイテムの変更イベントには「SelectedIndexChanged」のほかに「ItemSelectionChanged」イベントがあり、こちらは引数に選択動作対象アイテムや選択状態が渡されてくるので、判定する際に使いやすいのではないかと思います。(ちなみにこちらも選択と選択解除の2パターンが発生しますが、引数で選択の可否が調べられるので楽です)
引用返信 編集キー/
■11730 / inTopicNo.3)  Re[2]: ListViewで行選択変更をキャンセルしたい
□投稿者/ もんもん (2回)-(2007/12/20(Thu) 16:07:22)
No11725 (オノデラ さん) に返信

オノデラ さん ご返信ありがとうございます。

>> 1度の選択で1回〜複数回イベントが呼ばれるようになっています。

提示のプログラムでは、 selectedIndex によって一応判定しています。
状況としてはカーソルによる選択行変更では、期待通りに動きます。
マウスによる選択行変更では「行を変更しますか」が二度表示されます。

if (MessageBox.Show("行を変更しますか", "", MessageBoxButtons.YesNo) == DialogResult.No)
{
//listView1.SelectedIndexChanged -= listView1_SelectedIndexChanged;
//listView1.Items[prevSelectedIndex].Focused = true;
//listView1.Items[prevSelectedIndex].Selected = true;
//listView1.SelectedIndexChanged += listView1_SelectedIndexChanged;
return;
}
のようにコメントアウトすると「行を変更しますか」は一度だけしか表示されなくなります。

どうも、元の行が10で、マウスで行5を選択した場合
listView1.SelectedIndexChanged -= listView1_SelectedIndexChanged;
listView1.Items[prevSelectedIndex].Focused = true;
listView1.Items[prevSelectedIndex].Selected = true;
listView1.SelectedIndexChanged += listView1_SelectedIndexChanged;
で選択を10に戻したあと、マウスイベントが発生してまた行5が選ばれるようなんです。

なにか解決索がありましたらご教授下さい。


引用返信 編集キー/
■11742 / inTopicNo.4)  Re[3]: ListViewで行選択変更をキャンセルしたい
□投稿者/ オノデラ (28回)-(2007/12/20(Thu) 19:07:32)
オノデラ さんの Web サイト
 すみません、ちょっと的外れな答えになってしまいました(^^)

 えっとこの ListView の動作なんですが、結構根が深い問題かと思います。
 今回のプログラムはマウスで操作するときに起こる問題で、マウスでクリックして選択したときにダイアログを表示させると、マウスのボタンを離したときのイベントメッセージが処理されずにダイアログが表示されます。
 その後ダイアログが閉じるとイベント的にはマウスがクリックされた状態のままなので、カーソルをフォーム上に移動させるとマウスのボタンを離したときのイベントが走ってしまうのではないかと思います(私の憶測ですが…まとまってない orz)。

 実際にイベントをトレースしてみると結構余分にイベントが呼ばれていることがわかるかと思います。

 正直いい対処法は思いつかないのですが、前に「選択したアイテム」のほかに、「選択をキャンセルしたとき」のアイテムインデックスをさらに保持しておくか、ダイアログを表示しないで別な方法で対処するか、でしょうか。

 う〜ん、いい方法が思いつかない…。
引用返信 編集キー/
■11750 / inTopicNo.5)  Re[4]: ListViewで行選択変更をキャンセルしたい
□投稿者/ まどか (417回)-(2007/12/20(Thu) 21:08:43)
そのプロシージャだけでよいのなら
ローカルスタティックな「実行中フラグ」を作って
・実行中=Trueなら即抜ける
・再突入してしまう処理を実行中=Trueと実行中=Falseで囲む
が簡単かと。

現象としては、TextBoxのTextChanged内でTextプロパティをいじるのと一緒ですね。
引用返信 編集キー/
■11751 / inTopicNo.6)  Re[5]: ListViewで行選択変更をキャンセルしたい
□投稿者/ オノデラ (29回)-(2007/12/20(Thu) 21:52:37)
オノデラ さんの Web サイト
2007/12/20(Thu) 21:54:42 編集(投稿者)

 確かにテキストボックスなどではその方法で対応できるのですが、ListView だとかなり厄介なのです。

 ためしにもんもんさんのコードを試してみるとわかるのですが、マウスでアイテムを選択し、ダイアログボックスが出たときにマウスカーソルはそのままの位置にしておき、キーボードでダイアログボックスの「いいえ」を選択します。

 ダイアログが閉じた後、マウスカーソルを移動しようとしたときに「SelectedIndexChanged」イベントが走ってしまうのです(選択解除と選択の2回)。どうやら ListView の乗っている Form 内でマウスカーソルを動かしたときに発生するようで、なんとも摩訶不思議です。(^^;) (実際に発生順序を調べてみるとわかるかもしれません)


引用返信 編集キー/
■11767 / inTopicNo.7)  Re[6]: ListViewで行選択変更をキャンセルしたい
□投稿者/ まどか (418回)-(2007/12/21(Fri) 09:54:55)
>  確かにテキストボックスなどではその方法で対応できるのですが、ListView だとかなり厄介なのです。

フライングしてしまいました。
現在検証中です。
引用返信 編集キー/
■11770 / inTopicNo.8)  Re[7]: ListViewで行選択変更をキャンセルしたい
□投稿者/ まどか (419回)-(2007/12/21(Fri) 10:18:25)
とりあえず、要求を満たすという意味で。
Activation プロパティを「OneClick」に設定。ItemActivate イベントを利用。

#アイコンが「手」に変わるのと↓がVBなのはご愛嬌。

Private _CurrentItemIndex As Integer = -1

Private Sub ListView1_ItemActivate(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListView1.ItemActivate

If Not ListView1.SelectedItems.Count > 0 Then
Exit Sub
End If

Dim selectedIndex As Integer = ListView1.SelectedItems(0).Index

If _CurrentItemIndex < 0 Then
_CurrentItemIndex = selectedIndex
Exit Sub
End If

If selectedIndex <> _CurrentItemIndex Then
Dim msg As String = "行を変更しますか?"
Dim result As DialogResult = MessageBox.Show(msg, Me.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If result <> Windows.Forms.DialogResult.Yes Then
With ListView1.Items(_CurrentItemIndex)
.Selected = True
.Focused = True
.EnsureVisible()
End With
Else
_CurrentItemIndex = selectedIndex
End If
End If

End Sub

引用返信 編集キー/
■11773 / inTopicNo.9)  Re[8]: ListViewで行選択変更をキャンセルしたい
□投稿者/ オノデラ (30回)-(2007/12/21(Fri) 10:35:39)
オノデラ さんの Web サイト
2007/12/21(Fri) 10:48:15 編集(投稿者)

 ItemActivate イベントと Activation プロパティとは盲点でした。そんな方法もあったんですね。これだと確かにマウス操作でうまくいくようです。

 でもキーボードで操作するとなると…orz っていう問題もあるんですが、このあたりはもんもんさんのご判断ということになるでしょうか。

# > もんもん さん
# 私も以前に ListView の選択キャンセルをやったことがあるのですが、今回と同様に結構面倒でした。なので別なアプローチを取ったほうが好ましいかと思います。


 まどかさんのコードを C# に置き換えました。

private int _CurrentItemIndex = -1;

private void listView1_ItemActivate(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count == 0)
{
return;
}

int selectedIndex = listView1.SelectedItems[0].Index;

if (_CurrentItemIndex < 0)
{
_CurrentItemIndex = selectedIndex;
return;
}

if (selectedIndex != _CurrentItemIndex)
{
string msg = "行を変更しますか?";
DialogResult result = MessageBox.Show(msg, Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result != DialogResult.Yes)
{
listView1.Items[_CurrentItemIndex].Selected = true;
listView1.Items[_CurrentItemIndex].Focused = true;
listView1.Items[_CurrentItemIndex].EnsureVisible();
}
else
{
_CurrentItemIndex = selectedIndex;
}
}
}

引用返信 編集キー/
■11785 / inTopicNo.10)  Re[9]: ListViewで行選択変更をキャンセルしたい
□投稿者/ もんもん (3回)-(2007/12/21(Fri) 11:25:19)
まどかさん、ありがとうございます。
Activationプロパティの使い方を始めて知りました。
とても勉強になりました。
オノデラさんのおっしゃるとおりキー操作をどうするかという問題がありますので、検討してみます。

オノデラさん、いろいろご調査頂きありがとうございます。
どうも奥深い問題のようですね。
インターフェースを変える等の別のアプローチも検討してみます。
現在は、イベントの中でタイマーを仕掛けてイベントを抜けた後に、選択を元に戻すという禁じ手で、とにかく目的の動作は達成しています。(^^;
また何か良い方法がありましたら、お教え下さい。

一応解決済みとします。
有難うございました。

解決済み
引用返信 編集キー/
■11789 / inTopicNo.11)  Re[10]: ListViewで行選択変更をキャンセルしたい
□投稿者/ まどか (420回)-(2007/12/21(Fri) 12:01:33)
まず、ItemSelectionChanged イベントが選択状態とアイテムを引数に持つので、
イベントの発生粒度がはっきりしているので使えるなと思ったのですがだめでした。
たとえば、インデックスを3→5にした場合
なぜか「5:非選択」「3:選択」「3:非選択」「5:選択」となって
元のアクションを再現しようとしてるのがわかりました。
ClickやMouseDownはもちろん、MouseUpも同様のことが起きました。

たぶんイベントの連鎖が完全に終わっていないためで、その終了を判断できなければ無理だと思いました。
その意味ではItemActivate イベントは変化が確定した後のイベントであることがわかりますね。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -