PROGRAMMING WORKSHOP

.Net FrameWork,VB.Net | UserControl,Timer

사용자정의 콘트롤--UserControl

VB.Net에는 많은 콘트롤들이 제공되지만,
여러개의 콘트롤을 조합하여 하나의 콘트롤로 만들어서 사용하면
편리한 경우가 많다
개발자가 여러개의 제공되는 콘트롤을 하나로 묶어서 하나의 콘트롤로..
바로 이 하나의 콘트롤로 만드는 작업을 제공해주는 콘트롤이
UserControl이다



프로젝트메뉴에서 Add UserControl을 크릭하면
UserControl이 프로젝트탐색기(혹은 설정에 따라서 소루션탐색기)
만들어지게 된다..이름을 적당히 임의로 지어주고..
여기에서는 myControl이라고 지어주고..
그림과 같이 Lable 콘트롤 4개와 NumberUpDown콘트롤을 하나 그려 넣는다



위와 같이 그려넣는 것도 실은 프로그래밍적으로 할수 있지만
디자인 타임에 그려 넣었다
이제 프로그래밍적으로 런타임에 서식등을 처리하게 하자
빌드(Build)메뉴에서 빌드소루션(Build solution)을 실행시켜야
위에서 만든 Usercontrol이 도구상자에 아래와 같이 나타난다



이것을 window폼에다가 다른 콘트롤을 끌어다가 사용하듯이
하여도 되겠지만, 여기에서는 프로그래밍적으로 처리할것이니
그냥 냅두고..
끌어다가 사용하게 하려면, UserControl상에서 좀더 디자인타임에
서식등을 제대로 주어야 되겠지..여기서는 생략..
그냥 RunTime에 처리하도록 하자

UserControl의 이름을 myControl이라고 지어주었고
이 UserControl의 코드보기를 열면 그림과 같이 목록상자에 New 메소드가 보인다
이것을 크릭하면 개체가 생성되면서 일어나는 생성자(constructor)이벤트프로시져이다



이 New메소드에 생성되면서 어떤 컨트롤은 높이를 얼마로하고,
어떤 컨트롤은 Text속성값을 어떻게 하고 등의 주문을 할수 있으면좋을 것이다
즉 주문생산하면서 개체가 만들어지는 순간 다양한 표현이 가능한 것이다
하지만 위의 New 메소드는 매개변수가 하나도 없다..
매개변수를 전달하면, 무언가 생성되면서 뚝딱거리고 만들어 줄수 있지 않을까??
그래서 New메소드를 하나 더 만들수 있다..
같은 이름올 메소드를 더 만들수 있다구???!!
VBA에서는 없었던 이야기다..
VBA에서는 같은 이름의 변수나 프로시져등은 에러가 난다..
하지만 VB.Net에서는 같은 이름으로 프로시져를 또 만들수 있다
아래와 같이

Public Class myControl
''본래있던 매개변수 없는 New()는 지워도 좋고, 그냥 놓아두어도 좋고
    Public Sub New()
        ' 아래 한줄은 VB.Net에서 생성자 프로시져(New())할때 자동으로 작성되는 것
        InitializeComponent()
    End Sub
''OverLoading 메소드.. 같은 이름으로 매개변수를 전달받을수 있는 것	
    Public Sub New(sA As String, sB As String, sOpe As String, sEqu As String)

        ' 아래 한줄은 VB.Net에서 생성자 프로시져(New())할때 자동으로 작성되는 것
        InitializeComponent()
''아래와 같이 매개변수를 받아서 각각 대강 그려주었던 라벨콘트롤과 NumberUpDown콘트롤의 서식을 할수 있다
        Dim oFont As New Font("맑은 고딕", 20, FontStyle.Bold)
        Dim oFont1 As New Font("맑은 고딕", 12, FontStyle.Bold)
        Dim iX As Integer
''전달받은 매개변수를 각 콘트롤의 Text속성값으로 사용한다
''{...,...,...}는 VBA에서 Array함수와 같다고 보시면 된다..
''콘트롤들을 배열화하여 순환하는 것
''NumberUpDown콘트롤의 경우와 Label콘트롤두개의 분기하여 처리한다
        For Each ctl As Control In {lblA, lblB, lblOpe, lblEqu, numResult}
            If TypeOf ctl IsNot Label Then
                ctl.Font = oFont1
                ctl.Text = ""
            Else
                ctl.Font = oFont
                ctl.Text = {sA, sOpe, sB, sEqu}(iX)
                iX += 1
            End If
        Next

    End Sub
''myControl의 메소드를 하나 만들어주자..	
  Public Function isOK() As Boolean
        Dim bOk As Boolean = False
        Try
            Dim iA As Integer = lblA.Text
            Dim iB As Integer = lblB.Text
            Dim iRes As Integer = numResult.TextAlign

            Select Case lblOpe.Text
                Case "+"
                    bOk = iA + iB = iRes
                Case "-"
                    bOk = iA - iB = iRes
                Case "x"
                    bOk = iA * iB = iRes

                Case "/"
                    bOk = iA / iB = iRes
            End Select
        Catch ex As Exception

        End Try

        Return bOk


    End Function
End Class

이것을 메소드의 OverLoading 이라고 한다..
OverLoading 이라는 용어 하나 챙기시고..

다른 콘트롤을 도구상자에서 선택하여 폼에 그려 넣듯이
myControl도 끌어다가 놓을수 있지만..
여기에서는 프로그래밍적으로 Form이 로딩될때 만들어 넣는 것으로 하자

Dim myCtl As MyControl=New MyControl(매개변수1,매개변수2,매개변수3,매개변수4)

와 같이 하면 컨트롤이 생성되는 것은 잘 알고 계실것이고..
아하..생성될때 myControl 속의 라벨콘트롤등의 값을 주게 되겠구나..
컨트롤마다 라벨의 Text값이 달라지게 표현된다는 이야기로군..
그래서 Window Form의 로딩될때의 Load 이벤트프로시져에 아래와 같이 코딩하자

Public Class Form1
    '' Timer 개체 이벤트가 나타나게 하기 위하여 WithEvents 를 사용한다
    WithEvents myTimer As Timer
    '' Start Button의 이벤트를 활용하기 위하여 WithEvents
    WithEvents myButton As Button
    ''현재 남은 시간을 기록하기 위한 라벨
    Dim myLabel As Label
    ''현재 남은 시간을 보관하기 위한 변수
    Dim iTimeLeft As Integer



    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        ''UserContol로 만든 사용자정의 컨트롤 myControl
        Dim oMyControl As myControl
        ''컨트롤의 사이간격
        Dim iGap As Integer = 6
        '' 컨트롤의 높이
        Dim iHeight As Integer = 30
        '' 첫째 라벨의 Text값
        Dim sA As String = ""
        ''두번째 라벨의 Text값
        Dim sB As String = ""
        ''연산자(+,-,x,/)
        Dim sOpe As String = ""
        ''= 문자
        Dim sEqu As String = "="
        ''폼의 높이를 계산하기 위한 변수
        Dim iHeight_ As Integer
        ''랜덤 개체
        Dim myRandom As New Random
        '' myControl의 갯수를 랜덤으로 만든다
        '' 최대 9개까지
        For iX As Integer = 0 To myRandom.Next(10)
            ''연산자를 어떤 것으로 사용할지 랜덤으로 
            sOpe = {"+", "-", "x", "/"}(myRandom.Next(4))
            ''연산자의 종류에 따라서 라벨의 값을 랜덤으로 계산
            Select Case sOpe
                Case "+"
                    sA = myRandom.Next(1, 51)
                    sB = myRandom.Next(1, 51)
                Case "-"
                    sA = myRandom.Next(1, 101)
                    sB = myRandom.Next(1, sA)
                Case "x"
                    sA = myRandom.Next(2, 11)
                    sB = myRandom.Next(2, 11)
                Case "/"
                    sB = myRandom.Next(2, 11)
                    sA = sB * myRandom.Next(2, 11)


            End Select
            ''myControl 개체 생성하면서 위에서 랜덤으로 만든 값을 매개변수로 전달한다
            oMyControl = New myControl(sA, sOpe, sB, "=")
            oMyControl.Location = New Point(iGap, iX * (iHeight + iGap))
            ''어떤 콘트롤이던 생성하면 폼의 Controls 집합체에 추가해줘야 한다
            Me.Controls.Add(oMyControl)
            iHeight_ = iX * (iHeight + iGap) + iHeight * 2
            ''Start 버튼을 크릭하기 전까지는 myControl을 비활성화
            oMyControl.Enabled = False

        Next
        '' 시간경과되는 것을 나타내기 위한 라벨
        myLabel = New Label
        Dim oFont As New Font("맑은 고딕", 10)
        With myLabel
            .Left = iGap
            .AutoSize = True
            .Text = "Start버튼크릭.."
            .Font = oFont
            .Top = iHeight_
        End With
        ''Start 버튼
        myButton = New Button
        With myButton
            .Text = "Start!!!"
            .Left = Me.Controls(0).Width - .Width
            .Top = iHeight_
            .Font = oFont
            .AutoSize = True
        End With
        '' 두개의 콘트롤을 한꺼번에 배열로 Controls의 AddRange메소드의 매개변수로
        Me.Controls.AddRange({myLabel, myButton})
        ''폼의 사이즈를 랜덤으로 생성된 컨트롤의 갯수에 따라서 조정되게
        Me.ClientSize = New Size(Me.Controls(0).Width + iGap * 2, iHeight_ + iHeight)
    End Sub

    Private Sub myButton_Click(sender As Object, e As EventArgs) Handles myButton.Click
        Dim iNum As Integer
        ''Start버튼을 크릭하면 비활성화되었던 myControl 콘트롤을 활성화시켜서
        ''문제의 답을 입력하게 한다
        ''콘트롤을 순환하면서
        For Each oX As Control In Me.Controls
            ''개체타입이 myControl일 경우 TypeOf 연산자를 사용하여 개체타입을 확인한다
            If TypeOf oX Is myControl Then
                oX.Enabled = True
                iNum += 1
            End If
        Next
        ''Start 버튼은 비활성화시킨다
        myButton.Enabled = False

        ''Timer개체를 생성한다
        myTimer = New Timer
        ''한 문제에 10초씩..시간제한을 주도록 계산한다
        myLabel.Text = "[" & iNum * 10 & "] 초남았습니다"
        ''Timer가 1초마다 Tick 이벤트가 발생하고 이때마다 계산결과를 
        ''확인하게 한다
        myTimer.Interval = 1000
        ''전역변수에 남은 시간을 계산보관한다
        iTimeLeft = iNum * 10
        ''Timer가 시작되면 Timer이벤트가 발생하면서 째깍,째깍시간이 지나간다
        myTimer.Start()
    End Sub

    ''Timer의 Tick 이벤트 1초에 한번씩 작동하게 하였다
    Private Sub myTimer_Tick(sender As Object, e As EventArgs) Handles myTimer.Tick
        ''순환하면서 사용할 myControl 타입 콘트롤변수
        Dim oMC As myControl
        Dim bOK As Boolean = False

        ''폼의 콘트롤을 순환하면서
        For Each oX As Control In Me.Controls
            ''myControl 타입만 선택하여
            If TypeOf oX Is myControl Then
                ''myControl타입으로 옮겨서
                oMC = oX
                ''myControl에 함수(메소드)로 작성된 isOK를 호출하여
                ''계산결과를 확인한다
                If Not oMC.isOK Then
                    ''만약 False이면 틀린답이거나, 아직 풀지 않은 문제, 순환을 끝내고
                    bOK = False
                    Exit For
                Else
                    bOK = True
                End If
            End If
        Next

        '현재 남은 시간 1초씩 감소시키고
        iTimeLeft -= 1
        '' 모든 컨트롤의 계산결과가 True이면
        If bOK Then
            '' 맞은 결과를 알려주고
            myLabel.Text = "축하합니다..모두 맞았습니다"
            myTimer.Stop()
            MsgBox("축하합니다..모두 맞았습니다")
        Else
            ''아직 못푼문제가 있으면 남은 시간
            If iTimeLeft > 0 Then
                myLabel.Text = "[" & iTimeLeft & "] 초남았습니다"
            Else
                ''시간이 초과되었으면 시간초과 중단
                myLabel.Text = "시간이 초과되었습니다"
                myTimer.Stop()
            End If
        End If

    End Sub
End Class
***[LOG-IN]***