コントロール配列について

タグの編集
投稿者 heaven  (社会人) 投稿日時 2010/8/18 11:10:39
初めまして、初投稿させていただきます。

現在複数のコントロールを配列にまとめて処理を行っているのですが、クラスレベルでコントロール配列を宣言して実際に実行してみると、「オブジェクト参照がオブジェクト インスタンスに設定されていません。」と出てエラーが出てしまいます。

ただし、各プロシージャー内でコントロールを配列を宣言すればエラーは起こらないのですが、大量のコントロール配列をプロシージャー毎に宣言するとコードが莫大になってしまうのでなんとか避けたいです。

クラスレベルでコントロール配列を宣言して使い回す事はできないでしょうか?


' 例: 

   Dim TextBoxs() As TextBox = {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5} 'コントロール配列 


    Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load

        For i As Integer = 0 To 4

            TextBoxs(i).Text = "ここに入力"

        Next

    End Sub



    Private Sub Button1_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Button1.Click

        For i As Integer = 0 To 4

            TextBoxs(i).Text = String.Empty

        Next

    End Sub

    Private Sub Button2_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Button2.Click

        Dim sum As Integer

        For i As Integer = 0 To 4

            sum += CInt(TextBoxs(i).Text)

        Next i

        TextBox6.Text = sum

    End Sub

投稿者 heaven  (社会人) 投稿日時 2010/8/18 11:22:24
追記ですが、環境開発はVB2005です。
投稿者 YuO  (社会人) 投稿日時 2010/8/18 12:28:59
仕様書上の記述を探しきれなかったのですが,初期化の順序は,
・フィールドの初期化を派生クラス→基本クラスの順序で行う
・コンストラクタを基本クラス→派生クラスの順序で呼び出す
ことによって行われます。

TextBox1などのコントロールは,コンストラクタ中で作成されます。
そのため,フィールドで初期化しても作成される前のオブジェクトが取り出されることになります。

TextBoxsの初期化を,コンストラクタに移せば,やりたいことができると思います。

Public Partial Class Form1
    Private TextBoxes As TextBox()

    Public Sub New ()
        InitializeComponent()
        TextBoxes = New TextBox() { TextBox1, TextBox2, TextBox3, TextBox4, TextBox5 }
    End Sub
End Class
投稿者 るきお  (社会人) 投稿日時 2010/8/18 12:45:02
>・フィールドの初期化を派生クラス→基本クラスの順序で行う
なるほどです。
Dim TextBoxs() As TextBox = {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5} 'コントロール配列  

これが実行されるときはまだ、TextBox1~5がまだNothingなんですね。


回避策として名前で指定する方法も紹介します。
  
Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load

    For i As Integer = 1 To 5
        Me.Controls("TextBox" & i).Text = "ここに入力"
    Next

End Sub

Private Sub Button1_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Button1.Click

    For i As Integer = 1 To 5
        Me.Controls("TextBox" & i).Text = String.Empty
    Next

End Sub

Private Sub Button2_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Button2.Click

    Dim sum As Integer

    For i As Integer = 1 To 5
        sum += CInt(Me.Controls("TextBox" & i).Text)
    Next i

    TextBox6.Text = sum

End Sub
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2010/8/18 13:04:43
> Me.Controls("TextBox" & i).Text = "ここに入力"
この方法の場合、フォーム上に配置した TextBox は操作できますが、
Panel や GroupBox に配置した TextBox は操作できません。

Panel 上のコントロールを拾う場合には、
 Panel1.Controls(名前).Text = "ここに入力"
のように記述するか、もしくは Find メソッドを使って
 Me.Controls.Find(名前, True)(0).Text = "ここに入力"
下位要素まで検索するようにすれば OK です。
投稿者 YuO  (社会人) 投稿日時 2010/8/18 13:23:06
ひゃあ,ごめんなさい。初期化順序が間違っていました。
まさかC#とVBで初期化順序が違うとは思わなかった……。
# すいません,基本C#屋なんです,と言い訳しておきます。

Imports System

Namespace VbLibrary
    Public Class Class1
        Public F1 As New Class3(GetType(Class1).FullName)

        Public Sub New()
            Console.WriteLine("{0}.ctor()"GetType(Class1).FullName)
        End Sub
    End Class

    Public Class Class2
        Inherits Class1

        Public F2 As New Class3(GetType(Class2).FullName)

        Public Sub New()
            MyBase.New()
            Console.WriteLine("{0}.ctor()"GetType(Class2).FullName)
        End Sub
    End Class

    Public Class Class3
        Public Sub New(ByVal s As String)
            Console.WriteLine("{0}.ctor({1})"Me.GetType().FullName, s)
        End Sub
    End Class
End Namespace

using System;

namespace CsLibrary
{
    public class Class1
    {
        public Class3 F1 = new Class3(typeof(Class1).FullName);

        public Class1 ()
        {
            Console.WriteLine("{0}.ctor()", typeof(Class1).FullName);
        }
    }

    public class Class2 : Class1
    {
        public Class3 F2 = new Class3(typeof(Class2).FullName);

        public Class2 ()
            : base()
        {
            Console.WriteLine("{0}.ctor()", typeof(Class2).FullName);
        }
    }

    public class Class3
    {
        public Class3 (string s)
        {
            Console.WriteLine("{0}.ctor({1})", this.GetType().FullName, s);
        }
    }
}

という,同じ動作をするはずのコードに対して,Class2を作成してみたところ,VBでは
VbLibrary.Class3.ctor(VbLibrary.Class1)
VbLibrary.Class1.ctor()
VbLibrary.Class3.ctor(VbLibrary.Class2)
VbLibrary.Class2.ctor()

と,基本クラスのフィールド→基本クラスのコンストラクタ→派生クラスのフィールド→派生クラスのコンストラクタの順序で実行しました。
それに対して,C#では,
CsLibrary.Class3.ctor(CsLibrary.Class2)
CsLibrary.Class3.ctor(CsLibrary.Class1)
CsLibrary.Class1.ctor()
CsLibrary.Class2.ctor()

と,派生クラスのフィールド→基本クラスのフィールド→基本クラスのコンストラクタ→派生クラスのコンストラクタの順序で実行しました。

根本的にこんなのCLIで決まっているだろうと思っていたのですが,甘かったです。
投稿者 heaven  (社会人) 投稿日時 2010/8/18 20:05:38
皆様ご回答ありがとうございます。

YuOさまの方法で無事に思い通りに作ることが出来ました。
非常に助かりました。

また、コントロールが10個以上ある場合は、管理人さまの方法も合理的だと思いましたので参考にさせていただきます。


今後ともよろしくお願いいたします。