배움터  
  HOME > 배움터 > 무료강좌
무료강좌
 
엑셀, 곽승주님의 오튜공구함 제작으로 배워보는 VBA 이야기, Excel

1. 메뉴 만들기-Ⅰ

자료다운로드 : 오튜공구함001.xls

안녕하세요. 오튜가족 여러분.

지난 번에 말씀드린대로 오늘부터는 구체적인 코딩과 이에 대한 각 설명을 드리고자 합니다. 이번 시간에는 엑셀의 메뉴에 사용자정의 메뉴를 추가하는 것입니다.

모든 윈도우프로그래밍에 있어 가장 먼저 할 일은 어떤 개체가 필요한지 파악하고 그 개체의 프로퍼티(속성), 메소드(방법) 그리고 이벤트(사건)중 필요한 것을 파악해두는 것입니다.

자~아 엑셀의 메뉴를 다루기 위해서 가장 먼저 필요한 개체는 CommandBars컬렉션입니다. 원래 95버전까지는 MenuBars라는 개체를 이용했습니다. 그러나 오피스의 타애플리케이션과 공용으로 사용하기 바뀌었습니다. 그러므로 여기에서 말씀드리는 코딩과 설명은 오피스97버전이상이어야 합니다.

이제 필요한 개체가 CommandBars컬렉션개체라는 것을 알았습니다. 그러면 CommandBars컬렉션개체에 우리가 만든 개체를 추가해주는 것이 메뉴구성의 전부입니다. 물론 말은 간단하지만.

그러면 개체를 추가하려면 데이타가 필요합니다. 단순히 데이타도 없는 개체만 생성하는 것은 무의미하겠죠. 두번째 할 일은 데이타를 정의하고 어떻게 개체에 데이타를 입힐 것인가를 생각해봐야 합니다.
저는 다음과 같이 메뉴를 위한 데이타를 정의해두었습니다.
 

데이터 설명
mnuLvl 최상단 /중간 /하위메뉴를 구분하기 위한 변수
mnuCaption 메뉴의 이름
mnuMacro 메뉴를 클릭하면 실행할 프로그램명
mnuDivider 메뉴와 메뉴사이의 구분선표시 유무
mnuFaceID 메뉴왼편의 그림
mnuState 토글메뉴를 표시하기 위한 변수
mnuNextLvl 하위메뉴가 있는가를 파악하기 위한 변수

이는 메뉴의 구성을 살펴보면 알 수 있는데 다음 그림을 참조하세요

그리고 메뉴의 테이타는 워크시트 Sheet1에 두었습니다. 다음 그림을 보시면 아실 겁니다. 물론 오튜공구함의 시작단계라 아직 Macro, FaceID등 비어 있는 필드가 많습니다.
물론 메뉴의 관한 정보를 저장하여 불러오는데 여러 방법이 있습니다. 여기처럼 워크시트에 저장하는 방법도 있고 MDB파일로 저장하는 ADO 나 DAO개체를 이용하여 쿼리하는 방법도 있죠. 가장 간단한 방법은 아마 코딩에 아예 삽입하는 것일 겁니다. 또 미리 도구상자의 버튼이라면 수작업으로 만들어 놓고 .xlb로 저장하는 방법도 있죠.


Option Explicit

Const USER_TAG As String = "UserMenu"
사용자정의 상수를 선언하였습니다. 이를 선언한 이유는 사용자정의 메뉴삭제시 엑셀의 메뉴와 사용자정의메뉴를 구분하기 위한 것입니다. 뒷부분의 Sub DeleteUserMenu( )에서 나오지만 미리 말씀드리면 모든 메뉴개체를 For Each~Next를 이용하여 탐색하면서 이 꼬리표가 있으면 내가 만든 것이므로 안심하고 삭제하려는 것입니다.

상수에는 세 가지 유형이 있습니다.
● 시스템상수(고유상수)
시스템상수란 응용프로그램자체에서 제공하는 것으로 개체,메서드, 속성을 사용할 때 이용할 수 있는 상수입니다. VBE에서 F2를 눌러 개체찾아보기를 하시면 vb~ 나 xl~ 로 시작하는 많은 상수들을 보실 수 있습니다.
● 사용자정의상수
사용자정의상수는 Const 문을 사용하여 선언하며 말 그대로 사용자 즉 프로그래머가 필요해서 따로 정의합니다.
● 조건부컴파일상수
#Const 문을 사용하여 선언합니다.

Const MENU_LEVEL0 As Byte = 1
Const MENU_LEVEL1 As Byte = 2
Const MENU_LEVEL2 As Byte = 3

메뉴의 계층적인 구조를 표현하고 확인하기 위한 상수입니다. 빼먹은 것이 있군요. 보통은 상수의 데이터형을 지정하지 않고 사용합니다. 가령 위의 상수를 Const MENU_LEVEL0 = 1 으로 표시하기도 합니다. 그러나 정확한 데이터형을 주는 것이 아무래도 나을 것 같아 저는 상수에도 데이터형을 표시합니다.

Type mnuType
   mnuLvl As Byte
   mnuCaption As String
   mnuMacro As String
   mnuDivider As Boolean
   mnuFaceID As Integer
   mnuState As String
   mnuNextLvl As Byte

End Type

위에서 말씀드린 메뉴개체에 입힐 데이터를 담을 구조체를 선업하였습니다. 물론 개개의 변수를 굳이 Type문을 써가며 사용하지 않아도 됩니다. 그러나 같은 부류의 일을 하는 변수를 하나로 통일하는 것이 코딩에서도 보기좋고 실제 사용에서도 편리합니다.

가령 직원들의 데이터베이스프로그램을 작성한다면 다음과 같은 구조체를 정의합니다.
Type EmployeeRecord
   ID As Integer '사원 고유키 혹은 사원번호
   Name As String * 20 '이름
   Address As String * 30 '집주소
   Phone As Long '전화번호
   HireDate As Date '입사일자
End Type

그리고 다음과 같이 사용합니다.
Sub CreateRecord()
   Dim Dept_Acctg(10) As EmployeeRecord '회계부서의 데이터베이스
   Dim Dept_Mktg(10) As EmployeeRecord '마케팅부서의 데이터베이스
   Dim Dept_Sales(10) EmployeeRecord '판메부서의 데이터베이스

   '회계부서 첫번째 직원 자료
   Dept_Acctg(0).ID=….
   Dept_Acctg(0).Name=….
   …
   Dept_Acctg(1).ID=….
   Dept_Acctg(1).Name=….

   '마케팅부서 첫번째 직원 자료
   Dept_Mktg(0).ID=….
   Dept_Mktg(0).Name=….
   …
   Dept_Mktg(1).ID=….
   Dept_Mktg(1).Name=….
   …
End Sub

이제 메뉴구성을 위한 상수와 구조체변수선언이 끝났습니다.
이제 메뉴구성을 위한 Sub프로시저를 살펴보죠.

Sub CreateUserMenu()

   Dim cmdbarPopup As CommandBarPopup

< 워크시트메뉴바>

MENU_LEVEL0의 메뉴(오튜공구함)를 만들기 위해서는 엑셀의 워크시트메뉴바 개체를 참조해야 합니다. 이를 참조하기 위한 변수 cmdbarPopup 을 선언했습니다. 그리고 cmdbarPopup 변수의 데이터형은 CommandBarPopup입니다. 이단계에서는 실제 워크시트메뉴바 개체를 cmdbarPopup 변수에 할당한 것은 아닙니다. 다만 선언만 해둔 것이죠.

    Dim cmdbarPup As CommandBarPopup

< MENU_LEVEL1 팝업메뉴바>

cmdbarPup은 CommandBarPopup변수로서 위의 그림과 같은 팝업메뉴를 저장할 개체입니다.

   Dim cmdbarBtn As CommandBarButton

 < MENU_LEVEL2 버튼메뉴바>

cmdbarBtn 은 CommandBarButton변수이며 최하위계층의 메뉴를 담는 변수입니다.

   Dim bytBefore As Byte
   Dim bytRow As Byte

bytBefore는 오튜공구함메뉴를 워크시트메뉴바내의 위치를 저장합니다.
bytRow는 메뉴의 데이터를 워크시트로 읽어오기 위해 행의 번호를 저장합니다.

   Dim usrMnu As mnuType
앞서 선언한 구조체를 데이터형으로 하는 변수를 선언합니다. 이제 변수를 사용하려면 usrMnu.mnuLvl , usrMnu.mnuCaption등등으로 사용합니다.

   DeleteUserMenu
프로그램소스 하단에 있는 Sub DeleteUserMenu( )를 실행합니다. 프로시져 이름을 보시면 알겠지만 사용자정의메뉴를 삭제하는 역할을 합니다. 아니 만들기도 전에 무슨 삭제를 먼저 하느냐고 궁금하실텐데, 이는 Sub CreateUserMenu( )를 2회이상 실행하여 오튜공구함메뉴가 2개이상 생기는 일을 방지하기 위한 것이 첫번째 이유입니다. 그리고 프로그램의 개발단계에서는 자주 Sub CreateUserMenu( )를 돌려볼텐데 이를 일일이 삭제하려는 수고를 덜기 위한 것이 두번째 이유입니다.

목차 | 이전 | 다음