C# Excel2007 추가 기능을 사용해 아래의 기능을 구현해보았습니다.

 

1. 특정 셀 클릭시 다른 시트의 셀 선택
2. 상태바 컨트롤
3. 툴팁 띄우기
4. Form 띄우기
5. 특정영역만 사용하기 (색표시 & 잠금(패스워드: test))
6. 콤보박스 사용

 

 

 

 

프로젝트 파일(VS2010) :TestExcelAddIn.zip

 

 

참조 : C# Excel 2007 추가기능(AddIn) 만들기

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Tools.Ribbon;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace ExcelAddIn1
{
    public partial class Ribbon1
    {
        Worksheet _sheet;
        public double pppx = -1, pppy = -1;
        public double fbTop = -1, fbLeft = -1;
        public double hLeft = -1, hTop = -1;
        public bool init = false;

        const int LOGPIXELSX = 88;
        const int LOGPIXELSY = 90;
        [DllImport("user32.dll")]
        static extern IntPtr GetDC(IntPtr hWnd);
        [DllImport("user32.dll")]
        static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
        [DllImport("gdi32.dll")]
        static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

        private void PixelsPerPointX()
        {
            if (!this.init)
            {
                IntPtr hdc = GetDC(new IntPtr(0));
                int PixPerInchX = GetDeviceCaps(hdc, LOGPIXELSX);
                double pppx = PixPerInchX / 72.0; //72 is the points per inch
                ReleaseDC(new IntPtr(0), hdc);
                this.pppx = pppx;
            }
        }

        private void PixelsPerPointY()
        {
            if (!this.init)
            {
                IntPtr hdc = GetDC(new IntPtr(0));
                int PixPerInchY = GetDeviceCaps(hdc, LOGPIXELSY);
                double pppy = PixPerInchY / 72.0; //72 is the points per inch            
                ReleaseDC(new IntPtr(0), hdc);
                this.pppy = pppy;
            }
        }

        private void GetFormulaBarAndHeadingsDim()
        {
            if (!this.init)
            {
                bool formBar = Globals.ThisAddIn.Application.DisplayFormulaBar;
                bool headings = Globals.ThisAddIn.Application.ActiveWindow.DisplayHeadings;
                double beforeH, afterH, beforeW, afterW;

                //check the size of the formula bar                
                beforeH = Globals.ThisAddIn.Application.ActiveWindow.Height;
                beforeW = Globals.ThisAddIn.Application.ActiveWindow.Width;
                Globals.ThisAddIn.Application.DisplayFormulaBar = false;
                afterH = Globals.ThisAddIn.Application.ActiveWindow.Height;
                afterW = Globals.ThisAddIn.Application.ActiveWindow.Width;                
                Globals.ThisAddIn.Application.DisplayFormulaBar = true;                
                this.fbLeft = afterW - beforeW;
                this.fbTop = afterH - beforeH;



                this.hLeft = 0;
                this.hTop = 0;

                Globals.ThisAddIn.Application.DisplayFormulaBar = formBar;
                Globals.ThisAddIn.Application.ActiveWindow.DisplayHeadings = headings;
            }
        }

        private void log(String s)
        {
            this.label3.Label += s + " | ";
        }

        private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
        {            
            this._sheet = Globals.ThisAddIn.Application.ActiveSheet;
            this._sheet.SelectionChange +=new DocEvents_SelectionChangeEventHandler(sheet_SelectionChange);
        }

        private void sheet_SelectionChange(Range rng)
        {
            MessageBox.Show("Select changed!");
        }


        private void CellTopLeftPixels(Range rng)
        {
            if (!init)
            {
                PixelsPerPointX();
                PixelsPerPointY();
                GetFormulaBarAndHeadingsDim();
                init = true;
            }

            double appTop = Globals.ThisAddIn.Application.Top;
            double appLeft = Globals.ThisAddIn.Application.Left;
            long RibbonHeight = Globals.ThisAddIn.Application.CommandBars["Ribbon"].Height;
            

            this.log("aT " + appTop);
            this.log("aL " + appLeft);
            this.log("rH " + RibbonHeight);
            this.log("px " + this.pppx);
            this.log("py " + this.pppy);
            this.log("fY " + this.fbTop);
            this.log("fX " + this.fbLeft);
            this.log("hY " + this.hTop);
            this.log("hX " + this.hLeft);
            this.log("rT " + rng.Top);
            this.log("rL " + rng.Left);
            this.log("1T " + (appTop + RibbonHeight + rng.Top + this.fbTop + this.hTop));
            this.log("1L " + (appLeft + rng.Left + this.fbLeft + this.hTop));


            long top = (long)((appTop + RibbonHeight + rng.Top + this.fbTop + this.hTop) * this.pppy);
            long left = (long)((appLeft + rng.Left + this.fbLeft + this.hLeft) * this.pppx);
            this.label1.Label = "left: " + left + " top: " + top;


            long topc = (long)((appTop + RibbonHeight + rng.Top + this.fbTop + this.hTop + rng.Height) * pppy);
            long leftc = (long)((appLeft + rng.Left + rng.Width + this.fbLeft + this.hTop) * pppx);
            this.label2.Label = "left: " + leftc + " top: " + topc;
        }

        private void button1_Click(object sender, RibbonControlEventArgs e)
        {
            this.label3.Label = "";
            CellTopLeftPixels(Globals.ThisAddIn.Application.ActiveCell);
        }        
    }
}
 

출처 : https://svn.kwarc.info/repos/sissi/trunk/win_office_alex/ExcelAddIn1/ExcelAddIn1/Ribbon1.cs

Thread에서 Task

스레드를 만드는 데는 비교적 많은 리소스가 필요하고 개별 스레드의 가상 메모리 소비량도 큰 편이다(기본 1MB). 앞에서 살펴 본 것처럼 스레드 풀을 이용하면 스레드를 만들어서 비동기 작업을 처리하고 작업이 끝난 스레드를 재사용할 수 있기 때문에 매번 새로 스레드를 만드는 것보다 효과적이다.

 닷넷 프레임워크 4에서는 비동기 작업을 시작할 때마다 운영체제 스레드를 만들어서 사용하는 대신 TPL이 태스크(Task)를 만들고 작업 스케줄러에게 처리할 비동기 작업이 있음을 통보하는 방식을 도입했다. 태스크 스케줄러를 이용하는 수많은 방법이 있겠지만 기본적으로 태스크 스케줄러는 스레드 풀에 작업 스레드를 요청한다. 스레드 풀은 효율성을 높이려고 현재 진행 중인 다른 작업이 끝난 다음에 요청된 작업을 처리하는 것이 낫다고 판단할 수도 있고, 혹은 요청된 비동기 작업을 처리하기 위한 작업 스레드를 특정 프로세서에 할당할 수도 있다. 스레드 풀은 또한 새 작업을 실행하는 일에 기존의 스레드를 재활용하는 것이 나을지 혹은 새 스레드를 만들어서 사용하는 것이 효과적일지도 판단한다.

 Task 개체의 안쪽에 내장하도록 비동기 작업을 추상화함으로써 TPL은 비동기 작업을 대변하는 개체를 제공하고 필요한 작업과 상호작용이 가능한 개체 지향 API를 제공할 수 있다. 그리고 작업의 단위를 표현하는 개체를 제공하므로 TPL은 프로그래밍을 통해 작은 작업들로 구성된 큰 작업을 워크플로우로 구성할 수 있으며, 이 점을 이후에 살펴본다.

 태스크는 비동기로 수행할 작업을 캡슐화하는 개체다. 코드를 대변하는 개체라는 점에서 대리자의 개념과 비슷하게 들릴 수 있다. 대리자는 동기적이며 태스크는 비동기적이라는 점에서 차이가 난다. , 예를 들어 Action과 같은 대리자를 실행하면 현재 스레드의 제어점은 즉시 대리자 코드로 이동하고 대리자의 실행이 끝날 때까지 제어를 반환하지 않는다. 이와 대조적으로 태스크는 처리해야 할 작업의 양과 무관하게 태스크 시작과 동시에 호출측으로 제어를 반환한다. 태스크는 비동기적으로 실행되며 보통 별도의 스레드를 이용한다.(이번 장의 뒤에서 살펴 볼 것인데, 사실 스레드 한 개만 이용해서 태스크들을 비동기적으로 실행하는 것이 가능하며 또 이득이 되기도 한다.) 태스크는 본질적으로 대리자를 동기적은 형태에서 비동기 실행 패턴으로 변환한다.

 

 - Essential C# 5.0 (C#의 기초와 고급을 아우르는 핵심 바이블) - 18장 다중 스레딩

 

+ Recent posts