PROGRAMMING WORKSHOP

VB.Net |
Diary_5

DataTable와 더불어 LINQ,Delegate,Lambda,Extension Method 등....

엑셀의 내용을 DataGridView에 직접 올릴수도 있을 것이다
물론 순환하면서 하나,하나 옮길수도 있지만 DataTable이라는 개체의 편리함을
모르면 천상 그렇게 일일이 올려야 했을 것이다
DataTable은 마치 파워풀한 엑셀이 메모리속에 숨어 있는 것이라고 생각하시면 좋을 것이다
엑셀은 파워풀하다고 한다
그런 파워풀한 지능을 DataTable은 갖고 있다고해도 좋겠다
물론 사용자가 눈에는 보이지 않는 메모리속에 있는 녀석이다
VB.Net에 익숙하게 되면
엑셀에서 VBA로 작업을 하다 보면, 아하..DataTable이 있다면 참 편리할텐데
하는 생각이 절로 나오게 하는 것이 DataTable을 위시한 다양한 집합체들이다
이번 페이지는 아래와 같은 것을 만들어보면서 DataTable을 다루어 보면서
흥미로운 점들을 챙겨 보시기 바란다



어떤 분들에게는 뭔가 듣도 보도 못한 코딩을 볼수있게 된다
그러나 엄청난 파워를 갖고 있는 편리한 도구를 알게 된다
DataTable이던 엑셀의 시트의 데이타이던 어떤 정보를 뽑아 오려고 핟다면
대개가 순환문을 뺑뺑 돌아야 하는 구문을 작성해야 할 것이다
그러나 위의 그림의 코드는 좀 별난 암호같은 소리로 작성되어 있다
하지만 알고 보면 아주 간단한 언어이다
이것을 LINQ 라고 하는 언어이고, 마치 DB에서 SQL문을 작성하는 것같이
작성하면 되는 것..

우선 연습패널용 인터페이스를(위의 그림의 오른쪽 버튼들)하나 만들자..
UserControl이라는 없어서는 안될 윈도우의 콘트롤을 하나 사용해 보자
실은 현재의 기능으로서는 앞페이지에서 사용한 Panel컨트롤을 사용해도 되겠지만
다양한 경험을 위하여 UserControl을 추가해 보도록 하자
시리즈를 마치면 윈도우폼의 개체를 모두 사용하는 코스가 되도록 해보자
이 개체(크래스)를 하나 프로젝트에 추가 시켜 놓고..소루션창(프로젝트창)에서
추가 하기 오른쪽 마우스에서 UserControl을 선택하여 아래와 같이 빈탕의 회색박스만
하나 덜렁 나타난다



이름을 적당히 지어주면

도구상자의 사용자 콤포넨트로 등록이 되었음을 볼수 있다
디자인타임에 사용하는 것이 아니라서 신경쓸것은 아니고..
이것을 프로그래밍적으로 개체생성하는 크래스로 두고 만들어 가면 된다

이 UserControl의 크래스모듈에 아래와 같이 작성하자

Public Class UserControlVBNetTraining
	Dim oTextBox As TextBox = Nothing
	
	createControls프로시져는 메인폼의 버튼두개(하나는 쌤플DB, 다른 하나는 excel에서 불러 오는 데이타)
	를 크릭하면 호출되어..
	메인폼의 전역변수로 선언된 DataTable을 읽어서
	휠드명의 갯수만큼 버튼을 UserControl 상에 동적으로 만들어 넣는다 
    Public Sub createControls()
	만약 DataTable이 어떤 연유로던지 만들어지지 않았다면
	취소되고
        If Form1.oDataTable Is Nothing Then Return
        Try
            For Each oX As Windows.Forms.Control In Me.Controls
                oX.Dispose()
            Next
			집합체의 삭제는 뒤에서 부터 앞으로 이동하면서 삭제하여야 한다
	For iX As Integer = Me.Controls.Count - 1 To 0 Step -1
                Me.Controls(iX).Dispose()
        Next
        Catch ex As Exception
        End Try

        Dim iStartX As Integer = 6
        Dim iStartY As Integer = 6
        Dim iGap As Integer = 3
        Dim oBtnOld As Button = Nothing
        Dim iWidest As Integer = 0
        For Each sX As DataColumn In Form1.oDataTable.Columns
            Dim oBtn As New Button
            oBtn.AutoSize = True
            oBtn.Font = New Font("맑은 고딕", 9)
            oBtn.Text = sX.ColumnName
			버튼을 크릭하면 buttonClick프로시져가 호출되고
            AddHandler oBtn.Click, AddressOf buttonClick

            If oBtnOld Is Nothing Then
                oBtn.Location = New Point(iStartX, iStartY)
            Else
                oBtn.Location = New Point(iStartX, oBtnOld.Top + oBtnOld.Height + 3)
            End If

            Me.Controls.Add(oBtn)
            oBtnOld = oBtn
            If iWidest < oBtn.Width Then
                iWidest = oBtn.Width
            End If
        Next
		각각의 휠드명 버튼을 크릭하면 작업결과를 나타나게 할 TextBox하나 만들어 넣고
        oTextBox = New TextBox
        oTextBox.Location = New Point(iWidest + iStartX * 2, iStartY)
        oTextBox.Width = 300
        oTextBox.Multiline = True
        oTextBox.ScrollBars = ScrollBars.Vertical
        oTextBox.Font = New Font("맑은 고딕", 9)
        oTextBox.Height = oBtnOld.Top + oBtnOld.Height
        Me.Controls.Add(oTextBox)

    End Sub
아래가 버튼을 크릭하면 실행되어 텍스트상자의 내용에 결과물을 올린다
    Sub buttonClick(sender As Button, e As EventArgs)
	흥미로운 구문이 아래의 LINQ라는 언어의 처리이다
        Dim oQ = From Z In Form1.oDataTable.AsEnumerable
                Select a = Z("식당명"), b = Z(sender.Text)

        oTextBox.Text = ""
        For Each sX As Object In oQ
            oTextBox.Text &= sX.a & "=" & sX.b & vbNewLine
        Next
    End Sub
End Class

oDataTable.AsEnumerable 이라고 하는 표현은 메인폼에 만들어 놓은
DataTable개체의 Extension Method 인 AsEnumerble()을 사용하여 DataTable이라는 형식의 데이타를
LINQ라는 언어를 사용하여 내부적으로 순환하면서 원하는 정보를 찾게 하는 개체로 변환하는 것
즉, IEnumerable(Of T) 라는 형식의 개체이고..여기에서 순환하는 행에 해당하는 T가 되는것은
DataTable의 DataRow개체인 것이다..즉 IEnumerable(Of DataRow) 라는 것으로 변환된다
왜냐면 LINQ가 처리하는 집합체의 형식이 IEnumerble(of T)이니까..
이게 도대체 뭔소린가??
일반적으로 이것을 처리한다고 하면

Dim oListSelect As New List(Of String)
For Each oRow As DataRow In Form1.oDataTable.Rows
    oListSelect.Add(oRow("식당명") & "=" & oRow(sender.Text))
Next
oTextBox.Text = ""
For Each sX As String In oListSelect
    oTextBox.Text &= sX & vbNewLine
Next

으로 처리된다, 당연히 DataTable을 행별로 순환하면서 찾아서 모아서
TextBox의 Text속성에 주면 된다
간단한데..뭘...
뭐 복잡하게 새로운 LINQ라는것을 이야기 하나..
새로운 것이 나오면 받아들이고 학습하고 숙달시키는 것이 좋은 것..
호기심을 갖고 따져 보는 것은 사람을 건강하게 한다 그러니 따져보는 것이 좋은 것이다
LINQ가 싫으면 그냥 전통적인 순환문을 사용하시면 된다
이곳에서는 모듈시트에 일반적인 방법을 주석처리하고 기록하여 놓았으니
비교하면서 학습하시면 많은 도움이 될 것이다

그렇다면 LINQ(Language-Integrated Query)는 뭔가..
말그대로 코딩속에 통합된 쿼리(원하는 것을 찾는 것)기능을 포함시켜 버리자는 것
그냥 말하듯이 쓰면 내부적으로 LINQ라는 것이 순환을 하면서 원하는 것을 찾아온다
말하듯히 쓰는 DB를 위한 SQL언어와 같은 컨셉인 것이다

실은 LINQ 이전에 Lambda Expression 이라는 것이 바탕에 깔려있다
위의 내용은 아래와 같이 표현되고 이것을 Lambda Expression이라고 한다

Dim oQ = Form1.oDataTable.AsEnumerable.Select(Function(p) (p.Field(Of String)("식당명") & "=" & p.Field(Of String)(sender.Text)))
oTextBox.Text = ""
For Each sX As Object In oQ
    oTextBox.Text &= sX & vbNewLine
Next

Lambda는 표현식에 함수나 프로시져를 함수명과 프로시져명을 사용하지 않고
표현하여 같은 효과를 보는 편리한 표현식이다
LINQ와 Lambda표현식 어느것이 좋다, 나쁘다가 아니고 편리한 것을 골라사용하면 된다
하지만 LINQ가 표현식내부에 변수를
선언할수 있다는 장점이 있어서 결론은 LINQ를 사용하는 것이 좋을 것이다
하지만 또 간단한 것은 lambda expression을 사용하는 것도 좋고..

아무튼 계속 사용하게 될 LINQ와 관련된 내용들의 상세한 설명은
진행중인 이페이지를 참조하시기 바란다
메뉴를 새로 추가하여 놓았다
아직은 아무 기능도 없지만, 메뉴를 이렇게 만드는구나..하는 것을 보시기 위하여

Dim oMenu As MenuStrip = New MenuStrip
Dim oMenuDropDown As ToolStripDropDownButton = New ToolStripDropDownButton With {.Text = "MenuSystem", .BackColor = Color.Black, .ForeColor = Color.White}
oMenuDropDown.DropDownItems.Add("Menu1", Nothing, New EventHandler(AddressOf menuOnClick))
oMenuDropDown.DropDownItems.Add("Menu2", Nothing, New EventHandler(AddressOf menuOnClick))
Dim oMenuDropDown1 As ToolStripDropDownButton = New ToolStripDropDownButton With {.Text = "SubMenu"}
oMenuDropDown1.DropDownItems.Add("Menu3", Nothing, New EventHandler(AddressOf menuOnClick))
oMenuDropDown1.DropDownItems.Add("Menu4", Nothing, New EventHandler(AddressOf menuOnClick))
oMenuDropDown.DropDownItems.Add(oMenuDropDown1)
oMenu.Items.Add(oMenuDropDown)
Me.Controls.Add(oMenu)

위의 빨강색의 표현은 VBA에서 볼수 없었던 표현이다..

New ToolStripDropDownButton With {.Text = "MenuSystem", .BackColor = Color.Black, .ForeColor = Color.White}

이것은
Dim oMenuDropDown As ToolStripDropDownButton=New ToolStripDropDownButton
With oMenuDropDown
    .Text="MenuSystem"
    .BackColor=Color.Black
    .ForeColor=Color.White
End With

과 같은 표현을 약식으로 표현하는 구문이다
어느 것을 사용해도 괞찮지만 새로운 표현방식을 자꾸 익숙하게 하는 것이 좋을 것이다
어떤 메뉴를 크릭하면 이벤트프로시져와 연결하기 위한 구문도 아래의 것은
oMenuDropDown1.DropDownItems.Add("Menu3", Nothing, New EventHandler(AddressOf menuOnClick))
아래와 같은 표현이다
With oMenuDropDown1.DropDownItems.Add("Menu5")
    AddHandler .Click, AddressOf menuOnClick
End With

위의 내용을 아래의 화일에서 살펴보시고 ...

***[LOG-IN]***

프로그래밍은 자꾸 확장하는 것이 재미있는 것
UserControl을 활용하여 하나의 컨트롤패널로 활용하게 되었다
이때 UserControl에 만들어진 버튼을 크릭할때 좀더 실용적인 일을 하게 해보자
그림과 같이 조건을 넣는 텍스트박스를 하나 만들고 조건을 넣고 찾게 해보자



찾고자 하는 것을 텍스트박스에 입력하면 해당 내용을 잘 찾아낸다
이것도 역시 일반적인 순환문으로 할수 있는 것이지만
여기에서는 LINQ를 사용하여 아래와 같이 간단하게 처리된다

Dim sFldName As String = sender.Text
Dim oQ As Object = Nothing
Dim sCriteria As String = oCriteriaText.Text
If sCriteria <> "" Then
	oQ = From Z In Form1.oDataTable.AsEnumerable
		 Where Z(sFldName) Like sCriteria
		 Select a = Z("식당명"), b = Z(sFldName)

Else
	oQ = From Z In Form1.oDataTable.AsEnumerable
		 Select a = Z("식당명"), b = Z(sFldName)

End If


oTextBox.Text = ""
For Each sX As Object In oQ
	oTextBox.Text &= sX.a & "=" & sX.b & vbNewLine
Next

Wild문자(*)를 사용하여 검색하면 되도록 조건문에 Like연산자를 사용하고
Like연산자는 LINQ에만 있는 것이 아니고 이미 오래전부터 있던 연산자인데
잘 활용들을 하지 않는다..
VBA직접실행창에 아래와 같이 입력하고 엔터키를 쳐보시면 아하..VBA에서도
이미 사용하고 있는 연산자로구나..를 알수 있을 것이고, VBA에서도 많은 활용을
하시는 것이 좋을 것이다(MS 예문중에서..)
?"a12Qa" Like "a*a"
결과는 True
?"P" Like "[A-Z]"
결과는 True
?"6" Like "[!A-Z]"
결과는 True
?"a25a" Like "a##a"
결과는 True
?"aM556Q" Like "a[L-P]###[!c-e]"
결과는 True
?"BOOT123excel" Like "B??T*"
결과는 True
?"B123word" Like "B###w*"
결과는 True

강좌내용이 자꾸 산으로 간다..하지만 언어라고 하는 것이
자꾸 사족을 요하는 것이니,..산발적으로
이야기해도 잘 챙겨가면서 보시면 좋을 것 같다

산발적이고 산만함을 막기위하여....

엑셀에서 DB에 저장하지 않고 그대로 DataTable에 채우고
DataGridView의 DataSource속성에 연결하였었었다
엑셀을 DB에 저장해 버리고, 이제 엑셀은 잊어 버리도록 하는 것이
산만함을 덜어 버리게 될 것 같다
엑셀은 나중에 DB에서 엑셀로 보고 싶은 것을 만들때 사용하기로 하고
DB와 LINQ에 몰입하도록 정리해 버리자

DB가 존재하는지 확인도 해보고
Table이 존재하는지 확인도 해보고
Table에 데이타가 들어 있는지 확인도 해보고
Table내의 데이타를 모두 삭제를 해보기도 하여 SQL문의 다양한
활용을 해보는 것이다

엑셀에 있던 것을 Access DB의 테이블에에 옮기거나
DataTable의 것을 Access DB의 테이블에 옮기거나
아무튼 정보를 다른 형태의 것에 옮길때, 가장 염려하여야하는 것은
데이타의 타입이 제대로 인가??
데이타의 크기가 제대로 인가?
휠드의 갯수가 같은가??
등을 항상 고려하여야 하는 것이고, 이것의 개념을 모르면 밤을 팬다
이런것을 확인해 보는 작업은 역시 Debug 의 요령을 잘 챙기고 있으면
금방 문제를 찾고 해법을 찾을수 있다
엑셀에서 만든 DataTable을 억세스의 DB에 옮기는 과정에서 에러가 난다??
수천개, 혹은 수만개가 될지도 모를 데이타중에서 어떤 것에서 에러가 났는지
찾으면 금방 고친다..
그러려면 관찰할수 있어야 한다
VBA에서는 Debug.Print를 사용하여 직접실행창에 작성하고 에러가 난위치에서
중단되면 그 부분의 데이타의 생겨먹은 것을 관찰하면 될것이다
그런데 VB.Net에서는 직접실행창에 어떻게 쓰게 하지??

System.Diagnostics.Debug.Print(iDebug & " " & sSQL_Main)

와 같이 앞에 네임스페이스가 좀 붙으면 된다



붙여준 번호가 66에서 끝났으니, 67번째 행에서 뭔가 수상쩍은 일이 일어난 것

아하..여기에서 문제가 있었구나..




엑셀의 마지막 열의 휠드는 [가본곳]이라는 내용이다
이것은 TRUE나 FALSE로 DB에서 데이타타입을 만든 것이다
그런데 엑셀에는 문자가 들어 있다(○ 문자)
그러니 Boolean타입에 문자열을 넣으려고 했으니 에러가 난 것

If oRow.Item(8) Is DBNull.Value Then
	bVisit = False
Else
	bVisit = oRow.Item(8)
End If


아래와 같이 수정한다, 문자열정보가 있으면 True아니면 False값으로

If oRow.Item(8) Is DBNull.Value Then
	bVisit = False
Else
	bVisit = IIf(oRow.Item(8) <> "", True, False)
End If


DBNull.Value 라고 하는 것은 DataTable이나 DB의 테이블의 값이 빈값인지를 알아본다
이 값은 "" 나 0 값과는 다른 의미의 것이다, 한번도 데이타를 입력한 적이 없는 상태인 것이다
DB의 Null값인 것이다
이것을 oRow.Item(8)="" 혹은 oRow.Item(8)=0 등과 같이 비교하면 에러가 난다

아래의 화일에서는 위의 내용을 많이 수정했습니다
엑셀화일로 만드는것과 쌤플로 만드는 테이블에서 혼선이 와서 헷갈릴 것 같아서
그림과 같이 엑셀에서 가져온 데이타용 테이블과 쌤플로 만든 테이블 두개를
만들도록 하였다
만들어진 프로시져와 함수(모두 메소드)를 잘 챙겨 보시면 좋겠습니다



그리고 엑셀에서 한번 불러서 테이블을 만들면 더이상 엑셀화일을 열지 않고
만들어진 테이블에서 데이타를 가져오는 것으로 하여서 보면서 이해하기 쉽도록 하였다
자세한 것은 모듈시트에 설명을 하였으니
차근 차근 보시면서 응용해 보시기 바랍니다

***[LOG-IN]***