Billy Hollis
2003년 5월 14일
요약: Billy Hollis가 Visual Basic .NET으로 컨트롤 배열, 폼 컬렉션 및 모든 컨트롤을 하나의 폼에
제공하는 Controls 컬렉션을 만드는 방법을 보여 줍니다. (18인쇄 페이지)
vbnet05132003_sample.exe
샘플 파일을 다운로드하십시오.
Microsoft Visual Basic 6.0의 주요 기능은 Visual Basic .NET에서 대부분 채택되어 그 기능이 더욱
개선되었습니다. 이제 Visual Basic 6.0에서 제공한 부분적인 기능이 아닌 완전한 개체 지향 기능을 사용하여 과거 불가능했던 일을
수행할 수 있게 되었습니다. 배포 기능이 향상된 것은 물론 웹 인터페이스, 완벽한 COM 세부 사항 등을 제공합니다.
그러나 개발자들과의 대화를 통해 그들이 Visual Basic .NET에서 제거된 기존 Visual Basic 6.0의 몇 가지 기능을
계속 사용하고 싶어한다는 사실을 알게 되었습니다. 이해할 수 있는 부분입니다. 무슨 일이나 익숙해진 상태에서 그 기술을 더 이상 사용할 수 없게
된다는 것은 아쉬운 일이 아닐 수 없습니다.
그러나 유연하고 완벽한 개체 지향 환경에서는 새로운 기술의 적절한 활용을 통해 이전 기술을 다시 사용할 수 있습니다. 이 문서에서는
Visual Basic .NET에서 제외된 기능 중 다음 세 가지 주요 기능에 대해 설명하고자 합니다.
- 컨트롤 배열
- 폼 컬렉션
- 모든 컨트롤을 하나의 폼에 제공하는 Controls 컬렉션
이러한 각 기능을 Visual Basic .NET에서 수행할 수 있는 방법을 보여드리겠습니다.
컨트롤 배열
Visual Basic 6.0까지는 컨트롤 배열이 한 폼에서 이름을 공유하는 동일한 유형의 컨트롤 그룹을 의미했습니다. 일반 컨트롤의 경우
이름만으로 식별되는 반면 컨트롤 배열의 한 구성원에 대한 참조를 가져오려면 이름과 인덱스가 모두 필요했습니다.
컨트롤 배열의 모든 구성원은 공용 이벤트를 공유했습니다. 즉, 배열 내 임의의 컨트롤을 클릭하면 Visual Basic 6.0이 단일
Click 이벤트 처리기로 이벤트를 라우팅했습니다. 이벤트 인수 중 하나가 이벤트를 발생시키는 특정 컨트롤을 나타내는
인덱스였습니다.
Visual Basic 6.0까지 개발자가 컨트롤 배열을 사용한 세 가지 주된 이유는 다음과 같습니다.
- 하나의 이벤트 루틴이 여러 컨트롤에 연결되도록 허용
- For Each 루프에서 컬렉션 형태로 컨트롤에 액세스
- 즉석에서 폼에 새 컨트롤 추가
이러한 기능은 Visual Basic .NET에서도 쉽게 제공할 수 있지만 그 방법이 대부분의 Visual Basic 6.0 개발자에게
익숙하지 않은 것입니다. 위의 각 기능에 해당하는 .NET 기술에 대해 알아보겠습니다.
이벤트에 컨트롤 연결
컨트롤 배열 및 이벤트에 대해 설명하기에 앞서 Visual Basic .NET에서의 이벤트에 대해 알아보아야 합니다. Visual
Basic 6.0과 Visual Basic .NET의 이벤트 기능에는 다소 차이가 있습니다. 처음에는 그 차이를 잘 알지 못할 것입니다.
코드에서 이벤트를 작성하는 방법도 표면적으로는 동일하게 보입니다.
Visual Basic .NET에서는 개체가 이벤트를 갖게 되면(WithEvents 키워드를 사용하여 선언됨) 코드 편집기의
왼쪽 드롭다운 메뉴에 나타납니다. 해당 목록의 개체를 강조 표시하면 해당 이벤트가 오른쪽 드롭다운 메뉴에 나타납니다. 이 드롭다운 메뉴에서
이벤트를 선택하면 이벤트 루틴 셸이 호출됩니다.
Visual Basic 6.0과 Visual Basic .NET에서 이벤트의 차이는 해당 셸 루틴에서 코드를 보는 경우에만 나타납니다.
Visual Basic 6.0의 경우 컨트롤 또는 클래스 인스턴스에 대한 이벤트 루틴은 표준 이름을 가집니다. 이 표준 이름은 개체 이름,
밑줄, 이벤트 이름의 순서로 구성됩니다. 따라서 txtAge 텍스트 상자의 Click 이벤트는 txtAge_Click이
됩니다. 이는 다음과 같이 표시됩니다.
' VB6 version of the click event
Private Sub txtAge_Click()
End Sub
Visual Basic .NET에서도 txtAge의 Click 이벤트는 txtAge_Click입니다. 그러나 이
이름은 단지 코드를 쉽게 읽을 수 있도록 하기 위한 것입니다. 실제로는 이벤트 루틴에 원하는 임의의 이름을 지정할 수 있습니다.
다음은 Click 이벤트에 해당하는 이벤트로 Visual Basic .NET에서 처음 작성한 것입니다(코드를 보다 쉽게 읽을
수 있도록 약간 수정함).
' VB.NET version of the click event
Private Sub txtAge_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtAge.Click
End Sub
이 이벤트 루틴의 경우 루틴을 이벤트에 연결시키는 것은 이름이 아닙니다. 루틴의 이름을 txtAge_Click에서
MyFunkyEventRoutine으로 바꿀 수 있으며 여기서는 차이가 별로 없습니다.
실제로 루틴을 이벤트에 연결하는 것은 루틴 선언의 끝에 있는 Handles절입니다. 이 경우 연결된 이벤트는 txtAge
텍스트 상자의 Click 이벤트입니다. Handles txtAge.Click이 끝에 나타나는
경우 루틴 이름에 관계없이 텍스트 상자를 클릭할 때마다 이벤트 루틴이 계속 발생합니다.
몇 가지 다른 흥미로운 가정을 해볼 수 있습니다. 먼저 끝에 Handles txtAge.Click이
있는 루틴이 두 개 이상 있는 경우입니다. 누군가 txtAge 텍스트 상자를 클릭하면 Handles
txtAge.Click이 있는 모든 루틴이 발생합니다. 이벤트 루틴이 발생하는 순서는 제어할 수 없으므로 특정 순서로 발생시키려
해서는 안 됩니다. 이 점에 대해서는 이 문서 뒷부분에서 다시 설명하겠습니다.
마지막으로 동일한 이벤트에 여러 컨트롤을 라우트할 때 사용하는 기술에 대해 설명하겠습니다. 이벤트 루틴 끝의 Handles 절에는 쉼표를
사용하여 원하는 만큼 이벤트를 나열할 수 있습니다. 다음은 세 개의 다른 텍스트 상자에 의해 생성된 Click 이벤트에 대해
발생하는 위의 이벤트 루틴을 약간 수정한 것입니다.
Private Sub MultiTextBox_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtAge.Click, txtHeight.Click, txtWeight.Click
End Sub
루틴 이름을 txtAge_Click에서 MultiTextBox_Click으로 바꾼 것에 대해 주목하십시오. 이름을 바꾸지 않으면 코드를
읽는 사람이 txtAge 이외의 텍스트 상자에서는 루틴을 사용할 수 있다는 사실을 모를 수 있기 때문입니다.
Visual Basic .NET에서 이 구문을 사용하면 컨트롤 배열을 사용하지 않고도 동일한 이벤트에 컨트롤을 연결할 수 있습니다. 이
구문은 또한 어떤 컨트롤에 대한 Click 이벤트에도 사용할 수 있다는 점에서 유연성이 뛰어납니다. 한 가지 유형의 컨트롤만
처리하는 이벤트 루틴으로 제한되지 않습니다. 단일 이벤트 루틴이 여러 가지 다른 유형의 컨트롤에서 발생하는 Click 이벤트를
포착할 수 있습니다. IchkHasCar 및 chkCertified라는 확인란을 사용하는 경우 다음과 같이 텍스트 상자 및 확인란에서 이벤트를
캡처하도록 위의 루틴을 수정할 수 있습니다.
Private Sub MultiControl_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtAge.Click, txtHeight.Click, txtWeight.Click, _
chkHasCar.Click, chkCertified.Click
End Sub
실제로 루틴은 이벤트가 동일한 인수 목록을 갖는 한 Click 이외의 다른 이벤트를 캡처할 수 있습니다. 예를 들어,
Enter 및 DoubleClick 이벤트는 Click 이벤트와 동일한 인수 목록을 가집니다. 따라서 위의
루틴은 다음과 같은 경우 하나의 텍스트 상자와 하나의 확인란에 대해 이러한 이벤트를 모두 캡처할 수 있습니다.
Private Sub CatchMultipleEvents(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles txtAge.Click, txtAge.DoubleClick, _
txtAge.Enter, chkHasCar.Click, _
chkHasCar.DoubleClick, chkHasCar.Enter
End Sub
이처럼 컨트롤 배열을 사용하지 않는다고 해서 기능이 약화된 것이 아니라 오히려 Visual Basic 6.0보다 이벤트 처리 측면에서
실제적으로 더 유연성이 높아졌습니다.
For Each 루프에서 컨트롤 액세스
Visual Basic .NET에서는 컨트롤 배열을 사용하지 않으므로 한 세트의 컨트롤을 통해 반복해야 하는 경우 컨트롤이 컬렉션의 일부가
되어야 합니다. 이를 수행하기 위한 가장 확실한 방법은 컬렉션을 작성하고 관련 컨트롤을 추가하는 것입니다. 예를 들어, 한 폼에
CheckBox1, CheckBox2 및 CheckBox3의 확인란이 있다고 가정하십시오. 다음과 같이 이 폼에 ArrayList를 선언하고
확인란을 추가할 수 있습니다.
Dim colMyCheckBoxes As ArrayList
Private Sub BuildCheckBoxCollection()
colMyCheckBoxes = New ArrayList
colMyCheckBoxes.Add(CheckBox1)
colMyCheckBoxes.Add(CheckBox2)
colMyCheckBoxes.Add(CheckBox3)
End Sub
일반적으로 사용자는 이 루틴을 폼의 Load 이벤트에서 실행하기를 원합니다. 이 Load 이벤트는 다음과
유사합니다.
Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
BuildCheckBoxCollection()
End Sub
이제 모든 확인란에 영향을 미치는 임의의 작업에 대해 확인란을 쉽게 반복할 수 있습니다. 예를 들어, 폼의 단추 뒤에 다음 코드를 배치하여
선택된 상자 수를 확인할 수 있습니다.
Dim obj As Object
Dim iCount As Integer = 0
For Each obj In colMyCheckBoxes
If TypeOf obj Is CheckBox Then
Dim chkCheckBox As CheckBox
chkCheckBox = CType(obj, CheckBox)
If chkCheckBox.Checked Then
iCount += 1
End If
End If
Next
MsgBox(iCount.ToString & " boxes checked")
그러나 바로 가기를 사용하면 컬렉션을 만들고 관리할 필요가 없습니다. 수집하려는 컨트롤을 패널 내에 배치하면 패널의
Controls 속성을 사용하여 원하는 모든 컨트롤을 보유하고 있는 컬렉션을 자동으로 가져올 수 있습니다.
이 방법이 실제 적용된 예를 보도록 하겠습니다. 패널 Panel1 을 폼에 추가하고 세 개의 확인란을 패널로 이동시킨다고 가정하십시오.
이제 코드를 제거하여 컬렉션 colMyCheckBoxes를 작성할 수 있으며 단추 뒤의 코드는 다음과 같습니다.
Dim ctl As Control
Dim iCount As Integer = 0
For Each ctl In Panel1.Controls
If TypeOf ctl Is CheckBox Then
Dim chkCheckBox As CheckBox
chkCheckBox = CType(ctl, CheckBox)
If chkCheckBox.Checked Then
iCount += 1
End If
End If
Next
MsgBox(iCount.ToString & " boxes checked")
이 기술의 한 가지 장점은 컬렉션이 컨트롤만을 포함한다는 것입니다. 즉, 일부 작업에서는 컬렉션 요소를 특정 유형으로 강제로 적용하기 위해
Ctype이 필요하지 않습니다. 예를 들어, 컬렉션 내 모든 컨트롤의 글꼴을 굵게 바꾸려는 경우 코드는 다음과 같습니다.
Dim ctl As Control
For Each ctl In Panel1.Controls
Dim fnt As New Font(ctl.Font, FontStyle.Bold)
ctl.Font = fnt
Next
모든 컨트롤이 Font 등록 정보를 포함하는 다형적 인터페이스를 공유하므로 컬렉션의 모든 컨트롤에 대해 위의 코드가 굵은
글꼴로 표시됩니다.
두 기능의 결합
여러 컨트롤에서 하나의 이벤트를 공유하는 기술과 컬렉션에 컨트롤을 배치하는 기술은 개별적으로는 아무런 문제가 없습니다. 그러나 이 기술을
연결해주는 수단이 없으며 새 컨트롤이 이벤트를 공유할 때마다 첫 번째 이벤트 기술을 사용하기 위해 몇 가지 코딩을 해야 합니다.
Visual Basic .NET에서는 몇 가지 고급 이벤트 기능을 사용하여 위의 두 기능을 결합할 수 있는 방법을 만들 수 있습니다.
사용자 대신 이벤트를 동적으로 연결해주는 특수한 버전의 패널을 만들 수 있습니다. 이런 방법을 사용하면 컨트롤 배열을 관리하는 폼에 코드를
작성하지 않고도 컨트롤 배열 기능과 아주 유사한 결과를 얻을 수 있습니다.
패널에 포함되어 있는 컨트롤이 이벤트를 발생시키면 이벤트가 공용 루틴으로 수집된 후 패널을 호스팅하는 폼에서 다시 발생하도록 이벤트를
연결하려 합니다. 이 방법은 매우 쉽습니다. 다음을 수행하십시오.
- Visual Studio® .NET에서 새 Windows Control Library 프로젝트를 작성합니다. 이름은
ControlPanelArray로 지정하십시오.
- 솔루션 탐색기에서 UserControl1.vb 파일 이름을 ControlPanelArray.vb로 변경하십시오.
- ControlPanelArray.vb 코드로 이동합니다. 처음 몇 줄은 다음과 같습니다.
Public Class UserControl1
Inherits System.Windows.Forms.UserControl
Change them to look like this:
Public Class ControlPanelArray
Inherits System.Windows.Forms.Panel
이는 새로운 패널 유형에 클래스 이름(ControlPanelArray)을 부여하고 상속하는 기본 클래스를 Windows
Forms Panel 컨트롤로 지정합니다.
- 이벤트가 모든 컨트롤을 상속받는 기본 Control 유형에 대한 인터페이스의 일부이므로 새
ControlPanelArray가 원하는 수만큼의 다른 유형 이벤트를 처리하도록 조정할 수 있습니다. 이 예의 경우
ControlPanelArray가 패널 내 각 컨트롤에 대한 다음 이벤트를 관리합니다.
이들 각각에 대해 ControlArrayPanel에서 컨트롤에 의해 생성된 이벤트를 포착해서 통합할 수 있습니다. 통합된
이벤트는 이후 ControlArrayPanel이 있는 폼에서 발생할 수 있습니다. 즉,
ControlArrayPanel이 사용하는 폼에서 이벤트가 발생하도록 ControlArrayPanel이 이벤트를
선언해야 한다는 것을 의미합니다. 이벤트는 다음과 같이 선언될 수 있습니다.
Public Event InternalControlClick(ByVal sender As Object, _
ByVal e As EventArgs)
Public Event InternalControlKeyPress(ByVal sender As Object, _
ByVal e As KeyPressEventArgs)
Public Event InternalControlMouseUp(ByVal sender As Object, _
ByVal e As MouseEventArgs)
이 코드는 Windows Forms Designer Generated Code라는 레이블이 있는
코드 영역의 바로 밑에 배치해야 합니다.
- ControlArrayPanel에서 컨트롤에 대한 개별 이벤트를 포착하려면 동적으로 연결하는 몇몇 이벤트 처리기가
필요합니다. 포착하려는 모든 유형의 이벤트에 이러한 이벤트 처리기 중 하나가 필요합니다. 다음은 단계 4에서 나열한 이벤트 유형에서 사용할 수
있는 세 가지 이벤트 처리기에 대한 코드입니다.
Private Sub InternalClickHandler(ByVal sender As Object, _
ByVal e As EventArgs)
RaiseEvent InternalControlClick(sender, e)
End Sub
Private Sub InternalKeyPressHandler(ByVal sender As Object, _
ByVal e As KeyPressEventArgs)
RaiseEvent InternalControlKeyPress(sender, e)
End Sub
Private Sub InternalMouseUpHandler(ByVal sender As Object, _
ByVal e As MouseEventArgs)
RaiseEvent internalControlMouseUp(sender, e)
End Sub
ControlArrayPanel의 내부에 있는 이 이벤트는 해당하는 통합 이벤트를 발생시키고 이 통합 이벤트는 다시 사용 중인
폼에서 ControlArrayPanel 외부에 발생시킵니다. 위의 코드는 단계 4의 이벤트 선언 바로 밑에 배치해야 합니다.
- 컨트롤이 패널에 추가되면 이 이벤트 처리기를 연결해야 합니다. 이 작업은 ControlArrayPanel에 대한
ControlAdded 이벤트로 수행해야 합니다. 다음은 단계 5에서 추가한 코드 바로 밑에 나타날 수 있는
ControlAdded 이벤트에 대한 전체 코드입니다.
Private Sub ControlArrayPanel_ControlAdded(ByVal sender As Object, _
ByVal e As System.Windows.Forms.ControlEventArgs) _
Handles MyBase.ControlAdded
AddHandler e.Control.Click, AddressOf InternalClickHandler
AddHandler e.Control.KeyPress, AddressOf InternalKeyPressHandler
AddHandler e.Control.MouseUp, AddressOf InternalMouseUpHandler
End Sub
AddHandler 키워드에 익숙하지 않은 경우 이 키워드는 이벤트를 처리할 수 있는 특정 서브 루틴으로 컨트롤 및 구성
요소에 의해 발생한 이벤트를 동적으로 연결합니다. 이 방법은 이 문서 앞 부분에서 설명한 Handles 절에 대한 런타임 대안으로
이벤트를 처리할 수 있는 루틴에 특정 컨트롤에 대한 이벤트를 연결하는 것과 동일한 결과를 얻게 됩니다.
연결할 서브 루틴에는 이벤트를 승인할 수 있는 올바른 인수 목록이 있어야 합니다. 예를 들어,
InternalClickHandler 루틴에는 Object 유형의 sender와
EventArgs 유형의 e 두 인수가 사용됩니다. Click 이벤트의 인수 목록에 동일한 인수가 있으므로
이 루틴은 Click 이벤트로 연결할 수 있습니다. 앞에서 설명한 대로 이 이벤트는 동일한 인수 목록을 가지므로 이 루틴은
DoubleClick 및 Enter 이벤트에도 연결할 수 있습니다.
AddHandler를 사용하여 e.Control.KeyPress를
InternalClickHandler에 연결하면 "메서드에 대리자와 동일한 서명이 없습니다"라는 구문 오류가 나타납니다. 여기서
타원은 메서드 설명 및 이벤트의 인수 목록(메서드 서명이라고도 함)으로 대체됩니다. 자세한 오류 메시지를 표시하려면 이 방법을 사용해 보십시오.
이 논리가 ControlAdded 이벤트에 배치되면 ControlArrayPanel에 추가되는 모든 컨트롤이
Click 이벤트, KeyPress 이벤트 및 MouseUp 이벤트를 포착해서 적절한 이벤트 통합 루틴으로
전달합니다. 앞에서 설명한 대로 이 루틴은 사용 중인 폼에 다시 이벤트를 발생시킵니다.
- 강력한 ControlArrayPanel을 만들려면 ControlArrayPanel에서 컨트롤을 제거할 때
처리기도 함께 제거해야 합니다. 이렇게 하려면 아래 코드와 같은 ControlRemoved 이벤트를 작성하여 단계 6에서 추가한
ControlAdded 이벤트 바로 밑에 코드를 배치하십시오.
Private Sub ControlArrayPanel_ControlRemoved(ByVal sender As Object, _
ByVal e As System.Windows.Forms.ControlEventArgs) _
Handles MyBase.ControlRemoved
RemoveHandler e.Control.Click, AddressOf InternalClickHandler
RemoveHandler e.Control.KeyPress, AddressOf InternalKeyPressHandler
RemoveHandler e.Control.MouseUp, AddressOf InternalMouseUpHandler
End Sub
RemoveHandler는 이벤트를 처리하는 ControlArrayPanel의 내부에 있는 루틴과 컨트롤의 이벤트
간 연결을 지원합니다. RemoveHandler 구문은 AddHandler와 동일합니다.
- 이제 컨트롤이 완성되었습니다. 컨트롤을 빌드하여 오류가 없는지 확인하십시오.
- 컨트롤을 테스트하려면 파일 | 프로젝트 추가 | 새 프로젝트를 선택한 후 새 프로젝트 대화 상자에서 프로젝트
유형으로 Windows 응용 프로그램을 선택합니다. 새 프로젝트의 이름을 TestControlArrayPanel로 지정하고
확인을 누르십시오.
- 방금 추가한 테스트 프로젝트는 디버그 | 시작을 선택할 때(또는 도구 모음에서 시작 단추를 누를 때) 시작되는
프로젝트여야 합니다. 마우스 오른쪽 단추로 솔루션(솔루션 탐색기의 첫 번째 항목)을 누르고 속성을 선택하십시오. 속성
대화 상자 내 한 개의 시작 프로젝트 드롭다운 메뉴에서 TestControlArrayPanel을
선택하십시오.
- 끌어서 놓기 모드에서 ControlArrayPanel 컨트롤을 사용하려면 도구 상자에 추가해야 합니다. 도구 상자에서 마우스
오른쪽 단추로 Window Forms 탭을 클릭하여 도구 상자 사용자 지정(Visual Studio .NET 2002)
또는 항목 추가/제거(Visual Studio .NET 2003)를 선택하십시오. ControlArrayPanel의
프로젝트를 찾은 후 bin 디렉터리를 찾으십시오. 이 디렉터리에는 ControlArrayPanel.dll이라는 DLL이 있습니다. 이 DLL을
선택하고 열기를 선택한 다음 확인을 클릭하십시오. 도구 상자 아래 쪽에 ControlArrayPanel
아이콘이 나타납니다.
- ControlArrayPanel 컨트롤을 TestControlArrayPanel의 비어 있는 Form1로 끌어다
놓으십시오. ControlArrayPanel1이라는 이름이 지정됩니다. 그런 다음 세 개의 라디오 단추를
ControlArrayPanel로 끌어다 놓고 다음과 같이 이름 및 텍스트 속성을 설정하십시오.
| 속성 이름 |
다음으로 텍스트 색 설정 |
| btnRed |
빨강 |
| btnYellow |
노랑 |
| btnLightGreen |
연한 녹색 |
- ControlArrayPanel로 단추를 끌어다 놓습니다. 이름을 btnRestore로 설정하고 해당 텍스트
속성을 색 복원으로 설정하십시오.
- Form1의 코드 창으로 이동합니다. 코드 편집기의 위쪽에 있는 왼쪽 드롭다운 메뉴에서 ControlArrayPanel1을
선택하십시오. 그런 다음 오른쪽 드롭다운 메뉴에서 InternalControlClick 이벤트를 선택하고 이벤트 안에 다음 코드를
추가하십시오.
Dim ctlSender As Control
ctlSender = CType(sender, Control)
Select Case ctlSender.Name.ToUpper
Case "optRed".ToUpper
ControlArrayPanel1.BackColor = Color.Red
Case "optYellow".ToUpper
ControlArrayPanel1.BackColor = Color.Yellow
Case "optLightGreen".ToUpper
ControlArrayPanel1.BackColor = Color.LightGreen
Case "btnRestore".ToUpper
ControlArrayPanel1.BackColor = SystemColors.Control
End Select
이 이벤트는 ControlArrayPanel의 컨트롤을 클릭할 때 발생합니다. sender 인수는 클릭한 ControlArrayPanel의 컨트롤을 식별합니다. 이 인수의 유형은
Object이므로 컨트롤 형태로 액세스하려면 Control 유형으로 캐스팅해야 합니다. 이 작업은 이 코드의 처음 두
줄에서 수행합니다. 그러면 컨트롤을 정상적으로 조작할 수 있습니다.
- 마지막으로 폼의 디자인 보기로 다시 이동하고 ControlArrayPanel 의 단추를 두 번 클릭하여 단추의
Click 이벤트를 작성합니다. 단추의 이벤트에 다음 코드를 추가하십시오.
MsgBox("Restore color button pressed")
Dim ctl As Control
For Each ctl In ControlArrayPanel1.Controls
If TypeOf (ctl) Is RadioButton Then
Dim ctlRadioButton As RadioButton
ctlRadioButton = CType(ctl, RadioButton)
ctlRadioButton.Checked = False
End If
Next
이 코드는 일반 패널을 사용할 때처럼 ControlArrayPanel에 있는 컨트롤을 통해 반복할 수 있음을 보여 줍니다.
따라서 이벤트 통합 기술과 컬렉션 내 여러 컨트롤에 있는 하나의 클래스 ControlArrayPanel에 액세스하는 기술 두 가지를
결합했습니다.
- 응용 프로그램을 실행 및 테스트합니다. 노랑으로 표시된 라디오 단추를 누르면 그림 1과 같은 결과가 나타납니다. 단추를 누르면 폼에서 두
개의 이벤트 루틴이 발생합니다.
한 이벤트가 여러 처리기에 연결되는 경우 특정 순서로 발생하는 이벤트 처리기에 의존해서는 안 됩니다. 각 처리기의 논리가 다른 처리기의
논리와 완전히 독립되도록 만들어야 합니다.
그림 1. ControlArrayPanel의 세 가지 라디오 단추와 한 가지 단추 노랑 라디오 단추를 누르면
라디오 단추의 Click 이벤트가 Form1의 통합 이벤트 처리기로 라우트됩니다.
즉석에서 폼에 컨트롤 추가
Visual Basic 6.0 폼에 컨트롤을 동적으로 추가하려면 컨트롤이 컨트롤 배열의 일부여야 합니다. 컨트롤 배열을 작성할 "시드"
컨트롤이 이미 폼에 존재해야 하므로 이것은 중요한 제한 사항입니다.
Visual Basic 6.0에서 컨트롤을 동적으로 폼에 배치하는 것과 관련된 모든 제한 사항이 Windows Forms에서는 적용되지
않습니다. 모든 컨트롤을 동적으로 폼에 배치할 수도 있습니다. 비주얼 디자이너가 컨트롤을 만들고 해당 속성을 설정할 코드를 작성하기 때문입니다.
이와 동일한 구문을 사용하여 필요한 경우 폼에 컨트롤을 배치할 수 있습니다.
폼에 새 도구 상자 txtExtraStuff를 두려는 경우 다음과 같은 논리를 사용할 수 있습니다.
Dim txtExtraStuff As New TextBox
txtExtraStuff.Top = 40
txtExtraStuff.Left = 180
txtExtraStuff.Text = "Some stuff"
txtExtraStuff.Width = 130
Me.Controls.Add(txtExtraStuff)
다른 클래스와 같이 새 컨트롤이 인스턴스화되어 관련 속성을 설정할 수 있습니다. 속성을 설정하지 않으면 기본값이 적용됩니다.
마지막 줄은 폼에 컨트롤이 나타나게 만듭니다. 폼의 Controls 컬렉션에 컨트롤을 추가하고 표면을 색칠하면 폼에 컨트롤을
포함시킬 수 있습니다.
컨트롤을 추가한 후에는 이 컨트롤을 이벤트 처리기에 연결하는 것이 바람직합니다. 이전 예에서 사용한 AddHandler 구문을
여기에 사용할 수 있습니다. 포착하려는 새 컨트롤의 모든 이벤트에 대해 AddHandler 줄을 포함시키십시오. 물론 미리 이벤트
처리기 루틴에 대한 코드를 작성해야 합니다.
즉석에서 컨트롤을 추가하는 이 기술은 유연성과 성능면에서 매우 뛰어나므로 한 번 사용해보면 컨트롤 배열을 사용하여 폼에 컨트롤을 추가하는
것보다 좋은 방법이라는 사실을 알게 될 것입니다.
폼 컬렉션 가져오기
Visual Basic 6.0 개발자들은 로드된 특정 폼 또는 로드된 특정 유형의 폼 수 등 무엇인가를 찾을 때 현재 응용 프로그램에
로드된 폼을 통해 반복하는 것을 선호합니다. 그러나 Windows Forms에는 Forms 컬렉션이 없으므로 Visual Basic
.NET에서 이 기능을 수행하려면 약간의 추가 작업이 필요합니다.
이 문서 앞 부분에서 컨트롤에 대한 컬렉션을 작성한 것과 유사한 방법으로 로드된 폼을 보유할 사용자의 고유 컬렉션도 손쉽게 작성할 수
있습니다. 문제는 응용 프로그램 내 원하는 위치에서 어떻게 컬렉션에 액세스하고 어떻게 하면 응용 프로그램에서 일관되게 폼을 추가할 수 있는가
하는 것입니다.
이러한 두 가지 요구를 해결할 수 있는 한 가지 좋은 방법은 응용 프로그램에서 클래스의 공유 속성으로 컬렉션을 작성하는 것입니다. 클래스는
공유 메서드로 폼을 표시할 수도 있으며 이 메서드는 폼이 표시되기 전에 컬렉션에 이미 폼이 있는지 확인할 수 있습니다.
이러한 클래스를 작성하려면 Visual Basic .NET에서 새 Windows 응용 프로그램을 시작합니다. 프로젝트 | 클래스
추가를 선택하고 새 클래스의 이름을 MyForms로 지정한 후 새 클래스의 코드를 다음 코드로 바꾸십시오.
Public Class MyForms
Private Shared m_colFormsCollection As New ArrayList
Public Shared Property Forms() As ArrayList
Get
Return m_colFormsCollection
End Get
Set(ByVal Value As ArrayList)
m_colFormsCollection = Value
End Set
End Property
Public Shared Sub Show(ByVal frm As Form)
Dim obj As Object
Dim bFound As Boolean = False
For Each obj In m_colFormsCollection
Dim frmCurrent As Form
frmCurrent = CType(obj, Form)
If frm Is frmCurrent Then
bFound = True
Exit For
End If
Next
If Not bFound Then
m_colFormsCollection.Add(frm)
End If
frm.Show()
End Sub
End Class
이제 Windows 응용 프로그램에 다른 폼을 추가합니다. 이름을 Form2로 바꾼 후 Form1의 디자인 표면으로 이동하고 두 개의 단추
위로 끌어다 놓으십시오. Button1의 Click 이벤트에 다음 코드를 배치하십시오.
Dim f As New Form2
MyForms.Show(f)
Button2의 Click 이벤트에 다음 코드를 배치하십시오.
Dim obj As Form
For Each obj In MyForms.Forms
Dim frm As Form = CType(obj, Form)
frm.Visible = False
Next
프로그램을 실행 및 테스트합니다. 첫 번째 단추를 두 번 눌러 Form2의 인스턴스를 가져오십시오. 그런 다음 두 번째 단추를 누르면
Form2의 모든 인스턴스가 사라집니다.
많은 Visual Basic 6.0 개발자들은 글로벌 변수로 이 문제를 해결해왔습니다. 예를 들어, Forms 컬렉션에 대한 글로벌 변수를
사용하는 경우입니다. 그러나 위에서처럼 공유 구성원이 있는 클래스를 사용하는 것이 보다 바람직합니다. 이 클래스는 글로벌 변수와 같이 프로젝트
내 어느 위치에서나 사용할 수 있으면서도 컬렉션을 보다 효율적으로 관리할 수 있는 논리를 가지고 있습니다. 위의 Show 메서드를
사용하면 컬렉션에 중복 폼을 추가하는 등의 시도는 할 수 없습니다.
Controls 컬렉션의 차이점
Visual Basic 6.0의 경우 폼의 Controls 컬렉션은 컨트롤이 프레임과 같은 폼 내 다른 컨트롤 내부에 있는지
여부에 관계없이 모든 컨트롤을 폼으로 반환합니다. 이는 그림 2처럼 내부에 두 개의 단추가 있고 외부에 한 개의 단추가 있는 프레임을 포함하는
Visual Basic 6.0 폼을 작성하면 확인이 가능합니다.

그림 2. Visual Basic 6.0에서 Controls 컬렉션을 테스트하는 Visual Basic 6.0
폼
Visual Basic 6.0에서 Command3의 Click 이벤트 뒤에 다음 코드를 배치하십시오.
Dim ctl As Control
For Each ctl In Me.Controls
Debug.Print ctl.Caption
Next ctl
프로그램을 실행하고 Command3을 누르면 디버그 창에 다음 네 줄이 나타납니다.
Command3
Frame1
Command2
Command1
Visual Basic .NET에서 같은 작업을 수행하면 다른 결과가 나타납니다. Visual Basic .NET에서 그림 3과 같이
GroupBox 및 세 개의 단추가 있는 유사한 폼을 작성하십시오.

그림 3. Visual Basic .NET에서 Controls 컬렉션을 테스트하는 폼
Button3의 Click 이벤트에 다음 논리를 배치하십시오.
Dim ctl As Control
For Each ctl In Me.Controls
Console.WriteLine(ctl.Text)
Next
이 프로그램을 실행하고 Button3을 누르면 출력 창에 다음 두 줄만 출력됩니다.
Button3
GroupBox1
Form1의 Controls 컬렉션에 없기 때문에 GroupBox의 두 단추에 대한 출력은 나타나지 않습니다. 이들 단추는
GroupBox1의 Controls 컬렉션에 있습니다.
Windows Forms의 경우 다른 컨트롤 내부에 있는 컨트롤을 임의의 수준으로 설정할 수 있습니다. 그렇다면 폼의 모든 컨트롤은 어떻게
가져올 수 있을까요?
이런 경우 재귀가 필요합니다. 마지막 수준에 도달할 때까지 임의의 컨테이너 수준을 재귀하고 임의의 수준에서 발견된 모든 컨트롤을 수집하는
함수를 작성할 수 있습니다.
다음 두 함수를 위의 Visual Basic .NET 폼에 추가하십시오.
Private Function AllControls(ByVal frm As Form) As ArrayList
Dim colControls As New ArrayList
AddContainerControls(frm, colControls)
Return colControls
End Function
Private Sub AddContainerControls(ByVal ctlContainer As Control, _
ByVal colControls As ArrayList)
Dim ctl As Control
For Each ctl In ctlContainer.Controls
colControls.Add(ctl)
AddContainerControls(ctl, colControls)
Next
End Sub
첫 번째 함수는 모든 컨트롤을 보유하는 데 사용되는 ArrayList를 설정하고 두 번째 함수는 여기에 컨트롤을 추가합니다. 각 함수는
이와 동시에 각 컨트롤이 다른 컨트롤을 포함하는지 반복해서 확인합니다.
이제 새 단추 Button4를 폼에 추가하고 그 뒤에 다음 논리를 배치하십시오.
Dim ctl As Control
For Each ctl In AllControls(Me)
Console.WriteLine(ctl.Text)
Next
폼을 실행하고 단추를 누르면 그 내용에 관계없이 폼의 모든 컨트롤에 대한 출력 줄이 나타납니다. 예를 들어, GroupBox의 패널 및
패널 내부의 확인란 두 개 등 몇 가지 컨트롤을 폼에 추가한다고 가정하십시오. 최종 결과는 그림 4와 유사하게 나타납니다.

그림 4. Visual Basic .NET에서 새 AllControls 컬렉션을 테스트하는 폼
이제 프로그램을 실행하고 Button4를 누르면 다음과 같은 텍스트가 출력됩니다.
Button4
Button3
GroupBox1
CheckBox2
CheckBox1
Button2
Button1
비어 있는 줄에 유의하십시오. 이 줄은 GroupBox에 들어 있는 패널에 해당하는 줄입니다. 기본적으로 패널 컨트롤의 속성은 비어 있는
텍스트입니다.
결론
Visual Basic 6.0에서 Visual Basic .NET으로의 이동은 약간 두려운 작업일 수 있습니다. Visual Basic
.NET은 완전한 개체 지향 개발 환경을 제공하기 위해 컨트롤 배열(개체 기능의 차이 연결을 의미)과 같은 Visual Basic 6.0의 일부
기능을 남겨두었습니다. 이러한 기능을 사용하지 못하게 된 것에 실망할 수도 있지만 앞에서 설명한 것처럼 거의 모든 경우 이 기능을 그대로 사용할
수 있습니다. 접근 방법에 차이가 있을 뿐입니다. 또한 Visual Basic .NET의 새 기능은 유연성과 성능을 크게 향상시켜 이러한 전환에
박차를 가할 것입니다.
Billy Hollis는 Visual Basic .NET을 처음으로 다룬 VB.NET Programming with the
Public Beta라는 책을 Rocky Lhotka와 공동 집필했으며 업계의 주요 회의에서 정기적으로 강연하고 있습니다. 2001년
Microsoft의 MSDN 지역 담당 이사를 역임했으며 현재는 Microsoft의 INETA 강연자 협회의 회원입니다. 그리고 상업용
소프트웨어와 스마트 클라이언트 개발을 전문적으로 취급하는 .NET 컨설팅 회사를 직접 운영하고 있습니다. 또한 미국 전역에서 Visual
Basic.NET 강의를 하고 있습니다.
최종 수정일: 2003년 8월 19일