VB.NET: コントロール配列に何が起こったのか

VB.NET でコントロールのコレクションを処理する方法

VB.NET から制御配列が省略されていることは、配列について教えている人にとっては難しい問題です。





  • テキスト ボックスなどのコントロールを単純にコピーし、それを (1 回または数回) 貼り付けてコントロール配列を作成することはできなくなりました。
  • コントロール配列に似た構造を作成するための VB.NET コードは、私が購入したオンラインの VB.NET に関するすべての本で、はるかに長く、はるかに複雑でした。 VB6 に見られる制御配列のコーディングの単純さが欠けています。

VB6 互換ライブラリを参照すると、制御配列のように機能するオブジェクトがそこにあります。言いたいことを理解するには、制御配列を含むプログラムで VB.NET アップグレード ウィザードを使用するだけです。コードはまた醜いですが、動作します。悪いニュースは、互換性コンポーネントが引き続きサポートされることを Microsoft が保証しないということです。

「制御配列」を作成して使用するための VB.NET コードは、はるかに長く複雑です。



Microsoft によると、VB 6 でできることに近いことを行うには、「コントロール配列の機能を複製する単純なコンポーネント」を作成する必要があります。

これを説明するには、新しいクラスとホスティング フォームの両方が必要です。クラスは実際に新しいラベルを作成および破棄します。完全なクラス コードは次のとおりです。



パブリック クラス LabelArray
System.Collections.CollectionBase を継承します。
Private ReadOnly HostForm As _
System.Windows.Forms.Form
パブリック関数 AddNewLabel() _
System.Windows.Forms.Label として
' Label クラスの新しいインスタンスを作成します。
aLabel を新しい System.Windows.Forms.Label として暗くする
' ラベルをコレクションの
' 内部リスト。
Me.List.Add(aLabel)
' Label を Controls コレクションに追加します
' HostForm フィールドによって参照されるフォームの。
HostForm.Controls.Add(aLabel)
' Label オブジェクトの初期プロパティを設定します。
aLabel.Top = カウント * 25
aLabel.Width = 50
aLabel.Left = 140
aLabel.Tag = Me.Count
aLabel.Text = 'ラベル' & Me.Count.ToString
ラベルを返す
終了機能
パブリック サブ 新規( _
ByVal ホストとして System.Windows.Forms.Form)
HostForm = ホスト
Me.AddNewLabel()
サブ終了
デフォルトの Public ReadOnly プロパティ _
Item(ByVal Index As Integer) As _
System.Windows.Forms.Label
得る
Return CType(Me.List.Item(Index), _
System.Windows.Forms.Label)
エンドゲット
End プロパティ
Public Sub Remove()
' 削除するラベルがあることを確認してください。
Me.Count > 0 の場合
' 配列に最後に追加されたラベルを削除します
' ホスト フォーム コントロール コレクションから。
' でのデフォルト プロパティの使用に注意してください
' 配列にアクセスしています。
HostForm.Controls.Remove(Me(Me.Count - 1))
Me.List.RemoveAt(Me.Count - 1)
終了条件
サブ終了
クラス終了

このクラス コードがどのように使用されるかを示すために、それを呼び出す Form を作成できます。以下に示すコードをフォームで使用する必要があります。

|_+_|

まず、これは VB 6 で行っていたようなデザインタイムでの仕事でさえありません! 2 つ目は、これらは配列ではなく、VB.NET コレクション内にあるということです。これは、配列とは大きく異なります。

VB.NET が VB 6 の「コントロール配列」をサポートしていない理由は、「コントロール」「配列」のようなものがないためです (引用符の変更に注意してください)。 VB 6 はバックグラウンドでコレクションを作成し、それを配列として開発者に表示します。しかし、これは配列ではないため、IDE で提供される機能以外ではほとんど制御できません。



一方、VB.NET はそれをオブジェクトのコレクションと呼んでいます。そして彼らは、開発者に王国への鍵を渡し、すべてをオープンに作成します。

これが開発者に与える利点の例として、VB 6 では、コントロールは同じタイプである必要があり、同じ名前である必要がありました。これらは VB.NET の単なるオブジェクトであるため、異なる型を作成し、異なる名前を付けて、同じオブジェクトのコレクションで管理することができます。



この例では、同じ Click イベントが 2 つのボタンとチェックボックスを処理し、どちらがクリックされたかを表示します。 VB 6 では 1 行のコードでそれを実行できます。

プライベート サブ MixedControls_Click( _
ByVal 送信者 As System.Object, _
ByVal e As System.EventArgs) _
Button1.Click、_を処理します
Button2.クリック、_
CheckBox1.Click
' 以下のステートメントは 1 つの長いステートメントでなければなりません!
' ここは狭くするために 4 行になっています
' Web ページに収まる程度
Label2.Text =
Microsoft.VisualBasic.Right(sender.GetType.ToString,
Len(sender.GetType.ToString) -
(InStr(sender.GetType.ToString, 'フォーム') + 5))
サブ終了



部分文字列の計算はちょっと複雑ですが、ここで話していることは実際にはそうではありません。 Click イベントでは何でもできます。たとえば、If ステートメントでコントロールの Type を使用して、さまざまなコントロールに対してさまざまなことを行うことができます。

Frank's Computing Studies Group アレイに関するフィードバック

Frank's Study Group は、4 つのラベルと 2 つのボタンを持つフォームの例を提供しました。ボタン 1 はラベルをクリアし、ボタン 2 はラベルを塗りつぶします。 Frank の元の質問をもう一度読んで、彼が使用した例が、Label コンポーネントの配列の Caption プロパティをクリアするために使用されるループであることに注意してください。これは、その VB 6 コードに相当する VB.NET です。このコードは、フランクが最初に要求したことを実行します!



|_+_|

このコードを試してみると、ラベルのプロパティを設定するだけでなく、メソッドを呼び出すこともできることがわかります。では、なぜ私 (および Microsoft) は、この記事の第 1 部で「醜い」コードを作成するのに苦労したのでしょうか?

古典的な VB の意味での「コントロール配列」であることに異議を唱えなければなりません。 VB 6 コントロール配列は、単なるテクニックではなく、VB 6 構文でサポートされている部分です。実際、この例は、コントロール配列ではなく、コントロールの配列であると説明できます。

パート I で、Microsoft の例は実行時にのみ機能し、設計時には機能しないと不満を漏らしました。フォームからコントロールを動的に追加および削除できますが、すべてをコードで実装する必要があります。 VB 6 のように、コントロールをドラッグ アンド ドロップして作成することはできません。この例は主に設計時に機能し、実行時には機能しません。実行時にコントロールを動的に追加および削除することはできません。ある意味、パート I の例とは正反対です。

従来の VB 6 コントロール配列の例は、VB .NET コードで実装されているものと同じです。 VB 6 コード (これは Mezick & Hillier から取得したものです。 Visual Basic 6 認定試験ガイド 、p 206 - 本の例ではコントロールが表示されないため、わずかに変更されています):

|_+_|

しかし、マイクロソフト (および私) が同意するように、VB 6 制御配列は VB.NET では使用できません。したがって、できることは機能を複製することです。私の記事は、Mezick & Hillier の例にある機能を複製したものです。 Study Group のコードは、プロパティを設定してメソッドを呼び出す機能を複製しています。

要するに、それは本当にあなたが何をしたいかによるということです。 VB.NET では、すべてが言語の一部としてまとめられているわけではありませんが (まだ)、最終的にははるかに柔軟です。

John Fannon のコントロール アレイへの取り組み

John は次のように書いています: 実行時に単純な数値テーブルをフォームに配置したかったので、制御配列が必要でした。それらをすべて個別に配置するという吐き気がしたくなくて、VB.NET を使用したかったのです。マイクロソフトは単純な問題に対して非常に詳細な解決策を提供していますが、非常に小さな問題を解決するには非常に大きなハンマーです。いくつかの実験の後、最終的に解決策にたどり着きました。これが私がやった方法です。

上記の Visual Basic についての例は、オブジェクトのインスタンスを作成し、プロパティを設定して、それを Form オブジェクトの一部である Controls コレクションに追加することによって、フォーム上に TextBox を作成する方法を示しています。

Dim txtDataShow を新しいテキスト ボックスとして表示
txtDataShow.Height = 19
txtDataShow.Width = 80
txtDataShow.Location = 新しいポイント(X, Y)
Me.Controls.Add(txtDataShow)
マイクロソフトのソリューションはクラスを作成しますが、代わりにこれらすべてをサブルーチンでラップすることが可能であると考えました。このサブルーチンを呼び出すたびに、フォーム上にテキスト ボックスの新しいインスタンスを作成します。完全なコードは次のとおりです。

パブリック クラス Form1
System.Windows.Forms.Form を継承

#Region ' Windows フォーム デザイナーで生成されたコード '

Private Sub BtnStart_Click( _
ByVal 送信者 As System.Object, _
ByVal e As System.EventArgs) _
btnStart.Click を処理します

Dim I As Integer
Dim sData を文字列として
I = 1 ~ 5 の場合
sData = CStr(I)
AddDataShow(sData, I) を呼び出す

サブ終了
Sub AddDataShow( _
ByVal sText As String, _
ByVal I As Integer)

Dim txtDataShow を新しいテキスト ボックスとして表示
Dim UserLft、UserTop As Integer
Dim X, Y As Integer
ユーザーレフト = 20
ユーザートップ = 20
txtDataShow.Height = 19
txtDataShow.Width = 25
txtDataShow.TextAlign = _
Horizo​​ntalAlignment.Center
txtDataShow.BorderStyle = _
BorderStyle.FixedSingle
txtDataShow.Text = sText
X = ユーザーレフト
Y = UserTop + (I - 1) * txtDataShow.Height
txtDataShow.Location = 新しいポイント(X, Y)
Me.Controls.Add(txtDataShow)
サブ終了
クラス終了
非常に良い点です、ジョン。これは確かに Microsoft のコードよりもはるかに単純です...なぜ彼らはそのように主張したのでしょうか?

調査を開始するために、コード内のプロパティ割り当ての 1 つを変更してみましょう。さあ変えよう

txtDataShow.Height = 19

txtDataShow.Height = 100
顕著な違いがあることを確認するだけです。

コードを再度実行すると、次のようになります。 ... 同じこと。まったく変化なし。実際、MsgBox (txtDataShow.Height) のようなステートメントで値を表示できますが、プロパティに何を割り当てても、プロパティの値として 20 が得られます。なぜそれが起こるのですか?

答えは、オブジェクトを作成するために独自のクラスを派生させているのではなく、別のクラスに何かを追加しているだけなので、他のクラスのルールに従う必要があるということです。これらの規則では、Height プロパティを変更できないと規定されています。 (まあ...できます。MultilineプロパティをTrueに変更すると、Heightを変更できます。)

なぜ VB.NET は先に進み、コードを実行するのに何か問題があるのではないかと心配することもありません。ただし、コンパイル時に少なくとも警告を表示することをお勧めします。 (ヒント! ヒント! ヒント! Microsoft は聞いていますか?)

パート I の例は、別のクラスから継承しています。これにより、継承するクラスのコードでプロパティを使用できるようになります。この例で Height プロパティを 100 に変更すると、期待どおりの結果が得られます。 (ここでも... 1 つの免責事項: 大きな Label コンポーネントの新しいインスタンスが作成されると、古いインスタンスが隠されます。新しい Label コンポーネントを実際に表示するには、メソッド呼び出し aLabel.BringToFront() を追加する必要があります。)

この単純な例は、オブジェクトを別のクラスに単純に追加することはできますが (これが正しい場合もあります)、オブジェクトを制御するには、オブジェクトをクラスで最も体系的な方法で派生させる必要があることを示しています (あえて言うなら、 「.NET の方法」??) は、新しい派生クラスにプロパティとメソッドを作成して、物事を変更することです。ジョンは最初は納得がいかなかった。彼は、「COO」(Correctly Object Oriented) ではないことによる制限はありますが、彼の新しいアプローチは彼の目的に合っていると述べました。しかし、ごく最近、ジョンは次のように書いています。

' ... 実行時に 5 つのテキストボックスのセットを書き込んだ後、プログラムの後続の部分でデータを更新したかったのですが、何も変更されず、元のデータがまだそこにありました。

古いボックスを取り外し、新しいデータで元に戻すコードを作成することで、問題を回避できることがわかりました。それを行うためのより良い方法は、Me.Refresh を使用することです。しかし、この問題は、テキスト ボックスを追加するだけでなく、削除するメソッドを提供する必要があるという私の注意を引きました。

John のコードでは、グローバル変数を使用して、フォームに追加されたコントロールの数を追跡しているため、メソッド...

Private Sub Form1_Load( _
ByVal 送信者 As System.Object, _
ByVal e As System.EventArgs) _
MyBase.Load を処理します
CntlCnt0 = Me.Controls.Count
サブ終了

次に、「最後の」コントロールを削除できます...

N = Me.Controls.Count - 1
Me.Controls.RemoveAt(N)
John 氏は、「これは少し不器用かもしれません」と述べています。

これは、Microsoft が COM および上記の「醜い」サンプル コードでオブジェクトを追跡する方法です。

ここで、実行時にフォーム上にコントロールを動的に作成するという問題に戻り、「コントロール配列に何が起こったのか」という記事をもう一度見てきました。

クラスを作成したので、希望する方法でコントロールをフォームに配置できるようになりました。

John は、彼が使い始めた新しいクラスを使用して、グループ ボックス内のコントロールの配置を制御する方法を示しました。結局のところ、マイクロソフトは「醜い」ソリューションでそれを正しかったのかもしれません!