ListBoxとTextBoxの連携

タグの編集
投稿者 是木  (社会人) 投稿日時 2009/5/1 04:45:42
ListBoxとTextBoxの連携について調べているのですが、
以下のようなプログラムを書いてみました。
動作の予定は、
マウスでListBoxの選択位置を変更する都度TextBoxは、ListBoxの該当項目を表示する。
TextBoxを、キー入力で更新する度にListBoxの該当項目も更新される。


1 デザイン画面でListBox1を配置
2 その下にTextBox1を配置
3 以下のコードを実行してみると、


ListBoxの選択項目を変えた場合にテキストボックスは
ListBoxの選択項目をうまく表示してくれたのですが、
テキストボックスにタイプを試みるとエラーで停止になります。
どうしてなんでしょうか?

Public Class Form1

    Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
        ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Text

    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ListBox1.Items.Add("aaa")
        ListBox1.Items.Add("bbb")
        ListBox1.Items.Add("ccc")
        ListBox1.Items.Add("ddd")
        ListBox1.Items.Add("eee")
        ListBox1.SelectedIndex = 0

    End Sub

    Private Sub ListBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
        TextBox1.Text = ListBox1.Items(ListBox1.SelectedIndex)
        TextBox1.Focus()
    End Sub
End Class
投稿者 ヴァン  (社会人) 投稿日時 2009/5/1 19:30:03
こんにちは。

>テキストボックスにタイプを試みるとエラーで停止になります。

「タイプを試みる」とは「変更のために記入してみる」ということでしょうか?

また、「エラーで停止になります」とありますが、どの様なエラーでしょうか?
投稿者 刈谷勇@頭だけGW  (社会人) 投稿日時 2009/5/1 23:47:19
まず、エラーになる原因ですが、TextChangedはテキストボックスの値が変わった瞬間に
発生します。
たとえば、テキストボックスにBBBと入っている場合にAAAと変更しようした場合、Aを入力した
瞬間にイベントが発生します。
ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Textを実行すると、SelectedIndexChangedが2回流れるようです。
(選択行解除、元の行の再選択)
このときの選択行解除のSelectedIndexChangedでItemsの配列外を指してしまうためにエラーになっています。(うまく説明できないので、ステップ実行でSelectedIndexの内容を確認してみてください)

もう1つ問題があります。
ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Textだと、リストの選択されている行の値を変えてしまいます。
選択行を変えるならば、ListBox1.Text = TextBox1.Textでいいと思います。
(リストに存在しない場合は、選択行は変わりません)

以上を踏まえたサンプルです。
    Private Sub TextBox1_TextChanged(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles TextBox1.TextChanged
        ListBox1.Text = TextBox1.Text
    End Sub

    Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load
        ListBox1.Items.Add("aaa")
        ListBox1.Items.Add("bbb")
        ListBox1.Items.Add("ccc")
        ListBox1.Items.Add("ddd")
        ListBox1.Items.Add("eee")
        ListBox1.SelectedIndex = 0

    End Sub

    Private Sub ListBox1_SelectedIndexChanged(ByVal sender As ObjectByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
        TextBox1.Text = ListBox1.Text
        TextBox1.Focus()
    End Sub
投稿者 是木  (社会人) 投稿日時 2009/5/11 10:01:22
返事が大変遅くなり本当に申し訳ありません。

ヴァンさん こんばんわ
「変更のために記入してみる」ということでしょうか?>>>>その通りです。
どの様なエラーでしょうか>>>>該当箇所=
TextBox1.Text = ListBox1.Items(ListBox1.SelectedIndex)
内容=
InvalidArgument=Value of '-1' is not valid for 'index'. Parameter name: index
です。エラーの内容を書き忘れて申し訳ありませんでした。

刈谷さん こんばんわ
>>>ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Textを実行すると、SelectedIndexChangedが2回流れるようです。
(選択行解除、元の行の再選択)
このときの選択行解除のSelectedIndexChangedでItemsの配列外を指してしまうためにエラーになっています>>>

うーむ。かなり参考になりました。ありがとうございました。
しかし、
僕の質問の仕方が悪かったかもしれないのですが
”TextBoxを、キー入力で更新する度にListBoxの該当項目も更新される”
の意味は、
TextBoxが仮に""の場合は、ListBoxの該当項目も""
TextBoxが仮に"aaaaaaaaaa"の場合は、ListBoxの該当項目も"aaaaaaaaaa"
いけないという意味です。刈谷さんのサンプルは、
TextBoxがListBoxに用意されているアイテムのどれかひとつにも
完全に合致しない場合はListBoxはまったく変更をしないので,。。。
しかし、それはそれで、違う用途で活路のあるプログラムだと思います。

ところで、正攻法ではないと思いますが、刈谷さんのおっしゃられるSelectedIndexChangedの弊害?を
避けるためListBox1_Clickを使ったらどうかと思い試してみたところうまくいきました。
TextBox1.Text = ListBox1.Text は、うまくいくみたいです。
しかしその逆はだめで
ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Textのところは、やはりそのように書かないと
このケースでは、だめみたいです。
Changedは想定外の時点でもイベントが発生してしまいますので扱いが難しいが
Clickの場合は実際にマウスを人為的に操作した場合だけに反応するので扱いやすいということが
いえるのではないでしょうか。
また、TextBox1_TextChanged とListBox1_SelectedIndexChanged というように
Changedが2つのコントロールで互いに干渉しあう心配も要らなくなるという利点があると思います。


Public Class Form1
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        ListBox1.Items.Add("aaa")
        ListBox1.Items.Add("bbb")
        ListBox1.Items.Add("ccc")
        ListBox1.Items.Add("ddd")
        ListBox1.Items.Add("eee")
        ListBox1.SelectedIndex = 0
        TextBox1.Text = ListBox1.Text

    End Sub
    Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
        ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Text
    End Sub
    Private Sub ListBox1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox1.Click
        TextBox1.Text = ListBox1.Text
        TextBox1.Focus()
    End Sub
End Class
投稿者 ヴァン  (社会人) 投稿日時 2009/5/11 19:19:42
>ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Text

このやり方は SelectedIndex が負の数値になった場合を考慮されていないのでダメだと思いますが。
ListBox1.Itemsの中からTextBox1.Textを探しだす方法の方が良いと思います。

TextBox1に"aab"と入力された場合はどの様な処理を行う予定ですか?
投稿者 是木  (社会人) 投稿日時 2009/5/12 08:22:07
ヴァンさんこんばんわ

>ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Text
>このやり方は SelectedIndex が負の数値になった場合を考慮されていないので
>ダメだと思いますが

しかし、
ClickのイベントではSelectedIndex が負の数値にならないみたいですし、
ダメだと言われても、ListBox1.Text = TextBox1.Text だと
当初の目的が達成できないので...。

>> ListBox1.Itemsの中からTextBox1.Textを探しだす方法の
>> 方が良いと思います。
>> TextBox1に"aab"と入力された場合はどの様な処理を行う
>> 予定ですか? 

すいません、質問がまったく伝わってなかったみたいですね。
今後質問書くときは文章に、気をつけないといけませんね。

TextBox1に"aab"と入力された場合の処理は、
その時点で選択されているリストボックスのテキスト(アイテムというべき?)を
TextBox1のテキストと全く同じテキスト"aab"に書き変えます。


投稿者 ヴァン  (社会人) 投稿日時 2009/5/12 20:41:08
SelectedIndexをListBoxをクリック時等で別な変数に保存してみてはどうですか?

>ClickのイベントではSelectedIndex が負の数値にならないみたいですし

クリックイベントでは負の数値にならないとしても、他でなる可能性はあります。
なっているから、

>InvalidArgument=Value of '-1' is not valid for 'index'. Parameter name: index

と言われるわけです。

投稿者 是木  (社会人) 投稿日時 2009/5/12 21:29:04
ヴァンさん。こんにちわ
確かにSelectedIndexは、負の数になる可能性がありますから
今後はあまりプロパティの受け渡しに頼らず
変数を使ってプログラムを書くようにしたいと思います。
今後ともよろしくお願いします。
投稿者 刈谷勇  (社会人) 投稿日時 2009/5/12 22:43:23
>今後はあまりプロパティの受け渡しに頼らず
>変数を使ってプログラムを書くようにしたいと思います。

いや、問題なのはプロパティを使っているからではなく、想定外のプロパティ値の時にそのプロパティ値を使っているのが問題なのです。なので、変数を使えば全て解決という問題でもありません。
今回の件では、SelectedIndexChangedのイベントが非選択状態になってとき(SelectedIndex値が-1の時)に呼ばれているのが問題なので、その場合の対処をすればいいだけです。
以下にサンプルを載せますので参考にしてください。

Public Class Form1
    Dim bTextChangeCancel As Boolean = False
    Dim bSelectedIndexChangeCancel As Boolean = False

    Private Sub TextBox1_TextChanged(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles TextBox1.TextChanged
        If ListBox1.SelectedIndex < 0 Or bTextChangeCancel Then
            Exit Sub
        End If

        ' SelectedIndexChangedの実行を抑制 
        bSelectedIndexChangeCancel = True

        ListBox1.Items(ListBox1.SelectedIndex) = TextBox1.Text

        ' SelectedIndexChangedの抑制を解除 
        bSelectedIndexChangeCancel = False
    End Sub

    Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load
        ListBox1.Items.Add("aaa")
        ListBox1.Items.Add("bbb")
        ListBox1.Items.Add("ccc")
        ListBox1.Items.Add("ddd")
        ListBox1.Items.Add("eee")
        ListBox1.SelectedIndex = 0

    End Sub

    Private Sub ListBox1_SelectedIndexChanged(ByVal sender As ObjectByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
        If ListBox1.SelectedIndex < 0 Or bSelectedIndexChangeCancel Then
            Exit Sub
        End If

        ' TextBox1_TextChangedの実行を抑制 
        bTextChangeCancel = True

        TextBox1.Text = ListBox1.Items(ListBox1.SelectedIndex)

        ' TextBox1_TextChangedの抑制を解除 
        bTextChangeCancel = False
        TextBox1.Focus()
    End Sub
End Class

投稿者   (社会人) 投稿日時 2009/5/13 02:23:48
>> 想定外のプロパティ値の時に
>> そのプロパティ値を使っているのが問題なのです。
まったく仰せの通りです。以後気をつけます。
おかげで今後Indexを扱う際は、常に"Index=-1"の問題に
特に気をつけないといけないということがよくわかりました。
いただいたサンプルも、(プロパティ値の例外を排除)という点に
重きを置いて書かれているように拝読させていただきましたが
参考にさせていただきたいと思います。