コントロールをまとめて操作。

タグの編集
投稿者 ゆうさく  (社会人) 投稿日時 2018/5/10 21:24:49
 前提・実現したいこと
ボタンとテキストボックスを9つ用意しています
ボタンを押した際に、テキストボックスに空欄があれば
空欄のテキストボックスの数をメッセージボックスに表示させたいです。

 コード
Dim o As Control
For Each o In Me.Controls

If o.GetType Is GetType(TextBox) Then
if o.text = "" then

End If
End If

Next

msgbox(_&"個空白です")

アンダーバーのところに空欄のテキストボックスの数が入るようにしたいです

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

投稿者 (削除されました)  () 投稿日時 2018/5/11 17:11:31
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/5/11 18:45:23
とりあえず、こんな感じですかね。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    MsgBox(Controls.OfType(Of TextBox)().Count(Function(t) t.TextLength = 0) & "個空白です")
End Sub



> For Each o In Me.Controls

対象とするのは、フォーム上に置かれたコントロールだけで構いませんか?

上記の構文の場合、Panel や GruopBox の上に置かれたコントロールは拾われません。
もしも Panel 上のコントロールを列挙する場合は、Me.Controls ではなく Panel1.Controls を使います。


> Dim o As Control
> For Each o In Me.Controls

これは、.NET Framework 1.0 (VB.NET 2002)世代の書き方ですね。
VB.NET 2003 以降では 
For Each o As Control In Me.Controls
にした方が良いですよ。


> If o.GetType Is GetType(TextBox) Then

こういう場合には 
If TypeOf o Is TextBox Then
にした方がスマートでは無いでしょうか。

『o が TextBox クラス』だった場合、どちらの構文も True を返しますが、
『o が TextBox を継承したクラス』が相手の場合、
「o.GetType Is GetType(TextBox)」は False を返し、
「TypeOf o Is TextBox」は True を返す仕様です。


ちなみに、TextBox だけでなく RichTextBox も含めたいような場合には、
If TypeOf o Is TextBoxBase Then
と書けます。(TextBox も RichTextBox も、TextBoxBase を継承しています)
投稿者 コントロールをまとめて操作。  (社会人) 投稿日時 2018/5/13 21:24:01
魔界の仮面弁士様
ありがとうございました。

解決しました
投稿者 コントロールをまとめて操作。  (社会人) 投稿日時 2018/5/15 19:54:41
        Dim m As Integer
        Dim empty As Integer
        For Each o As Control In Me.Controls

            If TypeOf o Is TextBox Then
                If o.Text = "" Then
                    empty = (Controls.OfType(Of TextBox)().Count(Function(t) t.TextLength = 0))

                ElseIf o.Text <= 0 Then
                    m = (Controls.OfType(Of TextBox).Count(Function(t) t.Text <= 0))
                End If
            End If

        Next

        If empty Then
            empty = MsgBox(empty & "個空白です")
        ElseIf m Then
            m = MsgBox(m & "個正しく入力してください")
        End If
    End Sub


1つめのテキストボックスにマイナスの値を入れて、ボタンを押すと
 m = (Controls.OfType(Of TextBox).Count(Function(t) t.Text <= 0))
のところで下記エラーが出ます

System.InvalidCastException: 'String "" から型 'Double' への変換は無効です。'

エラーがでなくなる方法を教えてください(テキストボックスの変数はDoubleで宣言しています)
投稿者 (削除されました)  () 投稿日時 2018/5/15 19:54:42
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/5/15 20:26:04
> 社会人
業務開発でしょうか。
常に『データ型』を意識してコーディングする癖をつけましょう。


> For Each o As Control In Me.Controls
>  If TypeOf o Is TextBox Then
先の例のように、OfType で絞り込んだ方が手っ取り早いかと。


> empty = (Controls.OfType(Of TextBox)().Count(Function(t) t.TextLength = 0))
Controls を For Each している最中に、その
Controls を再列挙しているようですね。
わざわざ二重に列挙しているのは何故ですか?


> ElseIf o.Text <= 0 Then
Text プロパティは String 型で、
0 というリテラルは Integer 型ですよね。

「<=」の左右で、データ型が違っていますので、これだと
Double 型の比較式として処理されてしまいます。
(なので、Text の内容が Double として解釈できない時にエラーになるわけで)


数値入力に特化させたいのなら、TextBox1.Text ではなく、
NumericUpDown1.Value の利用をお奨めします。(Value は Decimal 型です)

NumericUpDown なら、小数部の桁数や最大値、最小値などを、
デザイン時に決めておくことができますし、数値以外の入力も防げます。

どうしても TextBox が必要なら、数値として変換できることを保証するために、
TryParse メソッドを併用すべきです。


> Dim empty As Integer
> If empty Then
ここは 『If empty > 0 Then』 なのでは?


> テキストボックスの変数はDoubleで宣言しています
「テキストボックスの変数」というのは、どういう意味でしょうか。

たとえば、
 Dim t As TextBox = TextBox1
 Dim s As String = TextBox1.Text
とは書けても、
 Dim d As Double = TextBox1
とは書けないですよね。

もしかして、
 Dim d As Double = TextBox1.Text
ということでしょうか?
だとしたらそれは間違いです。(代入式の左辺が Double 型で、右辺が String 型なので)

こういう場合には、TryParse メソッドを使いましょう。
Dim d As Double = 0.0
If Double.TryParse(TextBox1.Text, d) Then
投稿者 コントロールをまとめて操作。  (社会人) 投稿日時 2018/5/15 21:08:40
>業務開発でしょうか。
他業種です。
最近、プログラミングに興味を持ち、始めたばかりです。


>Controls を For Each している最中に、その
>Controls を再列挙しているようですね。
>わざわざ二重に列挙しているのは何故ですか?

以前、教えていただいた、下記を参考にしました。
MsgBox(Controls.OfType(Of TextBox)().Count(Function(t) t.TextLength = 0) & "個空白です")

            If TypeOf o Is TextBox Then
                If o.Text = "" Then
 empty =

emptyのところに空欄のテキストボックスの数が入るようにしたいです

>「テキストボックスの変数」というのは、どういう意味でしょうか。

下記のようにテキストボックスの値を変数に入れています。

    Private a As Double
    Private b As Double
    Private c As Double
  
  Private Sub txt()
        a = Val(TextBox1.Text)
        b = Val(TextBox2.Text)
    end sub
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/5/16 00:24:43
「ゆうさく」さんと、「コントロールをまとめて操作。」さんは同じ方ですか? あるいは同僚?


> a = Val(TextBox1.Text)

この書き方だとエラーになりえますので、これも先の回答と同じ理由により NG です。
TryParse を使うとか、NumericUpDown に切り替えるなどするのが安全かと思います。

Val を使った変換は、一見うまくいくかのようにも見えますが、たとえば 
 TextBox1.Text = "1.2%"
 a = Val(TextBox1.Text)
などのように、変換できずに InvalidCastException となるパターンがあります。



> emptyのところに空欄のテキストボックスの数が入るようにしたいです

その代入操作を、For Each のループ処理で、繰り返し行っている理由が分からなかったのです。

前回のコード例にて、ループ処理せずに使わずにカウントする方法を提示していますよね。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim emptyCount As Integer = Controls.OfType(Of TextBox)().Count(Function(t) t.TextLength = 0)
    MsgBox(emptyCount & "個空白です")
End Sub




>> For Each o As Control In Me.Controls
>>  If TypeOf o Is TextBox Then
>>      If o.Text = "" Then
> 先の例のように、OfType で絞り込んだ方が手っ取り早いかと。

これは要するに、For Each をするにしても、わざわざ TypeOf や .GetType() で型判定せずとも、
最初の回答に載せた OfType を用いてやれば良いのではないか、という意味です。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim emptyCount As Integer = 0
    Dim nonValueCount As Integer = 0
    Dim minusCount As Integer = 0

    Dim d As Double
    For Each txt In Me.Controls.OfType(Of TextBox)()
        If txt.TextLength = 0 Then
            emptyCount += 1
        ElseIf Not Double.TryParse(txt.Text, d) Then
            nonValueCount += 1
        ElseIf d < 0.0 Then
            minusCount += 1
        End If
    Next

    MsgBox("空白=" & emptyCount & "/非数値=" & nonValueCount & "/負数=" & minusCount)
End Sub