質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

4回答

6466閲覧

ListViewで、常に1つの項目を選択するようにしたい

koiru

総合スコア4

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

1グッド

0クリップ

投稿2021/06/28 12:38

前提・実現したいこと

WinFormsのListViewで、項目以外の余白をクリックしても選択を解除せずに、常に1つの項目を選択するようにしたいです。

ListViewから項目を選択してOKボタンを押すようなUIで、OKボタンを押してなにも選択されていなかった際に、いちいち「項目を選択してください」とエラーを出すより、そもそも「なにも選択されていない」という状況を生み出せないように、上記のようなことをしたいです。

発生している問題・エラーメッセージ

ItemSelectionChangedのイベントハンドラで、「余白の部分にマウスカーソルがあり、選択を解除している」場合に、解除した項目を再選択させるようにしたのですが(該当のソースコード を参照)、

1.item2 か item3 を選択
2.ListViewの項目以外の余白部分をクリック
3.1で item2 を選択した場合はitem1を、item3 を選択した場合はitem2かitem1を選択

上記の手順を踏むと、1で選択した項目に、選択状態が残ったままになってしまいます。
実際の現象(1でitem2を選択した場合)

ListViewのMultiSelectプロパティをtrueにすれば、この現象自体は回避できるのですが、選択できる項目は1つだけにしたいです。

この現象を回避(選択状態が残らないように)し、選択できる項目は1つだけとするには、どうすればいいのでしょうか?
ご教示ください。

該当のソースコード

Form1.Designer.cs

C#

1namespace WindowsFormsApp1 2{ 3 partial class Form1 4 { 5 /// <summary> 6 /// 必要なデザイナー変数です。 7 /// </summary> 8 private System.ComponentModel.IContainer components = null; 9 10 /// <summary> 11 /// 使用中のリソースをすべてクリーンアップします。 12 /// </summary> 13 /// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param> 14 protected override void Dispose(bool disposing) 15 { 16 if (disposing && (components != null)) 17 { 18 components.Dispose(); 19 } 20 base.Dispose(disposing); 21 } 22 23 #region Windows フォーム デザイナーで生成されたコード 24 25 /// <summary> 26 /// デザイナー サポートに必要なメソッドです。このメソッドの内容を 27 /// コード エディターで変更しないでください。 28 /// </summary> 29 private void InitializeComponent() 30 { 31 System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("item1"); 32 System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem("item2"); 33 System.Windows.Forms.ListViewItem listViewItem3 = new System.Windows.Forms.ListViewItem("item3"); 34 this.listView1 = new System.Windows.Forms.ListView(); 35 this.SuspendLayout(); 36 // 37 // listView1 38 // 39 this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 40 | System.Windows.Forms.AnchorStyles.Left) 41 | System.Windows.Forms.AnchorStyles.Right))); 42 this.listView1.HideSelection = false; 43 this.listView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] { 44 listViewItem1, 45 listViewItem2, 46 listViewItem3}); 47 this.listView1.Location = new System.Drawing.Point(0, 0); 48 this.listView1.MultiSelect = false; 49 this.listView1.Name = "listView1"; 50 this.listView1.Size = new System.Drawing.Size(583, 262); 51 this.listView1.TabIndex = 0; 52 this.listView1.UseCompatibleStateImageBehavior = false; 53 this.listView1.ItemSelectionChanged += new System.Windows.Forms.ListViewItemSelectionChangedEventHandler(this.listView1_ItemSelectionChanged); 54 // 55 // Form1 56 // 57 this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 58 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 59 this.ClientSize = new System.Drawing.Size(584, 261); 60 this.Controls.Add(this.listView1); 61 this.Name = "Form1"; 62 this.Text = "Form1"; 63 this.ResumeLayout(false); 64 65 } 66 67 #endregion 68 69 private System.Windows.Forms.ListView listView1; 70 } 71}

Form1.cs

C#

1using System; 2using System.Windows.Forms; 3 4namespace WindowsFormsApp1 5{ 6 public partial class Form1 : Form 7 { 8 public Form1() 9 { 10 InitializeComponent(); 11 } 12 13 private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) 14 { 15 var mousePos = listView1.PointToClient(Cursor.Position); 16 if (listView1.GetItemAt(mousePos.X, mousePos.Y) == null && e.IsSelected == false) 17 { 18 Console.WriteLine($"選択解除させない ItemIndex: {e.ItemIndex}, IsSelected: {e.IsSelected}"); 19 e.Item.Selected = true; 20 } 21 } 22 } 23}

補足情報(FW/ツールのバージョンなど)

  • .NET Framework 4.7.2
  • Visual Studio 2019
TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

ベストアンサー

どうしてそういう状態になるんでしょうね?(根深そうなので特に深入りしませんが^^;

そもそも余白のクリックを無視させるというアプローチがありました。
ListView Losing focus while clicking on blank space of listview in C#.net - CodeProject

ListViewMyListViewに入れ替えます。
コードの詳細は分かっていませんが、希望の動きになっていると思います。

cs

1using System.Drawing; 2using System.Windows.Forms; 3 4namespace Questions346626 5{ 6 public partial class MyListView : ListView 7 { 8 public MyListView() => InitializeComponent(); 9 10 protected override void WndProc(ref Message msg) 11 { 12 // Ignore mouse messages not in the client area 13 if (msg.Msg >= 0x201 && msg.Msg <= 0x209) 14 { 15 var pointMousePos = new Point(msg.LParam.ToInt32() & 0xffff, msg.LParam.ToInt32() >> 16); 16 var lvhti = HitTest(pointMousePos); 17 switch (lvhti.Location) 18 { 19 case ListViewHitTestLocations.AboveClientArea: 20 case ListViewHitTestLocations.BelowClientArea: 21 case ListViewHitTestLocations.LeftOfClientArea: 22 case ListViewHitTestLocations.RightOfClientArea: 23 case ListViewHitTestLocations.None: 24 return; 25 } 26 } 27 base.WndProc(ref msg); 28 } 29 } 30}

投稿2021/06/28 14:43

編集2023/07/27 16:18
TN8001

総合スコア9862

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

koiru

2021/06/29 09:47

なるほど、そんな手が……! ありがとうございました!
guest

0

ListViewから項目を選択してOKボタンを押すようなUIで、OKボタンを押してなにも選択されていなかった際に、いちいち「項目を選択してください」とエラーを出すより、そもそも「なにも選択されていない」という状況を生み出せないように、上記のようなことをしたいです。

消極的な解決策としてば、

  1. SelectedIndexChanged あたりで選択されていない状態を検出し、OKボタンのEnabledを操作する

(一応エラーは出さなくて済む)

  1. ListViewではなくReadOnlyなDataGridViewで代用する。

(DataGridViewは未選択状態にならないので)

といった方法もあります。


質問のような状態になる原因が個人的に気になったので検証してみましたが、
単純に項目領域外をクリックして選択解除されるだけの時のイベントは
0. MouseDown
0. ItemSelectionChanged (選択解除)

のように流れるので、このケースで再選択させるのはおそらく上手くいきます。
しかし、選択中の項目から別の項目を選択した場合のイベントは
0. MouseDown
0. ItemSelectionChanged (選択解除)
0. ItemSelectionChanged (新しい項目を選択)

のように流れ、2に再選択処理を割り込ませると、既に選択解除した項目を選択し、更に新しい項目も選択しているような感じになり、ここで表示とプロパティの整合性が取れなくなり、動作がおかしくなっている感じがします。

極力ウィンドウメッセージを殺さないようにするなら、

C#

1 private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) 2 { 3 //何も選択されていなかったらスペースキーを送信して再選択 4 if (listView1.SelectedIndices.Count == 0) 5 { 6 SendKeys.Send(" "); 7 } 8 }

シンプルにこんなのでもいいかもしれません。

投稿2021/06/29 02:28

編集2021/06/30 07:35
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/06/29 03:17 編集

個人的な見解になりますが、トリッキーな方法で実装した非標準な挙動は予測できないバグを生む可能性があるので、最後の手段と考えて可能な限り避けた方が良いと思います。 (変更した結果の全ての挙動を理解してやるのであれば構いませんが)
TN8001

2021/06/29 03:33

そもそもListViewという選択が間違っているのかもしれませんね。 「複数の選択肢から一つだけ選ぶ」のはRadioButtonの役割ですね。 とはいえ意味論だけで決まるものでもないですが^^;
退会済みユーザー

退会済みユーザー

2021/06/29 03:36

まあ見た目的な問題もありますしね。 線入ってるの嫌だからDataGridView嫌って言われたらそれまでですし…
koiru

2021/06/29 09:49

1は、なぜボタンが無効化されているのか、ユーザーが分かりづらいような気がします。 2は、やはり線が気になりますね……。 >トリッキーな方法で実装した非標準な挙動は予測できないバグを生む可能性があるので、最後の手段と考えて可能な限り避けた方が良いと思います。 これは少なからず自分も思います。 が、今回ベストアンサーにしたマウスメッセージを握りつぶす方法は「まあ大丈夫かな」と個人的には思います。 回答ありがとうございました。
koiru

2021/06/30 13:09

追記へ 「選択中の項目から別の項目を選択した場合」は、マウス直下に項目が存在するため、再選択処理は実行されないと思うのですが……。 しかし、無選択時にスペースキーを押すだけでも回避できるんですね。選択状態は消えるけどフォーカスは残ってる、みたいな感じなんでしょうか。
退会済みユーザー

退会済みユーザー

2021/06/30 13:21

> 再選択処理は実行されないと思うのですが……。 ログでも取ってトレースしてみれば判ります
koiru

2021/06/30 13:47

すみません。「ログを取ってトレース」というのを、どうやってやるのかが分かりません……。 自分は、「質問文の「発生している問題・エラーメッセージ」の手順3を行ったときに、再選択処理(e.Item.Selected = true;)が実行されて動作がおかしくなるのが原因」と書かれているのだと思い、「しかし、Console.WriteLineメソッドで、VSの出力ウィンドウになにも出力されていないから、再選択処理は実行されていない(されない)」と思ったのですが、どこか間違っているのでしょうか……。
退会済みユーザー

退会済みユーザー

2021/06/30 13:52 編集

そもそもWindowsFormsアプリって、標準ではコンソール出力されないと思いますが。 (何らかの方法でコンソールウィンドウ表示させてるなら出るでしょうけど) 通常はDebug.WriteLine辺りを使ったり、ファイルに出力したりします。
koiru

2021/07/01 09:42

返信が遅くなりました。 >そもそもWindowsFormsアプリって、標準ではコンソール出力されないと思いますが。 コンソールというか、Visual Studioの出力ウィンドウに出力されます。 また、Console.WriteLineメソッドをDebug.WriteLineメソッドに置き換えてみましたが、同じように手順3を行ったときは、VSの出力ウィンドウに出力されませんでした。 再選択処理にブレークポイントを設定もしてみましたが、手順3を行ったときは停止しませんでした。 このことから、やはり質問文の手順3を行ったとき(選択中の項目から別の項目を選択した場合)には、再選択処理は実行されていないかと思います。 すなわち、その考えは違うのではないかと思います(せっかく検証いただいたのに申し訳ありません)。
退会済みユーザー

退会済みユーザー

2021/07/05 02:42 編集

> このことから、やはり質問文の手順3を行ったとき(選択中の項目から別の項目を選択した場合)には、再選択処理は実行されていないかと思います。 なんかもう面倒くさくなってきたので、信じないなら信じないでいいですのでこれで最後にしますが、真っ新なWindowsFormsプロジェクトを作成して、ItemSelectionChangedイベントハンドラだけ追加して余計な条件分岐等を全く入れずログ出力してみてください。 > 選択中の項目から別の項目を選択した場合 の操作で、ItemSelectionChangedでのログが二回出力される筈です。
koiru

2021/07/05 09:23

「選択中の項目から別の項目を選択した場合」にItemSelectionChangedイベントが2回発生するのは分かるのですが、その場合、「listView1.GetItemAt(mousePos.X, mousePos.Y) == null」が満たされないので再選択処理(e.Item.Selected = true;)は実行されないということが言いたかったです。 何度もすみません。私からもこれで以上です。
guest

0

Itemの再選択を BeginInvoke にしたらどうでしょうか.

CSharp

1//e.Item.Selected = true; 2listView1.BeginInvoke( (Action)( ()=>{ e.Item.Selected = true; } ) ); //※C#初心者なのでここの書き方に自信がないですが

投稿2021/06/29 01:52

編集2021/06/29 01:54
fana

総合スコア11996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/06/29 02:39

まぁ,BeginInvokeで投げた処理よりも前に OKボタン押下時の処理 が走る可能性もゼロとは言えない気もするが… (超高速のGUI操作とかした場合?)
koiru

2021/06/29 09:48

おお、仕組みはよく分かりませんが、確かにこれでも回避できますね。 回答ありがとうございました。
fana

2021/06/29 10:02

フレームワークがイベントの処理をしている途中でまたそのイベント処理が指し挟まるようなことをしちゃうから描画が変になるのだと予想したので, とりあえず現在のイベント処理が終わってから,再選択処理を行えばまともな形(ふつーにイベント処理を2回やる)になるであろう,と.
koiru

2021/06/29 11:46

なるほど。ありがとうございました。
guest

0

ListViewは項目を一度選択したら、それをずっと保持してますよ
最初だけ無選択になりますが、そのときだけどれかを選択してやればいいかと

投稿2021/06/28 13:27

y_waiwai

総合スコア88042

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gentaro

2021/06/28 20:50

質問文の操作を行うと選択は解除されます。 やってみればわかります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問