PROGRAMMING WORKSHOP

.Net FrameWork,VB.Net |DataTable,DataGridView,LINQ

Text정보를 DataTable로..

DataTable로 다시 돌아와서...

DataTable과 관련하여 이야기하다가 ,
크래스의 상속같은 것으로 한눈을 팔았다
처음에는 낯섫지만 , 한번 알면 별것도 아니면서,
또한 없어서는 안될 크래스의 성질머리니까..알고 가야 한다
기회나면 다시 하기로 하고 DataTable의 활용으로 다시 돌아와서
차근 차근 다시 이해하면서 숙달시키도록 하자
LINQ등을 숙달시키도록 하자

DataTable개체라고하는 것은 그냥 정보를 담아주는 창고이다
메모리상에 영역을 확보하고 집을 제대로 잘 지어놓은 창고인것이다
VBA에서 변수는 이미 잘 알고 있을 것이다
변수를 조금더 발전시켜서 사용자정의 변수(여러개의 정보를 계획하여)를 만들어서 쓰기도 하고
배열이라는 것을 만들어서 많은 정보를 넣기도 하고
집합체라는 것을 만들어서 많은 정보를 넣기도 했었다
IT의 발전은 다시 말해서, 정보를 어떻게 체계적으로 잘 보관할수 없을까??를
끊임없이 궁리하는 과정인 것이다
VB.Net즉 .NetFrameWork에서 최신의 정보창고 DataTable인 셈이다
엑셀은 시트자체가 정보의 창고역할을 하는 것이고..
VB.Net에서는 눈에 보이지 않는 메모리상에 엑셀시트같은 창고를 최신식으로
구축하는 셈이다
구축은 DataSet이라는 아주 큰 창고를 관리하는 개체에
DataTable이 또한 여러개 구축되는 것이다
이것에 대한 기본도구를 제공하여 주는 것을 개발자가 필요에 따라서
크게 혹은 작게 조립을 하면 된다
예를 들어 보자

My.Resource

프로젝트속성창에 보면 Resources 탭이 있다



My는 하나의 NameSpace이고
많은 편리한 개체들이 준비되어 있고 , 그 개체중의 하나가
Resources 개체이다, 이 개체에는
이곳에는 다양한 정보를 개발자가 저장하고 사용할수 있는 곳이다
그림화일이던,오디어화일,텍스트문서이던 이곳에 통합관리하면
개발중인 프로젝트의 Application의 자원이 된다
My.Resources로 접근할수 있으니 아주 편리한 곳이다
이곳에 Text화일을 하나 만들어 넣어보자
메뉴에서 Text화일만들기를 하면 Text문서를 작성할 창이 뜨고
이곳에 엑셀의 테이블,데이타를 복사하여 붙여 넣으면 텍스트문서가 하나 생긴다



저장하면 이것이 여러분의 자원중의 하나가 되는 것이다
이런 텍스트 정보를 분석하기가 힘들다...
휠터를 하고 싶어도, 정렬을 하고 싶어도 ,그룹핑을 하고 싶어도
물론 코딩을 하여 문자열정보를 처리할수도 있겠지만..
아주 옛날옛적에는 문자열정보로 보관된 정보자체를 분석하여야 하는 시절도 있었을 것이다
DataTable이라는 분석을 위한 창고가 준비되어있으니 안쓰면 손해다!!
이것을 프로그래밍적으로 접근하여 Text로 된 정보를 받아서 DataTable창고에
채워넣도록 하자

DataTable을 중심으로
앞에서 다양한 기능을 살펴본것을 복습한다고 보시면 더욱 좋을 것이다
그림과 같이 허접한 Text화일을 콘트롤에 모두 연결하였다



엑셀같은 인터페이스를 유지하는 DataGridView컨트롤
정보를 담고 있는 DataTable과 연결고리역할을 하는 BindingSource
그리고 같은 쏘스에 연결된 BindingNavigator
그리고 각 텍스트박스같은 콘트롤의 DataBindings 에 추가 되는 Binding개체등
헷갈리기 쉽고 , 하지만 개념을 알면 별것도 아닌 것이고..
개념을 잡는 것은 몇개의 상황을 바꿔가면서(여기에서는 쏘스가 텍스트화일) 같은
연결을 반복해 보는 것이 최선의 방법이다

그리고 TextBox와 Label콘트롤을 하나로 묶어서 UserControl에 넣어
하나의 컨트롤로 만들어서 사용하면 좋은 경우이다
크래스의 Inherts를 다시 살펴보는 기회도 될 것이고..

Visual Studio 2015 부터는 Express버전이 Community 버전으로 업그레이드



Express버전에는 중요한 엑셀 AddIn을 만들어주는 템프릿이 없었다
Community버전이 되면서 거의 모든 것을 할수 있는 환경으로 무료제공되게 되었다
프로그래밍워크샵에서 VSTO관련글을 올리면서 Express버전으로는 도전을
할수 없던 제약이 없어졌으니, 2017년에는 Visual Studio를 기본으로 다양한 프로그래밍을
즐겨 보시기를 ....!!!!
지금 이 페이지에 올리고 있는 것은 VSTO와 관련이 없는 것이니
계속 2010 Express버전을 사용하도록 하고 다른 코너의 페이지에서는
2015 Commnunity버전으로 하도록 하자
참..2015 Commnuity 버전은 윈도우환경이 윈도우 10으로 업그레이드 하시고
하셔야 한다
UNO_Weekly에서 올렸던 UNO_ZEN이라는 프로그램은 Community 2015버전의 WPF로
만들었기때문에 윈도우 10 이전의 버전에서는 설치가 안될수도 있었다


TextBox와 Label을 UserControl으로 하나의 컨트롤로 만들기..

아래의 그림과 같이 라벨과 텍스트상자는 하나의 셋트로 구성될수 있을 것이다
앞에서 이야기한 크래스를 UserControl개체를 상속(Inhert)받아서
라벨과 텍스트상자로 하나의 사용자정의 콘트롤로 구성하면 편리할 것이다




'' myControl이라는 사용자정의크래스를 하나 만들고
Public Class myControl
    '' 시스템에서 제공하는 콘트롤 UserControl의 상속받아서
    Inherits UserControl

    '' 본래의 UserControl이 갖고 있는 New 메소드에서
    '' BindingSource를 매개변수로 전달받게 하고..
    '' TextBox콘트롤과 Label콘트롤 두개로 하나의 콘트롤로 구성한다
    Sub New(sName As String, oBindingSource As BindingSource)
        Dim oLabel As New Label
        Dim oTextBox As New TextBox

        oLabel.Left = 3
        oLabel.Top = 3
        oLabel.Size = New Size(100, 20)
        oLabel.Text = sName
        Me.Controls.Add(oLabel)

        oTextBox.BackColor = Color.White
        oTextBox.Left = 106
        oTextBox.Top = 3

        oTextBox.Size = New Size(200, 20)
        '' 중요한 것은 이 부분
        '' BindingSource를 사용하여
        '' TextBox의 DataBindings 속성(개체를 만드는 속성) 의 Add 메소드에 새로운 Binding개체를 만들어 넣는다
        '' 그러면 본래의 폼의 BindingSource와 항상 연동된다
        '' DataGridView의 선택된 행에 해당하는 값이 연동되는 것
        oTextBox.DataBindings.Add(New Binding("Text", oBindingSource, sName))

        '' 여기에서 Me는 본래의 윈도우폼의 Me가 아니고
        '' 현재 자신의 크래스를 Me로 접근할수 있는 것
        '' 그래서 여기에서의 Me는 지금 상속받은 UserControl이 Me가 되는 것
        Me.Controls.Add(oTextBox)

        Me.Height = 24
        Me.Width = 320
        Me.BackColor = Color.Black
        Me.ForeColor = Color.White
    End Sub
End Class

위의 사용자정의 크래스를 폼컨트롤로 생성하여 만들어 넣기

앞 페이지에서 살펴보았던 기능을 모두 포함하여..

Public Class Form1

    '' 앞페이지에서 학습하였던 기능을 모두 사용해보고..
    '' 데이타소스만 프로그램속에 내장시킨 Resources/myData.tex 화일의 
    '' 문자열 정보를 DataSource로 사용해 본다
    '' DataTable 개체
    Dim oTable As DataTable
    '' DataGridView 개체
    Dim oDGV As DataGridView
    '' BindingSource 개체
    Dim oBS As BindingSource
    '' BindingNavigator 개체
    Dim oBN As BindingNavigator


    Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
        '' 폼의 로딩프로시져에서 
        '' DataTable개체를 생성하고
        '' DataTable개체에 문자열정보를 풀어서 담고
        '' 윈도우폼에 정보를 나타낼 DataGridView 개체를 생성하고
        '' 사용자정의 크래스를 생성하여 TextBox와 Label을 만든다
        createDataTable()
        fillDataTable()
        setDataGridView()
        addTextBoxes()

        '' 폼의 크기를 계산해주고
        Me.Height = Me.Controls(Me.Controls.Count - 1).Top + 80
        Me.Width = oDGV.Width + 30
        Me.Font = New Font("맑은 고딕", 9)
    End Sub


    Sub createDataTable()
        Dim sDatas As String() = My.Resources.myDatas.Split(Environment.NewLine) ''  (vbCrLf) 로 해도 되고...
        '' DataTable을 하나 만든다면 DataSet이 이미 하나 만들어지고, 이중의 하나의 DataTable이 되는 것으로 아시면 된다
        '' 그러니 DataSet을 만들필요는 없다
        '' 만약 여러개의 DataTable을 만든다면 DataSet을 하나 만들고 추가 시켜나가면 되고..
        oTable = New DataTable
        '' 첫째 행은 휠드행이라고 간주하고
        '' 둘째 행은 정보행이니, 각정보가 어떤 타입인지 않아서 테이블의 열의 타입을 결정하자

        Dim iCol As Integer = 0
        For Each sX As String In sDatas(0).Split(vbTab)
            Dim oCol As New DataColumn
            Dim sData As String = sDatas(1).Split(vbTab)(iCol)
            oCol.ColumnName = sX

            '' datatype은 System.Type.GetType("System.DateTime") 과 같이 얻을수도 있지만
            '' GetType(Date) 으로 각열의 데이타타입, 날자,숫자,문자..
            If IsDate(sData) Then
                oCol.DataType = GetType(Date)
            ElseIf IsNumeric(sData) Then
                oCol.DataType = GetType(Double)
            Else
                oCol.DataType = GetType(String)
            End If
            iCol += 1
            oTable.Columns.Add(oCol)
        Next
    End Sub

    '' 위에서 만든 DataTable에 데이타를 채워넣는다
    Sub fillDataTable()
        '' 프로그램에 내장된 Resource는 아래와 같이 불러서 배열변수에 담는다/////
        Dim sDatas As String() = My.Resources.myDatas.Split(vbCrLf)
        ''//////////////////////////////////////////////////////////////
        Dim iRow As Integer = 0
        Dim iCol As Integer = 0

        '' 배열 변수를 한줄,한줄(문자열) 순환하면서
        For Each sLine As String In sDatas
            If iRow > 0 Then
                Dim oDataRow As DataRow = oTable.NewRow
                '' 아래와 같이 ItemArray에 배열행을 한꺼번에 전달해도 되지만
                '' 이렇게 하면 모두 문자열로 전달이 된다
                ''oDataRow.ItemArray = sLine.Trim.Split(vbTab)
                ''oTable.Rows.Add(oDataRow)
                ''그래서 아래와 같이 하나, 하나 값을 정보를 점검하면서
                ''옮기는 것이 옳바른 정보창고관리가 된다
                iCol = 0
                For Each sFld As String In sLine.Split(vbTab)
                    Select Case oTable.Columns(iCol).DataType
                        Case GetType(Date)
                            oDataRow.Item(iCol) = DateValue(sFld) ' CType(sFld, DateTime)
                        Case GetType(Double)
                            oDataRow.Item(iCol) = CType(sFld, Double)
                        Case Else
                            oDataRow.Item(iCol) = sFld

                    End Select
                    iCol += 1
                Next
                oTable.Rows.Add(oDataRow)
            End If
            iRow += 1
        Next
    End Sub

    '' DataGridView 개체 만들어서 DataSource에 테이블 연결하기
    Sub setDataGridView()
        '' 아래 부분은 이미 앞에서 많이 연습했다
        oDGV = New DataGridView
        oBS = New BindingSource
        oBN = New BindingNavigator(True)

        oBS.DataSource = oTable
        oDGV.DataSource = oBS
        oBN.BindingSource = oBS
        oBN.Dock = DockStyle.Top
        Me.Controls.Add(oBN)
        oDGV.Location = New Point(3, 23)
        oDGV.Width = 700
        oDGV.SelectionMode = DataGridViewSelectionMode.FullRowSelect
        Me.Controls.Add(oDGV)
    End Sub

    '' 이제 중요한 것 연습은 사용자정의 개체를 생성하여 
    '' 폼 콘트롤집합체에 추가하는 것
    Sub addTextBoxes()
        Const CTL_GAP As Integer = 6
        Dim iStartY As Integer = oDGV.Top + oDGV.Height + CTL_GAP

        Dim iY As Integer
        Dim iHeight As Integer = 25
        Dim iWidth As Integer = 200

        For Each oCol As DataColumn In oTable.Columns
            '' 사용자정의 개체를 아래와 같이 생성하여 위치잡아서/////////////
            '' 폼의 콘트롤에 추가해 나간다
            Dim oQ As New myControl(oCol.ColumnName, oBS)
            ''////////////////////////////////////////////////////////////
            oQ.Location = New Point(6, (iY * iHeight) + iStartY + 3)
            oQ.BringToFront()
            oQ.Visible = True
            Me.Controls.Add(oQ)
            iY += 1
        Next
    End Sub
End Class

***[LOG-IN]***

원하는 값을 얻어오기..

아래와 같이 DateTimePicker 컨트롤 두개를 붙여서 DataGridView의 내용을
휠터를 하는 작업을 해보자..



시작날자와 종료날짜를 정하고 해당 날짜의 내용 휠터..
대개의 데이타의 분석의 개념은 엑셀에서 이미 숙달되었다면
데이타를 보면 휠터,정렬,그룹핑등등의 단어가 차례대로 연상되는 것이고
(아직 테이타분석의 내념이 막연하신 분은 엑셀에 좀더 미치시고..)
엑셀을 하던 .NetFrameWork를 하던 DB를 하던 개념은 같은 것이다
아무튼 그런 작업을 위하여 사용자가 편리하게 조건을 입력혹은 선택할 컨트롤을
준비해주는 것이 개발자의 일이다
위의 두개의 컨트롤은 디폴트값이 현재 컴튜터의 오늘날짜일것이다
하지만 시작 컨트롤은 사용하려는 데이타의 가장 빠른날자와
종료 콘트롤은 사용하려는 데이타의 가장 늦은 날자를 찾아서 지정해주는 것이
상식일 것이다
그렇다면, 어떻게 가장 빠른 날자와 가장 느린 날짜를 찾을 것인가??
엑셀에서라면 해당 날짜열의 Max와 Min값으로 찾으면 쉬운데
여기에서는 DataTable에서 찾을 것인지..
DataGridView에서 찾을 것인지..
BindingSource에서 찾을 것인지..
작업대상도 좀 막연해 진다
이것도 저것도 모르면 에라..순환문을 돌리면서 찾아 보자!!
라고 아래와 같이 코딩을 해도 될 것이다


'' DateTimePicker 컨트롤 두개 만들고
oCalStart = New DateTimePicker
oCalEnd = New DateTimePicker
'' 작은 날자와 큰 날자를 찾아서 담을 변수와 순환하면서 해당날자값을 담을 변수
Dim datMin As Date = DateSerial(3000, 1, 1)
Dim datMax As Date
Dim datCur As Date
''순환하면서...값을 비교하면서 찾아서
For Each oRow As DataGridViewRow In oDGV.Rows
	If IsDate(oRow.Cells("주문일자").Value) Then
		datCur = oRow.Cells("주문일자").Value
		If datMin > datCur Then datMin = datCur
		If datMax < datCur Then datMax = datCur
	End If
Next
'' DateTimePicker 컨트롤의 Value 속성에 값을 준다
oCalStart.Value = datMin
oCalEnd.Value = datMax

우선 모든 정보의 블랙박스는 여기에서는 뒤에 숨어있는 DataTable이다
이것을 사용하여 찾는 것이 좋다
이것을 DataRow를 순환하면서 찾아도 되겠지만
DataTable이 갖고 있는 메소드를 사용하면 간단하다

oCalStart = New DateTimePicker With {.Value = oTable.Compute("Min(주문일자)", ""), .Top = oDGV.Top + oDGV.Height + 6}
oCalEnd = New DateTimePicker With {.Value = oTable.Compute("Max(주문일자)", ""), .Top = oCalStart.Top + oCalStart.Height}

거참...딱 두줄이면 되네...!!
결국 개체를 사용한다고 하는 것은 어느 개체가 좀더 편리한 메소드나
속성을 갖고 있는지 참고하는 것이 가장 현명한 방법인 것이다
만약 VB.Net의 With {.Property=value, .Property=Value} 구문에 숙달되지 않으면
아래와 같이 VBA에서의 With브록을 사용하여 긴 코딩이 될 것이다


''위와 같은 결과의 구문이지만 바람직하지 않다
oCalStart = New DateTimePicker
oCalEnd = New DateTimePicker
With oCalStart
	.Value = oTable.Compute("Min(주문일자)", "")
	.Top = oDGV.Top + oDGV.Height + 6
End With
With oCalEnd
	.Value = oTable.Compute("Max(주문일자)", "")
	.Top = oCalStart.Top + oCalStart.Height
End With

oCalStart.Top = oDGV.Top + oDGV.Height + 6
oCalEnd.Top = oCalStart.Top + oCalStart.Height

콘트롤을 만들어서..집합체에 추가할때도..

Me.Controls.Add(oLblStart)
Me.Controls.Add(oLblEnd)
Me.Controls.Add(oCalStart)
Me.Controls.Add(oCalEnd)
Me.Controls.Add(oBtnF)
Me.Controls.Add(oBtnA)

와 같이 Add메소드로 하나씩 추가하는 것보다는 AddRange메소드를 사용하는 것도 편리하다

Me.Controls.AddRange({oLblStart, oLblEnd, oCalStart, oCalEnd, oBtnF, oBtnA})

버튼 두개를 추가하여 달고
시작날자와 종료날자를 기준으로 DataGridView의 휠터를 해보도록 하자
다른 버튼 하나는 본래대도 휠터해제한 상태가 되도록 하고..
여기에서 생각해 볼 문제는
과연 어디에서 휠터를 하는 것이 바른 방법일까..
DataGridView를 행별로 무식하게
순환하면서 조건에 맞는 것은 해당행의 Visible속성 True로 하고
아니면 Visible속성을 False로 하면 되려나???
그런 원본인 DataTable에서 휠터를 해볼까??
그렇지 않으면 DataTable의 중간 연결고리 역활을 하는 BindingSorce개체에서 할까??

BindingSource에서 하는 것이 가장 간단하다
중간고리역할을 하는 개체에서 하면 당초의 DataTable에도 아무 지장없고..
그래서 중간에 BindingSource를 연결하여 사용하는 이유가 되는 구나!!!


'' 두개의 버튼 크릭의 이벤트처리
'' DataTable, DataGridView, BindingSource,BindingNavigator등 모두
'' BindingSource개체를 중간에 두고 DataTable이 연결되었다
'' 그러니 중간연결개체인
'' BindingSource개체의 Filter 속성을 사용하거나
'' RemoveFilter메소드를 사용하면 휠터작업이 간단하다..
Sub filterData(sender As Button, e As EventArgs)
	'' 해제를 할 경우..
	If sender.Text = "전체" Then oBS.RemoveFilter() : Return

	''휠터를 할 경우
	Dim datStart As Date = oCalStart.Value
	Dim datEnd As Date = oCalEnd.Value
	If datStart > datEnd Then MsgBox("시작날자가 종료날자보다 늦습니다") : Return
	oBS.Filter = "주문일자>=#" & datStart & "# AND 주문일자<=#" & datEnd & "#"

End Sub

아래 화일에서 확인하시기를..

***[LOG-IN]***