NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 팁 앤 테크
  • 매뉴얼

    팁과 테크니컬 노하우를 확인하세요.

    팁 앤 테크

    팁과 테크니컬 노하우를 확인하세요.

    본 사이트의 컨텐츠는 저작권법의 보호를 받으므로 무단 복사, 게재, 배포 등을 금합니다.

    커스텀 2부 - 커스텀 액션으로 액셀 매크로 만들기.

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. [ 이전 글 ]에서 커스텀 모듈을 만드는 방법과 엑셀을 제어하는 샘플 코드에 대해 알아봤습니다. 그리고, 엔지엠 에디터에서 어떻게 사용되는지도 알아봤는데요. 혹시라도 이전 내용을 안보신 분들은 한번 읽어보세요. 그래야 이 내용을 이해할 수 있습니다. Visual Studio를 실행한 후 프로젝트를 불러오세요. 아래는 ExcelModel.cs의 전체 코드입니다.

    ExcelModel.cs

    using System;
    using System.Reflection;
    using System.Windows.Forms;
    using Excel = Microsoft.Office.Interop.Excel;
    
    namespace CustomExcelExampleModule
    {
        public class ExcelModel : NGM.Models.Interface.BaseCustomToolModel
        {
            public override string DisplayCategory => "엑셀 예제";
    
            public override string DisplayName => "테스트 1";
    
            public override void Execute()
            {
                Excel.Application oXL;
                Excel._Workbook oWB;
                Excel._Worksheet oSheet;
                Excel.Range oRng;
    
                try
                {
                    //Excel을 시작하고 Application 개체를 가져옵니다.
                    oXL = new Excel.Application();
                    oXL.Visible = true;
    
                    //새 통합 문서를 받으십시오.
                    oWB = (Excel._Workbook)(oXL.Workbooks.Add(Missing.Value));
                    oSheet = (Excel._Worksheet)oWB.ActiveSheet;
    
                    //셀 단위로 표 머리글을 추가합니다.
                    oSheet.Cells[1, 1] = "First Name";
                    oSheet.Cells[1, 2] = "Last Name";
                    oSheet.Cells[1, 3] = "Full Name";
                    oSheet.Cells[1, 4] = "Salary";
    
                    //A1 : D1 형식을 굵게, 세로 정렬 = 가운데로 지정합니다.
                    oSheet.get_Range("A1", "D1").Font.Bold = true;
                    oSheet.get_Range("A1", "D1").VerticalAlignment =
                    Excel.XlVAlign.xlVAlignCenter;
    
                    //한 번에 여러 값에 대한 배열을 만듭니다.
                    string[,] saNames = new string[5, 2];
    
                    saNames[0, 0] = "John";
                    saNames[0, 1] = "Smith";
                    saNames[1, 0] = "Tom";
                    saNames[1, 1] = "Brown";
                    saNames[2, 0] = "Sue";
                    saNames[2, 1] = "Thomas";
                    saNames[3, 0] = "Jane";
                    saNames[3, 1] = "Jones";
                    saNames[4, 0] = "Adam";
                    saNames[4, 1] = "Johnson";
    
                    //A2 : B6을 값 배열 (이름 및 성)로 채 웁니다.
                    oSheet.get_Range("A2", "B6").Value2 = saNames;
    
                    //C2 : C6을 상대 공식 (= A2 & ""& B2)으로 채 웁니다.
                    oRng = oSheet.get_Range("C2", "C6");
                    oRng.Formula = "=A2 & \" \" & B2";
    
                    //D2 : D6을 수식 (= RAND () * 100000)으로 채우고 형식을 적용합니다.
                    oRng = oSheet.get_Range("D2", "D6");
                    oRng.Formula = "=RAND()*100000";
                    oRng.NumberFormat = "$0.00";
    
                    //자동 맞춤 열 A : D.
                    oRng = oSheet.get_Range("A1", "D1");
                    oRng.EntireColumn.AutoFit();
    
                    //분기 별 판매 데이터에 대해 가변 개수의 열을 조작합니다.
                    DisplayQuarterlySales(oSheet);
    
                    //Excel이 표시되는지 확인하고 사용자가 Microsoft Excel의 수명을 제어 할 수 있도록합니다.
                    oXL.Visible = true;
                    oXL.UserControl = true;
                }
                catch (Exception theException)
                {
                    String errorMessage;
                    errorMessage = "Error: ";
                    errorMessage = String.Concat(errorMessage, theException.Message);
                    errorMessage = String.Concat(errorMessage, " Line: ");
                    errorMessage = String.Concat(errorMessage, theException.Source);
    
                    MessageBox.Show(errorMessage, "Error");
                }
            }
    
            private void DisplayQuarterlySales(Excel._Worksheet oWS)
            {
                Excel._Workbook oWB;
                Excel.Series oSeries;
                Excel.Range oResizeRange;
                Excel._Chart oChart;
                String sMsg;
                int iNumQtrs;
    
                //데이터를 표시 할 분기 수를 결정합니다.
                for (iNumQtrs = 4; iNumQtrs >= 2; iNumQtrs--)
                {
                    sMsg = "Enter sales data for ";
                    sMsg = String.Concat(sMsg, iNumQtrs);
                    sMsg = String.Concat(sMsg, " quarter(s)?");
    
                    DialogResult iRet = MessageBox.Show(sMsg, "Quarterly Sales?",
                    MessageBoxButtons.YesNo);
                    if (iRet == DialogResult.Yes)
                        break;
                }
    
                sMsg = "Displaying data for ";
                sMsg = String.Concat(sMsg, iNumQtrs);
                sMsg = String.Concat(sMsg, " quarter(s).");
    
                MessageBox.Show(sMsg, "Quarterly Sales");
    
                //E1부터 선택한 열 수에 대한 헤더를 채 웁니다.
                oResizeRange = oWS.get_Range("E1", "E1").get_Resize(Missing.Value, iNumQtrs);
                oResizeRange.Formula = "=\"Q\" & COLUMN()-4 & CHAR(10) & \"Sales\"";
    
                //머리글의 방향 및 WrapText 속성을 변경합니다.
                oResizeRange.Orientation = 38;
                oResizeRange.WrapText = true;
    
                //헤더의 내부 색상을 채 웁니다.
                oResizeRange.Interior.ColorIndex = 36;
    
                //열을 수식으로 채우고 숫자 형식을 적용합니다.
                oResizeRange = oWS.get_Range("E2", "E6").get_Resize(Missing.Value, iNumQtrs);
                oResizeRange.Formula = "=RAND()*100";
                oResizeRange.NumberFormat = "$0.00";
    
                //판매 데이터 및 헤더에 테두리를 적용합니다.
                oResizeRange = oWS.get_Range("E1", "E6").get_Resize(Missing.Value, iNumQtrs);
                oResizeRange.Borders.Weight = Excel.XlBorderWeight.xlThin;
    
                //판매 데이터에 대한 합계 수식을 추가하고 테두리를 적용합니다.
                oResizeRange = oWS.get_Range("E8", "E8").get_Resize(Missing.Value, iNumQtrs);
                oResizeRange.Formula = "=SUM(E2:E6)";
                oResizeRange.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).LineStyle
                = Excel.XlLineStyle.xlDouble;
                oResizeRange.Borders.get_Item(Excel.XlBordersIndex.xlEdgeBottom).Weight
                = Excel.XlBorderWeight.xlThick;
    
                //선택한 데이터에 대한 차트를 추가합니다.
                oWB = (Excel._Workbook)oWS.Parent;
                oChart = (Excel._Chart)oWB.Charts.Add(Missing.Value, Missing.Value,
                Missing.Value, Missing.Value);
    
                //ChartWizard를 사용하여 선택한 데이터에서 새 차트를 만듭니다.
                oResizeRange = oWS.get_Range("E2:E6", Missing.Value).get_Resize(
                Missing.Value, iNumQtrs);
                oChart.ChartWizard(oResizeRange, Excel.XlChartType.xl3DColumn, Missing.Value,
                Excel.XlRowCol.xlColumns, Missing.Value, Missing.Value, Missing.Value,
                Missing.Value, Missing.Value, Missing.Value, Missing.Value);
                oSeries = (Excel.Series)oChart.SeriesCollection(1);
                oSeries.XValues = oWS.get_Range("A2", "A6");
                for (int iRet = 1; iRet <= iNumQtrs; iRet++)
                {
                    oSeries = (Excel.Series)oChart.SeriesCollection(iRet);
                    String seriesName;
                    seriesName = "=\"Q";
                    seriesName = String.Concat(seriesName, iRet);
                    seriesName = String.Concat(seriesName, "\"");
                    oSeries.Name = seriesName;
                }
    
                oChart.Location(Excel.XlChartLocation.xlLocationAsObject, oWS.Name);
    
                //데이터를 덮지 않도록 차트를 이동하십시오.
                oResizeRange = (Excel.Range)oWS.Rows.get_Item(10, Missing.Value);
                oWS.Shapes.Item("Chart 1").Top = (float)(double)oResizeRange.Top;
                oResizeRange = (Excel.Range)oWS.Columns.get_Item(2, Missing.Value);
                oWS.Shapes.Item("Chart 1").Left = (float)(double)oResizeRange.Left;
            }
        }
    }
    

     

    코드에 대부분 주석을 달아 두었습니다. 큰 맥락을 이해하는데 문제는 없겠지만, 엔지엠에서 커스텀 모듈을 개발하기 위한 몇가지 규칙들에 대해 알아야 합니다. 이런 중요한 부분들은 꼭! 암기하고 넘어가야 합니다. 아래 코드에서 ":" 콜론을 기준으로 좌측은 클래스입니다. 우측은 상속받는 클래스 또는 인터페이스입니다. 프로그래밍 강좌가 아니라서 더 자세하게 설명은 하지 않겠지만, 중요한건 좌측은 사용자가 만드는 클래스라는 점입니다. 우측은 엔지엠에서 제공하는 라이브러리의 클래스 또는 인터페이스입니다.

    public class ExcelModel : NGM.Models.Interface.BaseCustomToolModel

     

    아무것도 없는 코드에서 엔지엠의 커스텀 모듈 클래스를 상속(Inheritance) 받으면 기본적인 코드를 생성할 수 있게됩니다.

    rhMxMtr.gif

     

     

    다른 시스템에서 사용하려면 클래스에 public 한정자 또는 제한자를 설정해줘야 합니다. 한정자나 제한자나 같은 의미로 사용되지만, 해석에 따라 다르게 말합니다. Access Modifier(접근 한정자 또는 접근 제한자)라고 부릅니다. 한정자는 여러 종류가 있지만, 이 부분은 직접 찾아서 학습하시길 바랍니다. 엔지엠 커스텀 모듈을 개발하려면 public만 알면 되지만, 복잡한 모듈이라면 반드시 알아야 하는 부분입니다^^;

     

    아래는 엔지엠 에디터의 사용자 도구에 표시되는 카테고리를 설정하는 코드입니다. 기본 생성은 아래와 같이 자동으로 만들어집니다.

    public override string DisplayCategory => throw new NotImplementedException();

     

    이렇게 바꿔주면~ 에디터의 사용자 도구에 카테고리가 "엑셀 예제"로 표시됩니다.

    public override string DisplayCategory => "엑셀 예제";

     

    아래 속성은 이 액션의 이름을 설정하는 부분입니다.

    public override string DisplayName => throw new NotImplementedException();

     

    "테스트 1"로 바꿔주세요~

    public override string DisplayName => "테스트 1";

     

    엔지엠 에디터의 사용자 도구 상자를 보면 "엑셀 예제" 카테고리에 "테스트 1" 이름으로 보여지는걸 알 수 있습니다. 당연한 이야기겠지만~ 카테고리 이름이 같으면 액션들이 자동으로 그루핑(Grouping)됩니다. 엑셀 예제 아래 화면 캡쳐 카테고리처럼요^^; 이렇게하면 유사한 기능들끼리 그룹으로 관리할 수 있고 찾기가 용이해집니다. 물론, 카테고리를 잘 세분화하고 용도에 맞는 이름을 지어주는게 중요하죠.

    3yJSAXm.png

     

     

    마지막으로 Execute가 있습니다. 사실 다른 상속 클래스들은 몇가지 더 존재하지만, 여기에서 깊이 다루지는 않겠습니다. 커스텀 모듈 개발에 대해 궁금하신 분들은 [ 여기 ] 강좌를 읽어보세요^^

    public override void Execute()

     

    이 부분은 스크립트에서 액션이 실행될 때 호출되는 메소드입니다. 그렇기 때문에 이 액션이 실행될 때 무언가 해야 한다면 이곳에서 코딩해야 합니다. 그래서 이 안에 엑셀 관련 코드들이 존재합니다. 이 부분은 어느정도 코드안에 주석(// 로 시작하는 부분)을 달아 두었으므로 이해하는데 크게 어려움(?)은 없을겁니다. 잘 이해가지 않는 부분은 댓글로 남겨주시면 답변 달아드릴께요^^;

     

    이제 오늘의 핵심인~ 상호 작용에 대해 알아보도록 하겠습니다. 상호 작용에 대해 설명하기 위해서 너무 많은 내용들을 위에 적은게 아닌가 걱정되기도 하네요. 위 내용을 읽다가 포기하진 않았는지 모르겠습니다-_-; 대부분은 여기까지 잘 참고 따라오셨으리라 믿고~ 출발하죠. 여기서부터는 엔지엠에서 제공하는 변수에 대한 이해가 있어야 합니다. 그렇지 않다면 아래 내용이 어떤 기능에 대해 설명하고 있는지 이해하기 어려울겁니다. 가능하면 메뉴얼의 변수부분을 학습하시고 이 글을 봐주세요.

     

    using 마지막에 using System.ComponentModel을 추가해주세요.

    using System;
    using System.Reflection;
    using System.Windows.Forms;
    using Excel = Microsoft.Office.Interop.Excel;
    using System.ComponentModel;

     

    Data1 속성을 하나 추가했습니다. 속성은 읽기(get)와 쓰기(set)가 있는 특별한 메소드입니다. 이 부분도 원론적인 부분부터 설명하려면 내용이 방대해서... 그냥 특별한 메소드 형식이며 속성(Property: 프로퍼티)이라고 알면 되겠습니다. 참고로, 언어별로 차이는 있지만 스페셜 메소드가 존재하며 이런 차이점에 대해서 잘 알고 있는것도 개발자로써 중요합니다.

            [Category("Data")]
            [Description("엑셀의 셀에 입력할 데이타 1입니다.")]
            public string Data1 { get; set; }

     

    동일한 속성을 몇개 더 추가해주세요. 예제는 속성을 4개 만들었습니다.

    using System;
    using System.Reflection;
    using System.Windows.Forms;
    using Excel = Microsoft.Office.Interop.Excel;
    using System.ComponentModel;
    
    namespace CustomExcelExampleModule
    {
        public class ExcelModel : NGM.Models.Interface.BaseCustomToolModel
        {
            public override string DisplayCategory => "엑셀 예제";
    
            public override string DisplayName => "테스트 1";
    
            [Category("Data")]
            [Description("엑셀의 셀에 입력할 데이타 1입니다.")]
            public string Data1 { get; set; }
    
            [Category("Data")]
            [Description("엑셀의 셀에 입력할 데이타 2입니다.")]
            public string Data2 { get; set; }
    
            [Category("Data")]
            [Description("엑셀의 셀에 입력할 데이타 3입니다.")]
            public string Data3 { get; set; }
    
            [Category("Data")]
            [Description("엑셀의 셀에 입력할 데이타 4입니다.")]
            public string Data4 { get; set; }

     

    아래 내용을 수정해야 합니다.

                    //셀 단위로 표 머리글을 추가합니다.
                    oSheet.Cells[1, 1] = "First Name";
                    oSheet.Cells[1, 2] = "Last Name";
                    oSheet.Cells[1, 3] = "Full Name";
                    oSheet.Cells[1, 4] = "Salary";

     

    이렇게 수정하세요. 표 머리글을 사용자가 직접 설정할 수 있도록 하는 부분입니다.

                    //셀 단위로 표 머리글을 추가합니다.
                    oSheet.Cells[1, 1] = this.Data1;
                    oSheet.Cells[1, 2] = this.Data2;
                    oSheet.Cells[1, 3] = this.Data3;
                    oSheet.Cells[1, 4] = this.Data4;

     

    아래 그림을 참고해서 빌드(컴파일) 하세요. 하단의 로그에 빌드 성공 메시지가 표시됩니다.

    프로젝트에서 우클릭 후 빌드 선택

    qvnSFO5.gif

     

     

    컴파일된 모듈을 가져오기 위해 아래 그림과 같이 처리합니다. 프로젝트에서 우클릭 후 "파일 탐색기에서 폴더 열기"를 선택하세요. 그리고, "bin/release" 폴더에 "CustomExcelExampleModule.dll"을 복사하세요.

    6FloOX8.gif

     

     

    복사한 모듈을 아래 폴더에 복사해줍니다.

    C:\Users\<사용자 계정>\Documents\NGM6\ToolExtension

     

    이제 엔지엠 에디터를 실행하세요. 그리고, 새 스크립트에 커스텀 모듈 액션을 추가하세요.

    sg2ATh2.gif

     

     

     

    Data1~4까지 속성이 추가된걸 확인할 수 있습니다.

    px4QmvB.png

     

     

    데이타를 입력하세요. 여러분들은 임의로 넣어도 됩니다.

    xWnIlsc.png

     

     

    실행하면 아래와 같이 제목이 Data1~Data4로 변경된걸 알 수 있습니다.

    8VENVfi.gif

     

     

    엔지엠 커스텀 모듈은 기본적인 내용은 자동으로 생성하고 연동됩니다. 대표적으로 변수죠. 코딩으로 변수쪽을 처리하지 않더라도 사용할 수 있습니다. 물론, 아이디를 설정하면 참, 거짓에 따라 로직도 이동할 수 있죠. 상당히 파워풀(?)한 기능이지만 진입 장벽이 높다는 단점이 있습니다. 크게 복잡한 코딩이 아니라면 간단한 데이타 조작같은 경우는 직접 처리하는게 더 효율적입니다. 회사의 경우에는 엔지엠소프트웨어에서 업무 로직을 개발해드릴 수 있습니다. 엔지엠에서 제공하는 액션으로 구현하기 어렵거나 비효율적인 작업은 커스텀으로 처리하고, 소스를 직접 관리하는게 더 좋은 선택일 수 있습니다. 핵심적인 부분을 커스텀 모듈로 회사에서 공유한다면 더 많은 부분에서 무인 자동화 시스템을 구축할 수 있습니다. 많은 비용을 절감하고 휴먼 에러 발생을 제로화 할 수 있죠^^; 오늘은 여기까지입니다. 궁금한 내용은 댓글로 남겨주세요~

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천1 비추천0

    댓글목록

    등록된 댓글이 없습니다.