PROGRAMMING WORKSHOP

COMAddIns | VSTO

엑셀로 만들면서 진행중이던 이 페이지의 내용을
VSTO버전으로 확장하여 만들어 가도록 하자
엑셀버전으로 만드는 노력이면 VSTO로 훌륭하게 만들수 있는 것이다
VSTO에서의 몇가지 환경의 습관을 들이는 것이 좀 중요할 뿐이다
엑셀로 만들던 것은 실은 uno_weekly의 칸트챠트그리기를 엑셀버전으로 옮겼던 것이고



이제 실제로 uno_weekly의 칸트챠트그리기를 만들어 보면서 많이 들 응용하여 나가 보시기 바란다
도형을 표현다는 것은 개체를 잘 다루는 경험을 많이 얻는 대상이다

앞에서 한 3페이지정도 진행하던 Office Open XML 은 하다보니 별로 실용적이지 못하여 중단하기로 했다
VB.Net을 겨냥한 언어이고 더더욱 좀더 활용하려면 VB.Net+SDK를 설치해야 하는등 번거롭다
이것을 VBA로 처리하여 보려고 하나 별로 시간투자에 대한 실용도가 크지 않다
공연히 고생시키는 것 같아서..
다음 기회에 VB.Net이 좀더 익숙해지면 별도 Offce Open XML용 라이브러리를 VB.Net에 확장하여
사용해 보도록 하지만, 추세가 별로 확장성이 높지 않다
그 노력을 VB.Net과 엑셀 VBA에 좀더 쏟도록 하는 것이 좋겠다

앞에서 VBA로 계속하던것과 같이 VB.Net으로 할때 VBA의 UserForm대신에 사용할 개체가 많다,
하나는 윈도우폼을 사용해도 좋고 또 다른 하나는 메뉴를 확장한 Ribbon을 사용해도 좋고
Custom Task Pane 이라고 하는 것을 사용해도 좋은..다양한 옵션이 있다

custom Task Pane은 Action Pane과 헷갈리지 마시기를
Action Pane은 VB.Net, VSTO로 엑셀추가기능을 만들때 Application 레벨이
아닌 Workbook 레벨로 만들때 사용하는 비슷한 모양의 것이다

지금 여기에서 하고 있는 것은 Application레벨의 추가기능화일이니
Custom Task Pane을 사용한다

만들어 보도록 하자
그림과 같이 프로젝트선택 오른쪽마우스크릭/Add/UserControl



UserControl은 윈도우폼의 대신에 여러개의 콘트롤을 수용하는
하나의 콘테이너 콘트롤(Container Control)이다



그림과 같이 UserControl상에 윈도우폼에서 이런 저런 컨트롤을
삽입하듯이 도구상자에서 필요한 것을 끌어다 놓으면 된다

Custome Task Pane은 빈 컨테이너박스이다..
이곳에 지금 만든 UserControl을 넣어야 물건이 되는 것이다

그리고 이렇게 코딩하면 된다

Public Class ThisAddIn
    Private oMyControl As UserControl1 ' UserContol을 담을 변수
    Private WithEvents oCTP As Microsoft.Office.Tools.CustomTaskPane ' Custom Task Pane 변수
	
	Private Sub ThisAddIn_Startup() Handles Me.Startup
        oMyControl = New UserControl1 ' UserControl 개체를 생성하고
        oCTP = Me.CustomTaskPanes.Add(oMyControl, "UNOVBNetCOMAddIn006") ' CTP를 하나 생성하고
        oCTP.Visible = True ' Visible 속성을 True로 하여야 눈에 보인다
    End Sub

    Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown

    End Sub

End Class

실행시키면 아래와 같이 만들어진다



참 간단하다..
UserControl에 필요한 컨트롤들 끌어놓고
UserControl을 Custom Task Pane을 생성할때 매개변수로 전달하면
만들어지는 것이다
개체의 즐거움을 좀더 해볼까??
Custom Task Pane을 5개를 만들어서 띄우면 어떻게 될까??



똑같은 것이 엑셀화면을 몽땅 채워버릴수도 있다..
개체는 찍어내는 대로 만들어지니까..

UserControl내에 사용하고 싶은 콘트롤들을 배치하고
각콘트롤에 코딩을 하는 것은 이미 엑셀 VBA에서 익힌대로
하면 되고, 거저 먹는거다
문제는 엑셀 VBA에서와 마찬가지로 어떤 개체에 어떻게
접근 할 것인가??를 잘 찾아 다니면 되는 것이다

위의 것은 CustomTaskPane을 띄우는 테스트였고
실제 작업을 해 보자, UserControl의 크기를 좀 키우고
이제 Usercontrol에 TabControl을 추가하자



TabControl은 여러장의 여러분이 원하는대로 TabPage개체를
추가할수 있다
TablControl에 이런 저런 콘트롤을 심을 것이니까..
이런 저런 컨트롤을 심을때마다 폰트나 기타서식을 매뉴얼로 하면 귀찮다
개발속도를 높이기 위하여 항상 코딩을 하는 것이 좋다
UserControl도 윈도우와 같은 형식의 작은 윈도우이니까
당연히 소루션창에서 UserControl을 오른쪽마우스로 크릭하고
나타나는 메뉴에 View Code(코드보기)가 있다, 이것은 해당개체의
모듈시트(크라스모듈)가 있는 것이다
그곳에 아래와 같이 코딩한다

Public Class UserControl1
    Private Sub UserControl1_Load(ByVal sender As Object, _
	ByVal e As System.EventArgs) Handles Me.Load
        Try
            Dim oFont As New System.Drawing.Font("맑은 고딕", 9)
            For Each oX As System.Windows.Forms.Control In Me.Controls
                oX.Font = oFont
            Next
        Catch ex As Exception
        End Try
    End Sub
End Class

System.Drawing.Font
라던가
System.Windows.Forms.Control

앞의 경로는 Imports System.Windows나 Imports System.Drawing 등과 같이
Imports 시키고 하는 것이 좋겠으나..
여러분들의 이해를 돕기 위하여 그냥 작성한다
여러분들은 나중에 Imports 시키고 간략하게 작성하시는 습관이 좋을 것이다
즉 아래와 같이

Imports System.Windows.Forms
Imports System.Drawing
Public Class UserControl1
    Private Sub UserControl1_Load(ByVal sender As Object, _
	ByVal e As System.EventArgs) Handles Me.Load
        Try
            Dim oFont As New Font("맑은 고딕", 9)
            For Each oX As Control In Me.Controls
                oX.Font = oFont
            Next
        Catch ex As Exception
        End Try
    End Sub
End Class

VB.Net을 Window프로그램을 개발한다고 할 때는
당연히 위의 것들이 디폴트로 Imports 된 상태이지만
현재의 프로젝트의 타입은 VSTO 즉 Office프로그램이기 때문에
Window등 다른 관련개체는 Imports시켜놓는 것이 좋은 것이다
VB.Net에서 다양한 프로그램을 Host로 하여 개발을 하므로
상황에 따라서 라이브러리를 적절히 참조하고 Imports시키고
하면서 작업을 한다는 점 잊지 마시고..

또한 코드를 작성하는 곳은 여러곳에서 할 수 있다
VBA에서는 일반 모듈, 시트개체크래스모듈, 일반크래스모듈,
UserForm의 개체크래스모듈등이 있었고, 이것도 헷갈리는데
VB.Net에서는 더욱 헷갈릴 가능성이 크다, 내공이 안쌓이면

위에서 Me라는 키워드를 사용했다
VBA에서도 시트개체크래스모듈에서 작성할때 Me를 사용하면
되는데 이것을 복사하여 일반모듈에 작성하고,
에러가 난다고 골치가 아파한다
개체의 개념이 아직 없어서 그렇다

위의 UserControl개체의 크래스모듈에서 작성된 것을
실은 ThisAddin개체 크래스모듈에서 작성할수도 있다
아래와 같이



ThisAddIn개체크래스모듈에서 Private나 Dim 대신에
WithEvents키워드로 변수를 선언하면 해당 개체의 이벤트를
ThisAddIn개체에서 통제할수 있게 된다
즉 같은 크래스모듈에 작성을 할 수 있는 것이다
그런데 이때 Me라는 키워드를 사용하면 에러가 나게 될것이다
왜냐면 Me는 이곳에서 사용하면 ThisAddIn을 의미하게 되니까!!
이럴때는 Me를 UserControl을 참조하는 변수명으로 바꿔줘야 할 것이다
이제 좀 헷갈린다..
그럼 어디에 작성하는 것이 좋은가??
이것은 소루션의 부품을 다른 곳에서도 사용하고 싶은 경우가 있다면
별도의 개체의 크래스모듈에 작성하는 습관이 좋을 것이다
그러면 지금 만들고 있는 UserControl을 다른 프로젝트에서
갖어다가 코드를 손볼 것없이 그대로 사용하면 되니까
마치 VBA에서 워크시트개체크래스모듈에 작성한 것을
워크시트를 다른 곳으로 옮기면 코드가 항상 같이 쫓아 가듯이

이제 Custom Task Pane이 만들어졌으면
엑셀이 열릴때마다 이것이 나타나게 하는 것은 다른 작업을 하는데
지장을 줄 것이다
그러니 뭔가 버튼을 크릭하면 열리기도 하고
닫히기도 하게 하고 싶을 것이다
이때 메뉴시스템에 무언가를 넣어야 하고 이때 제공되는 것이
2007버전이후에 제공되는 Ribbon이라고 하는 것이다
이것을 사용하려면 화일을 풀고 닫고
Office Open XML 이라는 새로운 표준의 xml문을
직접편집하던가 아니면 VBA로 편집을 하던가하는데
이것이야 말로, VB.Net과 조합을 위한 교두보로 만들어 넣은 셈이다
VB.Net에서는 그냥 윈도우폼하나 만들듯이 하면 된다



프로젝트명오른쪽마우스크릭/Add/New Items/템프릿대화상자의
왼쪽목록에서 Office선택하면 두개의 Ribbon 하나는 xml버전이고
다른 하나는 Visual버전, Visual버전을 선택하여 추가한다



도구상자에서 리본을 위한 도구상자가 분류되어 있다
이 부분에서 토글버튼을 선택하여 끌어다가
Ribbon바의 그룹상자속에 놓는다
그리고 설치된 토글버튼을 선택후 해당 속성창의 ControlSize를
RibbonControlSizeLarge 즉 큰 버튼사이즈로 바꿔준다

코딩을 하다 보면, 자리 바꿈을 하는 경우가 아주 빈번하다
이것도 CustomeTaskPane과 UserControl를 개체생성하여
화면에 나타나게 하는 것을 ThisAddIn의 Load이벤트에 만들어 넣었었다
이제 이것을 토글버튼을 크릭하여 토글링에 따라서
최초에는 생성하고 생성된 후에는 눈에 보였다, 안보였다 하는 일을
하게 하는 것을 별도의 프로시져에 만들어 놓는 것이 좋다
그래서 아래와 같이 별도로 만든다

Public Sub createCTP()
	If oCTP Is Nothing Then
		oMyControl = New UserControl1
		oCTP = Me.CustomTaskPanes.Add(oMyControl, "UNOVBNetCOMAddIn006")
	End If
	oCTP.Visible = True
End Sub
  

이때 oCTP나 oMyControl등의 변수는 전역변수로 옮기고
전역변수이지만 이것을 또 ThisAddIn크래스모듈 밖에서 호출하여야 하니까
Public으로 고쳐 주어야겠지
아마도 소루션이 커지면 또 다른 곳에 옮기고 상황에 따라서
변수의 위치도 바꿔야 하는 것이 프로그래밍의 전개과정이다
VBA에서도 마찬가지이니만 VB.Net에서는 변수,상수등의 관리에
조금만 소홀하면 쓰레기통이 된다
위의 프로시져를 ThisAddIn개체속에 있는 프로시져를
외부의 Ribbon의 컨트롤에서 접근하여야 아래와 같이 처리한다

Private Sub ToggleButton1_Click(ByVal sender As System.Object, _
	ByVal e As Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs) _
	Handles ToggleButton1.Click
If ToggleButton1.Checked Then
	Globals.ThisAddIn.createCTP()
Else
	Globals.ThisAddIn.oCTP.Visible = False
End If
End Sub
  

위에서 ThisAddIn개체를 접근할때 Globals라는 키워드를 꼭 앞장 세워야
찾아낸다..
개체접근에 있어서 최상위 접근 방법이다 Globals이라는 키워드
잊지 말자!!!

또한 사용자가 Ribbon의 ToggleButton을 실행하지 않고
그냥 CustomTaskPane의 닫기 버튼을 크릭하여 닫아 버릴때
ToggleButton도 상태에 맞게 토글링이 되게 해주어야 앞뒤가 맞는다
그 내용은
아래의 VB.Net화일의 코드를 보시고 또 각 변수의 위치와
Public 인지 Private인지 확인하시고 실행시켜보시기 바란다
다시 다듬어야 할 부분은 ToggleButton의 그림을 넣지 않았고,
CustomeTaskPane의 폭의 사이즈를 지정하지 않았다..
이것을 개발할때 어떻게 맞추어 나가면 좋을지 다음 페이지에서
연속 간다..
UNO_Weekly VSTO애드인 버전을 그대로 만들 수 있을 때까지..

***[LOG-IN]***