VB.NET、C# コントロール配列
VB6を使ったことの有るプログラマーが最初にVB.NETを使用して疑問に思うのは、
どのようにするとコントロールの配列が可能となるかと言うことである。
コントロールとは何かと言う、厳密な定義は抜きにしても、それに対する大方の
回答はオブジェクトの配列を使用しなさいと言うアドバイスである。
ここでは配列を使わずに、あたかも配列であるかのごとく、コントロールを
扱う方法を考えてみる。
仕様
先ずフォームであるが、ラベルとテキストボックス,ボタンを6個づつ置いてある。
位置は適当で、実行時にコードで位置合わせをする。
フォームロード時のコード
C# のコード
public Form1()
{
InitializeComponent();
SetControl();
}
private void SetControl()
{
//ラベルの左スパンと上スパン
const int intOffsetX = 10;
const int intOffsetY = 10;
//コントロールの高さと幅
const int intHeight = 20;
const int intWidth = 100;
//コントロールとコントロールのスパン
const int intVspan = 40 ;
const int intHspan = 10;
//コントロールの位置を設定
for (int i = 1 ; i<7; i++)
{
Label(i).SetBounds(intOffsetX, intVspan * (i - 1) +
intOffsetY, intWidth, intHeight);
Label(i).Text = "ラベル" + i.ToString();
//テキストボックスは2倍の幅を設定
TextBox(i).SetBounds(Label(1).Left + Label(1).Width + intHspan,
intVspan * (i - 1) + intOffsetY, intWidth * 2, intHeight);
TextBox(i).Text = String.Empty;
Button(i).SetBounds(TextBox(1).Left + TextBox(1).Width + intHspan,
intVspan * (i - 1) + intOffsetY, intWidth, intHeight);
Button(i).Text = "ボタン" + i.ToString();
}
//イベントの追加
for (int i = 1 ;i < 7; i++)
{
Button(i).Click += Button_Click;
}
}
VB.NETのコード
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
'ラベルの左スパンと上スパン
Const intOffsetX As Integer = 10, intOffsetY As Integer = 10
'コントロールの高さと幅
Const intHeight As Integer = 20, intWidth As Integer = 100
'コントロールとコントロールのスパン
Const intVspan As Integer = 40, intHspan As Integer = 10
'コントロールの位置を設定
For i As Integer = 1 To 6
With Label(i)
.SetBounds(intOffsetX, intVspan * (i - 1) + _
intOffsetY, intWidth, intHeight)
.Text = "ラベル" & i.ToString
End With
'テキストボックスは2倍の幅を設定
With TextBox(i)
.SetBounds(Label(1).Left + Label(1).Width + intHspan, _
intVspan * (i - 1) + intOffsetY, intWidth * 2, intHeight)
.Text = String.Empty
End With
With Button(i)
.SetBounds(TextBox(1).Left + TextBox(1).Width + intHspan, _
intVspan * (i - 1) + intOffsetY, intWidth, intHeight)
.Text = "ボタン" & i.ToString
End With
Next
'イベントの追加
For i As Integer = 1 To 6
AddHandler Button(i).Click, AddressOf Button_Click
Next
End Sub
上のコードはなるべくVB6のコントロールの配列のコードに似せてある。
「なーんだ何のへんてつもない、コントロールの配列を使った位置設定でじゃ無いですか」
と言われそうである、ところがこの Label(i)等は実は配列では無く関数呼び出しである。
「え!でも With Label(i)
などと Withまで使っていますが?」と言うかもしれない。
でもやはり配列ではないのだ、その証拠に6つずつ置かれているコントロール名はそれぞれ、
Label1 Label2 Label3 .... TextBox1 Textbox2 textbox3... Button1 Button2 Button3...
となっているのだ。
いったいどのようになっているのだろうか? そのヒミツは下のコードにある。
引数の番号を名前に持つコントロールを返す関数
C#のコード
//引数indexに番号を受け取って、その番号が付いたLabelコントロールを返す
private Label Label(int index )
{
return (Label)(this.Controls["Label" + index.ToString()]);
}
//引数indexに番号を受け取って、その番号が付いたTextBoxコントロールを返す
private TextBox TextBox(int index)
{
return (TextBox)(this.Controls["TextBox" + index.ToString()]);
}
//引数indexに番号を受け取って、その番号が付いたButtonコントロールを返す
private Button Button(int index)
{
return (Button)(this.Controls["Button" + index.ToString()]);
}
VB.NETのコード
'引数indexに番号を受け取って、その番号が付いたLabelコントロールを返す
Private Function Label(ByVal index As Integer) As Label
Return DirectCast(Me.Controls("Label" & index.ToString), Label)
End Function
'引数indexに番号を受け取って、その番号が付いたTextBoxコントロールを返す
Private Function TextBox(ByVal index As Integer) As TextBox
Return DirectCast(Me.Controls("TextBox" & index.ToString), TextBox)
End Function
'引数indexに番号を受け取って、その番号が付いたButtonコントロールを返す
Private Function Button(ByVal index As Integer) As Button
Return DirectCast(Me.Controls("Button" & index.ToString), Button)
End Function
上のコードは引数に番号をもらって、コントロール名の最後にその番号が付いたコントロール
そのものを返すのである。
つまり Label(1)は実際は
Label1が返ってきているのである。
ラベルばかりではなくテキストボックスも、ボタンも同じ書式で書かれている。
すなわち配列に見せかけた関数である。
関数の戻り値が、コントロールそのものであるので、殆どの処理が配列と同じ書式で書ける。
インデクサという言葉がある。
ウィキペディア(Wikipedia) インデクサ
要するに配列で無いものを配列に見せかけるコードである。
実はここに書いたコードはインデクサではない。
C#におけるインデクサは引数付きのプロパティであり、VB.NETはデフォルトプロパティ
である。
これに関しては 「インデクサ」 を御覧戴きたい。
C#、VB.NETのコントロールをあたかも
配列のように扱うことが出来るのである。
各ボタンのイベントハンドラはC#は「+=」でVB.NETは「AddHandler」で簡単に追加出来る。
さて問題はボタンのクリックイベントでイベント元のコントロールの
取得には、イベントハンドラーに渡ってくるイベント元のオブジェクトをボタンクラスにキャストして、
そのボタンオブジェクトの名前から、名前に付いている番号を取得している。
ボタンクリック・イベントハンドラ
ボタンをクリックした時のイベントハンドラの処理は下のコードで簡単にイベントが追加出来る。
イベントに関しては 「イベントの入門」 を参照されたし。
C#のコード
Button(i).Click += Button_Click;
VB.NET
AddHandler Button(i).Click, AddressOf Button_Click
ボタンクリック・イベント
さて問題はボタンのクリックイベントでイベント元のコントロールの
取得には、イベントハンドラーに渡ってくるイベント元のオブジェクトをボタンクラスにキャストして、
そのボタンオブジェクトの名前から、名前に付いている番号を取得している。
C#のコード
private void Button_Click(System.Object sender ,System.EventArgs e )
{
//イベントの送り側の名前を取得
string senderName = ((Button)(sender)).Name;
//ボタンのベース名 長さの取得に使用
string strBut = "Button";
//Buttonxxのxxを取得して数字に直している
int index = int.Parse(senderName.Substring(strBut.Length,
senderName.Length - strBut.Length));
//取得したインデックスで各ボタンのクリックイベントを処理
switch(index)
{
case 1:
TextBox(1).Text = "ボタン1が押されました";
break;
case 2:
TextBox(2).Text = "ボタン2が押されました";
break;
default:
TextBox(index).Text = "ボタン" + index.ToString() +"が押されました";
break;
}
}
VB.NET
Private Sub Button_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
'イベントの送り側の名前を取得
Dim senderName As String = DirectCast(sender, Button).Name
'ボタンのベース名 長さの取得に使用
Dim strBut As String = "Button"
'Buttonxxのxxを取得して数字に直している
Dim index As Integer = CInt(senderName.Substring(strBut.Length, _
senderName.Length - strBut.Length))
'取得したインデックスで各ボタンのクリックイベントを処理
Select Case index
Case 1
TextBox(1).Text = "ボタン1が押されました"
Case 2
TextBox(2).Text = "ボタン2が押されました"
Case Else
TextBox(index).Text = "ボタン" & index.ToString & "が押されました"
End Select
End Sub
よく目を凝らして見て欲しい、TextBox(1).Text
TextBox(2).Text TextBox(index).Textも配列
では有りません、番号を引数にした関数呼び出しなのです。
このコードを使うに当たっては一つ注意が必要である、実際にコントロールについている数字以外を、
引数にして関数を呼び出してはいけない、もしその可能性が少しでもある場合は、例に依って適切に
エラー処理を入れる必要が有る。
|