블로그 이미지
Every unexpected event is a path to learning for you.

카테고리

분류 전체보기 (2737)
Unity3D (817)
Programming (474)
Python (8)
TinyXML (5)
STL (13)
D3D (3)
MFC (1)
C/C++ (54)
C++/CLI (45)
C# (250)
WinForm (6)
WPF (5)
Math (10)
A.I. (1)
Win32API (11)
Algorithm (3)
Design Pattern (7)
UML (1)
MaxScript (1)
FMOD (4)
FX Studio (1)
Lua (2)
Terrain (1)
Shader (3)
boost (2)
Xml (2)
JSON (4)
Etc (11)
Monad (1)
Html5 (4)
Qt (1)
Houdini (0)
Regex (10)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (228)
협업 (58)
3DS Max (3)
Game (12)
Utility (136)
Etc (96)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (53)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday
04-23 13:34

Sorting

Programming/C# / 2013. 10. 2. 17:46


링크 : http://support.microsoft.com/kb/320727/ko

링크 : http://www.codeproject.com/Articles/42839/Sorting-Lists-using-IComparable-and-IComparer-Inte

링크 : http://www.dotnetperls.com/sort-string-array

반응형
Posted by blueasa
, |

1 System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(); 2 psi.FileName = "FilePath"; // File Path 3 psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; 4 psi.CreateNoWindow = true; // hidden 을 시키기 위해서 이 속성도 true 로 체크해야 함 5 psi.Arguments = "parameters"; // Parameters. if you using 6 7 System.Diagnostics.Process.Start(psi);



참조 : http://mynotepad.tistory.com/120

반응형

'Programming > C#' 카테고리의 다른 글

Copy List to List  (0) 2013.10.10
Sorting  (0) 2013.10.02
Connection strings for Excel(OLEDB/ODBC)  (0) 2013.07.03
Excel Data Reader - Read Excel files in .NET  (0) 2013.06.28
Excel 2007,2003 OLEDB 연결 문자열  (0) 2012.12.16
Posted by blueasa
, |

원본 : http://www.connectionstrings.com/excel



Connection strings for Excel

Developers Community

Find solutions and post questions regarding connection string related issues.

Forum for Excel

Microsoft Jet OLE DB 4.0

TYPE OLE DB Provider
USAGE Provider=Microsoft.Jet.OLEDB.4.0
MANUFACTURER Microsoft

Standard

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyExcel.xls;
Extended Properties="Excel 8.0;HDR=Yes;IMEX=1";
 

Standard alternative

Try this one if the one above is not working. Some reports that Excel 2003 need the exta OLEDB; section in the beginning of the string.

OLEDB;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyExcel.xls;
Extended Properties="Excel 8.0;HDR=Yes;IMEX=1";

Important note!
The quota " in the string needs to be escaped using your language specific escape syntax.
c#, c++   \"
VB6, VBScript   ""
xml (web.config etc)   "
or maybe use a single quota '.

"HDR=Yes;" indicates that the first row contains columnnames, not data. "HDR=No;" indicates the opposite.

"IMEX=1;" tells the driver to always read "intermixed" (numbers, dates, strings etc) data columns as text. Note that this option might affect excel sheet write access negative.

SQL syntax "SELECT [Column Name One], [Column Name Two] FROM [Sheet One$]". I.e. excel worksheet name followed by a "$" and wrapped in "[" "]" brackets.

Check out the [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel] located registry REG_DWORD "TypeGuessRows". That's the key to not letting Excel use only the first 8 rows to guess the columns data type. Set this value to 0 to scan all rows. This might hurt performance. Please also note that adding the IMEX=1 option might cause the IMEX feature to set in after just 8 rows. Use IMEX=0 instead to be sure to force the registry TypeGuessRows=0 (scan all rows) to work.

If the Excel workbook is protected by a password, you cannot open it for data access, even by supplying the correct password with your connection string. If you try, you receive the following error message: "Could not decrypt file."

A workaround for the "could not decrypt file" problem

 
 

ACE OLEDB 12.0

TYPE OLE DB Provider
USAGE Provider=Microsoft.ACE.OLEDB.12.0
MANUFACTURER Microsoft

Excel 97-2003 Xls files with ACE OLEDB 12.0

You can use this connection string to use the Office 2007 OLEDB driver (ACE 12.0) to connect to older 97-2003 Excel workbooks.

Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\myFolder\myOldExcelFile.xls;
Extended Properties="Excel 8.0;HDR=YES";

"HDR=Yes;" indicates that the first row contains columnnames, not data. "HDR=No;" indicates the opposite.

 
 

.NET Framework Data Provider for OLE DB

TYPE .NET Framework Wrapper Class Library
USAGE System.Data.OleDb.OleDbConnection
MANUFACTURER Microsoft

Use an OLE DB provider from .NET

Provider=any oledb provider's name;OledbKey1=someValue;OledbKey2=someValue;

See the respective OLEDB provider's connection strings options. The .net OleDbConnection will just pass on the connection string to the specified OLEDB provider. Read more here.

 
 

Microsoft Excel ODBC Driver

TYPE ODBC Driver
USAGE Driver={Microsoft Excel Driver (*.xls)}
MANUFACTURER Microsoft

Standard

Driver={Microsoft Excel Driver (*.xls)};DriverId=790;Dbq=C:\MyExcel.xls;
DefaultDir=c:\mypath;

SQL syntax "SELECT [Column Name One], [Column Name Two] FROM [Sheet One$]". I.e. excel worksheet name followed by a "$" and wrapped in "[" "]" brackets.

 
 

Specify ReadOnly

[Microsoft][ODBC Excel Driver] Operation must use an updateable query. Use this connection string to avoid the error.

Driver={Microsoft Excel Driver (*.xls)};Dbq=C:\MyExcel.xls;ReadOnly=0;

ReadOnly = 0 specifies the connection to be updateable.

 
 

Microsoft Excel 2007 ODBC Driver

TYPE ODBC Driver
USAGE Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)
MANUFACTURER Microsoft

Standard

Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=C:\MyExcel.xls;
 
 

.NET Framework Data Provider for ODBC

TYPE .NET Framework Wrapper Class Library
USAGE System.Data.Odbc.OdbcConnection
MANUFACTURER Microsoft

Use an ODBC driver from .NET

Driver={any odbc driver's name};OdbcKey1=someValue;OdbcKey2=someValue;

See the respective ODBC driver's connection strings options. The .net OdbcConnection will just pass on the connection string to the specified ODBC driver. Read more here.

 
 

.NET xlReader for Microsoft Excel

TYPE .NET Framework Class Library
USAGE VM.xPort.ExcelClient.ExcelConnection
MANUFACTURER xPortTools

Excel file with header row

Data Source =c:\myExcelFile.xls;HDR=yes;Format=xls;
 
 

Excel file without header row

Data Source =c:\myExcelFile.xls;HDR=no;Format=xls;
 
 


반응형
Posted by blueasa
, |



링크 : http://exceldatareader.codeplex.com/

반응형
Posted by blueasa
, |

  • Excel 2007(xlsx) 파일 연결문자열
  1. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;Extended Properties="Excel 12.0;HDR=YES";

 

  • Excel 2003 이전(xls) 파일 연결문자열
  1. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;Extended Properties="Excel 8.0;HDR=YES";
  2.  Provider=Microsoft.JET.OLEDB.4.0;Data Source=FilePath;Extended Properties="Excel 8.0;HDR=YES";

 

  • Access 2007(accdb) 파일 연결문자열
  1. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;
  2. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;Jet OLEDB:Database Password=Password; (패스워드가 존재할 경우)

 

  • Access 2003 이전(mdb) 파일  연결문자열
  1. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;
  2. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;Jet OLEDB:Database Password=Password; (패스워드가 존재할 경우)
  3. Provider=Microsoft.JET.OLEDB.4.0;Data Source=FilePath;

    Provider=Microsoft.JET.OLEDB.4.0;Data Source=FilePath;Jet OLEDB:Database Password=Password; (패스워드가 존재할 경우)

 

Microsoft.ACE.OLEDB.12.0 관련 오류 해결 방법

 
로컬에서 잘 되던 기능이 서버에서는 안되어서 인터넷을 검색해 보았습니다.

로컬에서는 오피스2007을 설치하면서 관련 컴포넌트가 같이 설치된 것 같습니다.

위와 같은 오류가 발생시 관련 컴포넌트를 설치해 주셔야 합니다.

* 다운로드 URL: http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=7554f536-8c28-4598-9b72-ef94e038c891




반응형
Posted by blueasa
, |

OleExcelReader

엑셀파일을 읽는 클래스이다. 
앞서 글에서도 언급을 했지만 OleDB를 이용한 방법은 다른 것보다 굉장한 속도를 자랑한다..^^;
단순히 읽거나 쓰거나 하는 거라면...이 방법을 사용하기 바란다.

다른 DB프로바이더와 마찬가지로 연결개체를 만들고(Connection) 만들어진 연결로 열고(Open) 데이터를 조작(Excute~)하고 마지막으로 닫는 (Close) 순서로 진행된다.
하나씩 살펴 보도록 하겠다.

 먼저 Connection을 살펴 보자

  261 private string ExcelConnection()

  262 {

  263   return

  264     @"Provider=Microsoft.Jet.OLEDB.4.0;" +

  265     @"Data Source=" + this.fileName + ";" +

  266     @"Extended Properties=" + Convert.ToChar(34).ToString() +

  267     @"Excel 8.0;" + ExcelConnectionOptions() + Convert.ToChar(34).ToString();

  268 }

기존의 sql 프로바이더와 비슷한 구조이다.
Provider가 Jet.OLEDB 라는거 빼면..비슷한 구조..^^;
Data Source는 출력할 파일의 이름을 선택하면 되겠다.

  247 private string ExcelConnectionOptions()

  248 {

  249   string strOpts = "Imex=" + this.imex + ";";

  250   if (this.hasHeaders)

  251   {

  252     strOpts += "HDR=Yes;";

  253   }

  254   else

  255   {

  256     strOpts += "HDR=No;";

  257   }

  258   return strOpts;

  259 }

추가로 헤더의 유무를 선택 할 수 있으며 IMEX 값을 선택 할 수 있다.
IMEX라는 속성은 중요하다..^^;
예전에 데브피아 팁게시판에 올려 놓은 링크를 보게 되면..^^;

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNo=8&no=986&ref=986

MSDN에 의하면 이 설명은 아래와 같다.

NOTE: Setting IMEX=1 tells the driver to use Import! mode. In this state, the registry setting Import!MixedTypes=Text will be noticed. This forces mixed data to be converted to text. For this to work reliably, you may also have to modify the registry setting, TypeGuessRows=8. The ISAM driver by default looks at the first eight rows and from that sampling determines the datatype. If this eight row sampling is all numeric, then setting IMEX=1 will not convert the default datatype to Text; it will remain numeric. 

You must be careful that IMEX=1 not be used indiscriminately. This is IMPORT! mode, so the results may be unpredictable if you try to do appends or updates of data in this mode. 

The possible settings of IMEX are:

<PRE class=FIXEDTEXT>0 is Export mode 1 is Import! mode 2 is Linked mode (full update capabilities)</PRE>

이 클래스는 Reader이므로 imex의 기본값은 1이 되겠다.
만약 Writer라면 ..당연히 0이 될것이다.

그 외의 속성은 첨부된 소스 코드를 참고 하도록 한다.

이제 생성된 ConnectionString으로 connection 개체를 만들고 Open 하도록 한다.

  289 this.con = new OleDbConnection(ExcelConnection());

  290 this.con.Open();

 이제 Select 할 Command 개체를 만들어 보겠다.

  313 private bool SetSheetQuerySelect()

  314 {

  315   try

  316   {

  317     if (this.con == null)

  318     {

  319       throw new Exception("Connection is unassigned or closed.");

  320     }

  321 

  322     if (this.sheetName.Length == 0)

  323     {

  324       throw new Exception("Sheetname was not assigned.");

  325     }

  326 

  327     string cmd = "";

  328     for (int i = 0; i < this.sheetName.Length; i++)

  329     {

  330       cmd += @"SELECT * FROM [" + this.sheetName[i] + "$" + this.SheetRange[i] + "]" + System.Environment.NewLine;

  331     }

  332     this.selectCmd = new OleDbCommand(cmd, con);

  333     return true;

  334   }

  335   catch (Exception ex)

  336   {

  337     throw ex;

  338   }

  339 }

330 라인에 주목하자.
SQL에서 사용하는 쿼리와 같은 형식의 SELECT 문이다.
해당 시트의 영역에서 컬럼들을 SELECT 한다는 의미가 되겠다.
이러한 쿼리로 332라인의 OleCommand 개체를 만들었다.
이제 쿼리를 실행만 하면 될것이다.

  382 //Fill table

  383 OleDbDataAdapter oleAdapter = new OleDbDataAdapter();

  384 this.selectCmd.CommandTimeout = 200;

  385 oleAdapter.SelectCommand = selectCmd;

  386 DataTable dt = new DataTable(strTableName);

  387 oleAdapter.FillSchema(dt, SchemaType.Source);

  388 oleAdapter.Fill(dt);

  389 if (this.hasHeaders == false)  //헤더가 없으면 임의로 컬럼명을 생성한다.

  390 {

  391   if (this.sheetRange[0].IndexOf(":") > 0)

  392   {

  393     string FirstCol = this.sheetRange[0].Substring(0, this.sheetRange[0].IndexOf(":") - 1);

  394     int intCol = this.ColNumber(FirstCol);

  395     for (int intI = 0; intI < dt.Columns.Count; intI++)

  396     {

  397       dt.Columns[intI].Caption = ColName(intCol + intI);

  398     }

  399   }

  400 }

  401 this.SetPrimaryKey(dt);

위 코드는 GetDataTable() 메서드의 일부이다.
OleDbDataAdpater 개체를 만들어서 388 라인처럼 Fill 하게 되면 dt 라는 데이터 테이블에 SELECT 한 데이터가 채워지게 된다.

구조는..간단하지 않은가????

이제 사용 사례를 살펴 보자..

    1 OleExcelReader reader = new OleExcelReader();

    2 reader.Filename = filePath;

    3 reader.HasHeaders = true;

    4 reader.KeepConnectionOpen = false;

    5 reader.PkColIndex = 0;

    6 string[] sheetNames = reader.GetExcelSheetNames();

    7 if (sheetNames.Length < 1)

    8 {

    9   exceptionMessage = "적어도 한개 이상의 Sheet가 있어야 합니다.";

   10   returnDataSet = null;

   11   return false;

   12 }

   13 else

   14 {

   15   reader.SheetName = new string[] { "List""Staphylococcus""E.coli""Salmonella""Shigella" };

   16   returnDataSet = reader.GetDataSet();

   17 

   18   if (returnDataSet.Tables.Count != 5)

   19   {

   20     exceptionMessage = "시트의 수가 맞지 않습니다.";

   21     return false;

   22   }

   23 }

6라인의 GetExcelSheetNames()라는 메서드로 해당 엑셀파일의 시트 이름의 배열을 얻을 수 있다.
이 배열로 해당 엑셀 파일이 몇개의 시트를 가지고 있는지 판단 할 수 있게 된다.

15라인을 보게 되면 SheetName 이라는 속성에 출력할 시트의 이름들의 배열을 할당 하는 것을 볼 수 있다.
그리고 나서 16라인의 GetDataSet() 메서드로 엑셀로 부터 쿼리해온 5개의 시트를 하나의 데이터셋으로 Fill 하는 작업을 할수 있다.

그외의 자잘한 작업이 있긴 하지만..
알고보면 아주..간단한 작업이 되겠다.

제공되는 샘플 소스는..꽤..지저분하다..
내가 처음부터 만든것도 아닐 뿐더러...불필요한 메서드 들도 꽤 있다...ㅡ.ㅡ;;;
혹시나 필요한 부분만 발췌해서 사용하고자 한다면.. 조금만 분석에 시간을 들여 보길..권한다.

다음은 OleExcelWriter에 대해서 간략히 알아 보자.

 

OleExcelWriter

ㅋ..내가 미쳤나 보다..ㅡ.ㅡ;
예제 코드를 만들고야 말았다..

이번 강좌는..이 예제 코드 중심으로 진행을 할 것이다..@.@

그러므로..예제 프로그램을 다운 받아 실행 해보면서 보는게...이해가 빠를 것이다...라고 생각된다.^^;

이 클래스의 사용 순서는 이렇다.

  1. 파일을 연다. (Open)
  2. 컬럼을 생성한다.
  3. 시트를 생성한다.
  4. 각 시트별 데이터를 인서트 한다. -> 인서트 할때 마다 이벤트를 발생시킨다.
  5. 파일을 닫는다.

앞서 살펴 보았던 Reader와 비슷하다.

   18     public delegate void RowCount(object sender, RowCountEventArgs e);

   19     public event RowCount OnRowCount;

하나의 row가 인서트 될때 마다 이벤트를 일으켜 클라이언트에게 알려 주기 위해 델리게이트와 이벤트를 만들었다.

RowCountEventArgs 클래스는 다음과 같이 정의 된다.

   11 public class RowCountEventArgs : System.EventArgs

   12   {

   13     private int rowCount = 0;

   14     private string tableName = "";

   15 

   16     ///

   17     /// Initializes a new instance of the class.

   18     ///

   19     /// The row count.

   20     public RowCountEventArgs(int rowCount)

   21     {

   22       this.rowCount = rowCount;

   23     }

   24 

   25     ///

   26     /// Initializes a new instance of the class.

   27     ///

   28     /// The row count.

   29     /// Name of the table.

   30     public RowCountEventArgs(int rowCount, string tableName)

   31     {

   32       this.rowCount = rowCount;

   33       this.tableName = tableName;

   34     }

   35 

   36     ///

   37     /// Gets the name of the table.

   38     ///

   39     /// The name of the table.

   40     public string TableName

   41     {

   42       get

   43       {

   44         return this.tableName;

   45       }

   46     }

   47 

   48     ///

   49     /// Gets the row count.

   50     ///

   51     /// The row count.

   52     public int RowCount

   53     {

   54       get

   55       {

   56         return this.rowCount;

   57       }

   58     }

   59   }

tableName과 rowCount 라는 필드를 만든 이유는 필요에 따라서 몇번째 테이블의 몇번째 row인지 보여줄 필요가 있겠다 싶어서 였다.
하나의 시트 라면 tableName은 필요 없는...게 되겠다. 생성자를 선택해서 사용하면 될것이다.

ConnectionString은 앞서 설명한 OleExcelReader와 같다. 단지 앞에서 언급한 대로 IMEX 속성의 값만 다를 뿐이다..기본값인 0을 유지 하기 바란다..

  145 public void Open()

  146 {

  147   try

  148   {

  149     if (con != null)

  150     {

  151       if (con.State == ConnectionState.Open)

  152       {

  153         con.Close();

  154       }

  155       con = null;

  156     }

  157 

  158     if (System.IO.File.Exists(this.fileName))

  159     {

  160       System.IO.File.Delete(this.fileName);

  161       //throw new Exception("파일이 이미 존재 합니다.");

  162     }

  163     con = new OleDbConnection(ExcelConnection());

  164     con.Open();

  165   }

  166   catch (Exception ex)

  167   {

  168     throw ex;

  169   }

  170 }

161라인의 파일의 존재 여부는 상황에 따라서..달라질듯 하지만..일단은 기존 파일은 삭제를 하는 방향으로 하였다.
본 예제처럼 SaveFileDialog 컨트롤을 사용하게 된다면..별 상관 없는 문제이지만 이 컨트롤을 사용하지 않거나 웹에서 사용하거나 하는 경우에는..문제가 생길 것이다..
웹에서 사용하는 경우에는 이 때문에..파일명을 시간으로 정해주는 것도 좋다..혹은 유일성을 보장하는 GUID 값이라든지...

두번째로는 컬럼을 생성하는 부분이다.

   41 string[] colType = new string[3];

   42 

   43 colType[0] = "int";

   44 colType[1] = "varchar(16)";

   45 colType[2] = "datetime";

Form1.cs 파일에서 보여지는 위 예처럼 코드로 직접 이렇게 컬럼의 형식을 입력해야 하는 이유는..C#의 형식과 SQL의 형식을 매칭 시킬수가 없기 때문이다.ㅡ.ㅠ;
int형과 datetime 형의 경우는 매칭을 시킬수가 있다고 친다지만..44라인의 varchar(16)은 c#의 string형을 변환 한 것이다..
이 때문에 만약 컬럼수가 많은 엑셀 파일의 경우에는...코드가 좀 많아진다는 단점이 있겠다..ㅡ.ㅠ;
string 형은 여러가지 sql 타입으로 변환 할 수 있다..varchar(사이즈), text 등...
그렇기에 이런식으로 string[] 로 타입을 순서대로..가진 List 형의 ColTypeList 라는 속성이 필요하다.
이 속성에 있는 string[] 은 하나의 시트에 들어 있는 컬럼의 배열을 뜻한다. 
그러므로 시트가 여러개 있는 엑셀파일이라면 그 시트의 수가 곧 ColTypeList.Count 가 되겠다.

세번째로 이제 시트를 생성할 차례이다.
시트는 SQL 쿼리문의 CREATE TABLE 로 생성한다.

  197 public void CreateSheet()

  198 {

  199   for (int i = 0; i < this.curDs.Tables.Count; i++)

  200   {

  201     this.Command("CREATE TABLE [" + this.curDs.Tables[i].TableName + "] (" + this.CreatColName(this.curDs.Tables[i], i) + ")");

  202   }

  203 }

위 구문은 CREATE TABLE ["시트명"] ( '컬럼명', '컬럼명',......) 이라는 쿼리문을 만들어 주어 쿼리(Command메서드)를 하는 메서드이다.
문맥으로 보아 CreateColName 이라는 메서드는 ( '컬럼명', '컬럼명',......) 라는 문자열을 만들어 주는 메서드라는 것을 짐작해 볼 수 있다.

네번째로 각 시트별 데이터를 인서트 할 차례이다.

  250 public void Binding()

  251 {

  252   foreach (System.Data.DataTable dt in this.curDs.Tables)

  253   {

  254     string col = "";

  255     string resultCol = "";

  256     foreach (DataColumn c in dt.Columns)

  257     {

  258       col += "[" + c.ColumnName + "],";

  259     }

  260     if (col.EndsWith(","))

  261     {

  262       resultCol = col.Substring(0, col.Length - 1);

  263     }

  264 

  265     for (int i = 0; i < dt.Rows.Count; i++)

  266     {

  267       this.Command(@"Insert Into [" + dt.TableName + "] (" + resultCol + ") values (" + this.CreateRowForInsert(dt, i) + ")");

  268       this.OnRowCount(thisnew RowCountEventArgs(++this.rowCount));

  269     }

  270   }

  271 }

252 라인의 foreach문을 보면 각각의 시트(DataTable)마다 인서트 하겠다는 것을 알 수 있다.
256~263라인은 인서트 할 컬럼을 만드는 것이다. 
267라인에 보면 INSERT INTO [테이블명] (컬럼명, 컬럼명, ....) VALUES (값, 값, ....) 형태의 인서트 구문을 만들어 쿼리 하는 것을 알 수 있다.
마지막으로 268 라인을 보면 이 강좌의 처음에 보여 주었던 이벤트를 발생 시키는 것을 볼 수 있다. 
이 이벤트의 매개변수로는 ++rowCount를 보낸다. 즉 하나의 라인을 인서트 한 후에 그 인서트한 행의 Count를 매개변수로 던지는 것이다.
그렇게 되면 클라이언트에서는 이 이벤트를 받아서 현재 몇번째의 데이터가 인서트 되어 있는지 알 수 있을 것이다.

자..이제 예제를 살펴 보도록 하겠다.(Form1.cs)
예제에는 실제로 progressbar와 label을 만들어서 현재 인서트 진행 상황을 보여주게끔 만들었다. 
아직 event와 delegate에 익숙하지 않은 분들도 약간의 참고가 될 수 있으리라 생각된다.

   16     private delegate void CurrentRowEventHandler(int rowIndex);

   17     private CurrentRowEventHandler rowCount;

   18     private DataSet ds = new DataSet();

16 라인을 보면 CurrentRowEventHandler 라는 델리게이트를 만들었고 17 라인에 이 델리게이트 형의 rowCount 라는 개체를 만들었다.
이 델리게이트는 OnRowCount 라는 이벤트를 받아서 그 때마다 UI 스레드에게 CallBack 하여 Label 컨트롤과 ProgressBar컨트롤을 작동시키기위해 필요한 존재이다.
그리고 18행은 예제에서 사용될 DataSet이다.

   65     private void Form1_Load(object sender, EventArgs e)

   66     {

   67       this.rowCount = this.CallBackRowCount;

   68       this.MakeDataSet();

   69     }

Form 이 Load가 되면 rowCount 라는 델리게이트는 CallBackRowCount 라는 델리게이트메서드에 연결을 시킨다. 
그리고 DataSet에 데이터를 채운다..
DataSet에는 2개의 테이블이 있으며 각각 int 형, string 형, datetime 형의 컬럼을 가지고 있으며 데이터는 500개씩 이다.
DataSet에 데이터를 채우는 내용은..예제를 참고 하도록 한다..

자 이제 버튼을 클릭하게 되면...스레드를 만들고 스레드를 시작한다.
이 스레드는 MakeExcelFile 이라는 메서드를 호출한다.

이제 이 MakeExcelFile 이라는 메서드를 살펴 보자.

   34 private void MakeExcelFile()

   35 {

   36   OleExcelWriter excel = new OleExcelWriter();

   37   excel.OnRowCount += new OleExcelWriter.RowCount(excel_OnRowCount);

   38 

   39   excel.FileName = this.saveFileDialog.FileName;

   40   excel.HasHeader = true;

   41   List<string[]> colList = new List<string[]>(2);

   42   string[] colType = new string[3];

   43 

   44   colType[0] = "int";

   45   colType[1] = "varchar(16)";

   46   colType[2] = "datetime";

   47 

   48   colList.Add(colType);

   49   colList.Add(colType);

   50 

   51   excel.ColTypeList = colList;

   52   excel.CurDs = this.ds;

   53   excel.KeepConnectionOpen = true;

   54   excel.CreateSheet();

   55   excel.Binding();

   56   excel.Dispose();

   57   excel.Close();

   58 }

36라인에서 OleExcelWriter의 개체를 생성하고 37라인에서 이벤트를 등록 하였다. 
40~51 라인은 컬럼을 생성하여 ColTypeList 속성에 대입시키는 코드이다. 앞에서 설명 했었다.
52 라인은 DataSet을 CurDs 라는 속성에 대입한다. 그리고 54 라인에서 시트를 생성하고 55라인에서 데이터를 인서트 하며, 56,57 라인에서 Close 하게 된다.

excel_OnRowCount 메서드를 살펴 보자면.

   60 void excel_OnRowCount(object sender, RowCountEventArgs e)

   61 {

   62   this.Invoke(this.rowCount, e.RowCount);

   63 }

단순히 Invoke 만 했다. 
Invoke 의 첫번째 매개변수는 rowCount 라는 델리게이트이며, 두번째 인자는 RowCountEventArgs.RowCount 이다. 
이 델리게이트를 호출(Invoke) 하게 되면 Form1_Load 이벤트 메서드에서 설정한 CallBackRowCount 메서드가 실행된다.

   71 private void CallBackRowCount(int rowIndex)

   72 {

   73   this.lblMessage.Text = String.Format("현재 {0}번째의 데이터를 쓰고 있습니다.", rowIndex.ToString());

   74   this.progressBar.PerformStep();

   75   Thread.Sleep(10);

   76 }

매개변수로 받은 rowIndex를 단지 label 컨트롤에 대입하고 progressbar를 performstep 하는 UI 컨트롤에 대한 설정 밖에 없다. 
제2 스레드에서 제1 스레드로 Invoke 하게 된 결과이다 
75라인은...너무 빨라서 진행상태를 보기에 어렵기에..살짝 텀을 준것 뿐이다...

이로서 간략하지만 OLEDB를 이용한 엑셀 핸들링을 마치도록 하겠다 
다음 시간(??)엔 jakarta.POI를 이용한 쓰기 방법을 알아 보자...^^;

 

 

Jakarta.POI 를 이용한 Excel Writing

자 이제 마지막 시간이다..^^;

http://jakarta.apache.org/poi/

위 URL을 참조 하면 jakarta.poi 에 대해서 정보를 얻을 수 있다.
이 어셈블리를 사용하기 위해서는 jakarta.poi.dll이 필요하며 웹의 경우 서버에 J#재배포패키지가 설치되어야 한다.
그리고 JSharplib.dll 파일을 참조 추가 하여야 한다.

자 이제 준비가 되었으니 사용법을 알아 보자.

먼저 Enum.cs 파일을 보게 되면 셀과 폰트에서 사용되는 열거형들이 정의되어 있다.
주석은 없지만...ㅡ.ㅡ;;;

외부에 노출되는 타입은 Enum.cs 에 정의되어 있는 열거형들과 ExcelHandler class, ExcelCellStyle class 이다.
ExcelCellSettingBase class는 internal로 정의되어 내부에서만 사용되는 클래스이다.
ExcelHandler 클래스는 엑셀파일을 생성하기위한 기본 클래스이다. 
ExcelCellStyle 클래스는 선택된 시트의 셀 영역에 적용할 스타일에 대한 클래스가 되겠다. 이르자면 폰트에 관련된 속성같은 것 말이다.

이 어셈블리의 구조를 먼저 설명을 해야 할 것 같다.

ExcelHandler 클래스는 내부적으로 List 형의 리스트를 가진다.  이 리스트는 DataSet형의 컬렉션인데 이 DataSet이 바로 하나의 Sheet이다.
즉 List.Count 는 시트의 갯수와 같다는 의미가 되겠다.
이제 이 DataSet의 구조를 살펴 보자.
이 DataSet에는 최소 1개 이상의 DataTable을 가진다.
이 DataTable은 하나의 데이터영역이다.
이르자면..타이틀영역, 빈공간, 데이터 넣는 곳 등등,,,
하나의 시트에 바인딩되는 데이터의 형태는 반드시 DataTable형태가 되어야 하며 이 DataTable들은 여러가지 형태로 시트에 표현이 될 수 있다.
샘플을 보게 되면 2개의 시트가 있는데 첫번째 시트의 경우에는 DataTable이 3개가 있다.  뒷 부분에서 설명하도록 하겠다.
그리고 시트의 설정에 대한 부분도 포함되어 있다. Split이라든지 Group 같은 기능들이 있다.

ExcelCellStyle 클래스는 데이터와 상관없이 셀의 스타일을 정의한다.
이 클래스는 테두리의 선 및 색상정의, 폰트의 종류,색상,크기등의 정의 등등의 속성이 있다.
이 클래스로 만들어진 스타일 인스턴스는 ExcelHandler.SetCellStyle()메서드로  시트에 임베디드된다.

이제 ExcelCellStyle 클래스부터 살펴 보겠다.

위 클래스 다이어그램을 보는 바와 같이 ExcelCellStyle 클래스는 모두 필드와 속성으로만 정의되어 있으며 내부 클래스인 Font 클래스를 가지고 있다.
이 클래스는 FontStyle이라는 속성으로 사용된다.
Align, Rotation, CellColor, Border, Font를 설정할 수 있다.

이제 핵심 클래스인 ExcelHandler 클래스를 살펴보자.

   11 using org.apache.poi.hssf.util;

   12 using org.apache.poi.hssf.usermodel;

이 어셈블리를 사용하기 위해서는 위처럼 2개의 네임스페이스를 등록하여야 한다.

   48     public delegate void RowCount(object sender, RowCountEventArgs e);

   49     public event RowCount OnRowCount;

앞서 OleExcelWriter에서 설명한 바와 같이 OnRowCount 라는 이벤트와 델리게이트를 생성하였다.
앞서 설명을 하였기 때문에 패스..

public ExcelHandler();

public ExcelHandler(List<string> sheetName);

public ExcelHandler(string fullName);

public ExcelHandler(string fullName, List<string> sheetName);

public ExcelHandler(string path, string fileName);

public ExcelHandler(string path, string fileName, List<string> sheetName);

생성자는 위와 같이 정의 되어 있다.
2개 이상의 시트가 있거나 시트의 이름을 정해주고자 할 때는 sheetName 매개변수를 이용하도록 한다. 주의사항은 한글이름은 지원이되지 않는다..ㅡ.ㅠ;

  789 ///

  790 /// 개체 생성

  791 ///

  792 private void Create()

  793 {

  794   this.wb = new HSSFWorkbook();

  795   foreach (string sheet in this.sheetName)

  796   {

  797     this.wb.createSheet(sheet);

  798   }

  799 }

생성자에서는 위의 Create()메서드를 호출하며 이 메서드에서는 마치 Automation과 마찬가지로 워크북을 생성한다음에  sheetName 리스트에서 할당된 갯수만큼 시트를 만든다.

이제 시트를 만들었다면 Data를 입력해야할 차례이다..다음 메서드를 살펴보자.

  506 public void InsertTable(int sheetIndex, bool hasTitle, DataTable dt)

  507 {

  508   if (this.dsList.Count == sheetIndex)

  509   {

  510     DataSet ds = new DataSet();

  511     this.dsList.Add(ds);

  512   }

  513   this.dsList[sheetIndex].Tables.Add(dt);

  514 

  515   if (this.hasTitleList.Count == sheetIndex)

  516   {

  517     StringCollection sc = new StringCollection();

  518     this.hasTitleList.Add(sc);

  519   }

  520   this.hasTitleList[sheetIndex].Add(hasTitle.ToString());

  521 }

매개변수를 보자면.

  1. sheetIndex - 데이터를 넣을 시트의 인덱스
  2. hasTitle - 데이터테이블의 경우 해당 컬럼의 이름을 데이터의 상단에 렌더 할수 있다. 이 제목의 사용 여부이다.
  3. dt - 시트에 입력될 데이터테이블

처음에 구조를 설명하였듯이...
dsList 라는 제너릭리스트는 하나의 워크북을 의미하고 그 안에 있는 ds 라는 데이터셋은 하나의 시트를 의미하며 그 안에 입력되는 dt는 하나의 데이터영역이다.
hasTitleList는 hasTitle의 bool 값을 저장하고 있는 문자열컬렉션(StringCollection)의 제너릭리스트이다. 
dsList와 hasTitleList의 인덱스는 정확히..매칭된다.

  530 public void InsertBlankRow(int sheetIndex, int rowCount)

  531 {

  532   DataTable dt = new DataTable();

  533   dt.Columns.Add(new DataColumn());

  534   for (int i = 0; i < rowCount; i++)

  535   {

  536     DataRow dr = dt.NewRow();

  537     dr[0] = "";

  538     dt.Rows.Add(dr);

  539   }

  540   this.InsertTable(sheetIndex, false, dt);

  541 }

InsertBlackRow() 메서드는 해당 시트에 빈 공간(row)을 삽입하는 메서드이다.
몇 row의 빈칸을 삽입할지 결정하여 호출 하면 540라인에서 보듯이 InsertTable메서드를 호출하며 해당 DataSet(Sheet)에 값이 없는 (스키마만 있는) DataTable을 삽입하게 된다.

이제 데이터를 바인딩 하기 전에 바딩딩 할때 적용되로 스타일을 세팅 하도록 한다.

public void SetCellStyle(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex, CellType type);

public void SetCellStyle(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex, ExcelCellStyle cellStyle);

public void SetCellStyle(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex, ExcelCellStyle cellStyle, CellType type);

스타일을 세팅하기위한 메서드는 SetCellStyle 메서드인데 3가지로 overload되어 있다.
매개변수를 살펴 보자.

  • sheetIndex - 스타일을 적용할 시트의 인덱스
  • startColumnIndex - 스타일을 적용할 영역의 시작 컬럼 인덱스
  • endColumnIndex - 스타일을 적용할 영역의 끝 컬럼 인덱스
  • startRowIndex - 스타일을 적용할 영역의 시작 로우 인덱스
  • endRowIndex - 스타일을 적용할 영역의 끝 로우 인덱스
  • cellStyle - ExcelCellStyle의 개체참조
  • type - 셀의 타입, 현재 정의된 타입은 string, int, datetime 형 3가지이다.

  301 public void SetCellStyle(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex, ExcelCellStyle cellStyle, CellType type)

  302 {

  303   if (this.wb == null)

  304   {

  305     throw new Exception("워크북이 생성되지 않았습니다.");

  306   }

  307   else

  308   {

  309     if (this.wb.getSheetAt(sheetIndex) == null)

  310     {

  311       throw new Exception(sheetIndex + "인덱스에 시트가 생성되지 않았습니다.");

  312     }

  313     else

  314     {

  315       HSSFCellStyle style = this.wb.createCellStyle();

  316       this.AttachStyle(ref style, cellStyle, type);

  317       this.settingList.Add(new ExcelCellSettingBase(sheetIndex, startColumnIndex, endColumnIndex, startRowIndex, endRowIndex, style));

  318     }

  319   }

  320 }

위 메서드를 살펴 보자.
먼저 HSSFCellStyle 형의 셀의 스타일 개체를 생성한다.
그리고 ExcelCellStyle 개채의 값을 style 개체에 attatch를 하고 ExcelCellSettingBase 형의 개체를 생성하고 settingList 라는 제너릭리스트에 Add 한다.
ExcelCellSettingBase 클래스는 스타일이 정의된 영역에 대한 정보를 가지고 있다. 그러므로 settingList 라는 제너릭 리스트는 이 스타일이 정의된 영역에 대한 정보를 가진 컬렉션이다.

이제 바인딩을 해보자.

  548 public void DataBinding()

  549 {

  550   if (this.dsList.Count == 0)

  551   {

  552     throw new Exception("데이터가 없습니다.");

  553   }

  554   else

  555   {

  556     //하나의 DataSet이 하나의 시트

  557     for (int j = 0; j < dsList.Count; j++)

  558     {

  559       this.sheetIndex = j;

  560       int totalRowCountInSheet = this.SumOfTableRowsCount(j);

  561 

  562       int rowCount = 0;

  563 

  564       //데이터 갯수와 헤더의 갯수(있을거라고 가정)가 65536을 넘을 수 없다.

  565       //헤더는 각각의 테이블당 한개의 row를 차지하므로 한개의 시트에 속하는 테이블의 갯수와 동일하다.

  566       //하나의 시트의 Row가 65535를 넘게 되면 오류...

  567       totalRowCountInSheet += dsList[j].Tables.Count;

  568 

  569       if (totalRowCountInSheet > 65535)

  570       {

  571         throw new ArgumentOutOfRangeException("데이터의 수가 65535건이 넘었습니다.");

  572       }

  573       else

  574       {

  575         for (int i = 0; i < dsList[j].Tables.Count; i++)

  576         {

  577           if (Convert.ToBoolean(this.hasTitleList[j][i]))

  578           {

  579             this.CreateTitle(j, dsList[j].Tables[i].Columns, rowCount);

  580             rowCount++;

  581           }

  582           this.DataBinding(dsList[j].Tables[i], rowCount);

  583           rowCount += dsList[j].Tables[i].Rows.Count;

  584         }

  585       }

  586     }

  587   }

  588 }

지금까지 정의 된 값을 토대로 하여 데이터를 바인딩 한다.
560라인의 SumOfTableRowCount()메서드는 해당 시트에 바인딩 될 총 row의 합계이다.
이를 조사하는 이유는..
엑셀의 경우 하나의 시트에 65536행까지만 허용되기 때문이다.
그러나 Excel 2007의 경우에는 1백만행*65536컬럼 까지 지원한다고 한다..
579라인에 보면 CreateTitle()메서드가 보이는데 제목을 가진 DataTable이라면(577라인의 조건절) 제목을 시트에 쓴다.
그리고 582라인의 DataBinding 메서드에서 실제로 시트에 쓴다.

먼저 CreateTitle()메서드를 살펴보자.

  745 HSSFSheet sheet = this.wb.getSheetAt(sheetIndex);

  746 HSSFRow row = sheet.createRow(rowCount);

  747 for (short i = 0; i < dcc.Count; i++)

  748 {

  749   if (!dcc[i].ColumnName.StartsWith("Column"))

  750   {

  751     HSSFCell cell = row.createCell(i);

  752     cell.setEncoding(1);

  753     this.currentRowIndex = rowCount;

  754     this.currentColumnIndex = i;

  755     ExcelCellSettingBase cellStyle = this.settingList.Find(FindStyle);

  756     if (cellStyle != null)

  757     {

  758       HSSFCellStyle cs = cellStyle.CellStyle;

  759       cell.setCellStyle(cs);

  760     }

  761     cell.setCellValue(dcc[i].ColumnName);

  762   }

  763 }

발췌했다.
745라인에서 sheet개체를 얻고 746라인에서 createRow()메서드로 row를 만들었다.
749라인의 조건절은 자동생성된 컬럼을 걸르기 위함이다. 자동으로 생성된 컬럼은 의미가 없기 때문이다 . 자동생성된 컬럼의 이름패턴은 Column1, Column2..... 처럼 생성된다.
755라인에 주목하자. 
제너릭리스트의 Find()메서드를 사용하였다. 닷넷 2.0에서 새로나온 개념이다..^^; 
이 Find 메서드는 settingList 라는 제너릭 리스트에서 해당 조건에 맞는 첫번째 아이템을 반환한다.

  806 private bool FindStyle(ExcelCellSettingBase setting)

  807 {

  808   if (setting.SheetIndex == this.sheetIndex && setting.StartRowIndex <= this.currentRowIndex && setting.EndRowIndex >= this.currentRowIndex && setting.StartColumnIndex <= this.currentColumnIndex && setting.EndColumnIndex >= this.currentColumnIndex)

  809   {

  810     return true;

  811   }

  812   else

  813   {

  814     return false;

  815   }

  816 }

FindStyle 메서드를 살펴 보면 앞서 정의했던 settingList 제너릭 리스트에서 현재 Cell의 영역이 포함되어 있는 첫번째 ExcelCellSettingBase 개체를 반환한다.
음..한번도 안써보신 분이라면..약간 헤깔릴 수도....

어쨋든 해당되는 부분을 찾았으면 759라인처럼 셀에 스타일을 정의한 후에 761라인 처럼 값을 입력한다.

이제 582라인의 DataBinding 메서드를 살펴 보자.

  620 HSSFCellStyle cs = cellStyle.CellStyle;

  621 c.setCellStyle(cs);

  622 object o = dt.Rows[rowIndexInCurrentTable][currentColumnIndex];

  623 if (o.ToString() != "")

  624 {

  625   switch ((CellType)cs.getDataFormat())

  626   {

  627     case CellType.CELL_TYPE_NUMERIC:

  628       c.setCellValue(Convert.ToDouble(o));

  629       break;

  630     case CellType.CELL_TYPE_DATETIME:

  631       c.setCellValue(new java.util.Date(Convert.ToDateTime(o).ToLongTimeString().Trim()));

  632       break;

  633     case CellType.CELL_TYPE_STRING:

  634       c.setCellValue(o.ToString().Trim());

  635       break;

  636   }

  637 }

  638 else

  639 {

  640   c.setCellValue("");

  641 }

역시 발췌했다.
CreateTitle() 메서드와 마찬가지로 Find 메서드로 해당되는 ExcelCellSettingBase 개체를 반환하여 적용한다.
그리고 추가로 작업을 하나 더 하는데..
바로 셀의 타입을 결정하는 부분이다.
셀의 타입 역시 HSSFCellStyle 개체에 포함되어 있는 부분이다.
그렇기에 셀의 형식에 맞게끔 데이터를 형변환해서 입력해주어야 한다.
그 부분이 switch ~case 구문이다.
631라인의 경우에는 datetime형식을 표현하는 방식이 닷넷과 자바가 달라서..저런 방법을 취할수 밖에 없었다...쿨럭. 그리고 마지막으로 이벤트를 발생 시킨다.

  655 this.OnRowCount(thisnew RowCountEventArgs(rowIndexInCurrentTable + 1, dt.TableName));

지금까지 작업을 하였다면 하나의 워크북에 시트와 데이터가 모두 만들어 졌을 것이다.
이제 약간의 추가 작업을 선택적으로 해주면 되겠다.

추가적인 기능은

public void ColumnWidth(int sheetIndex, short columnIndex, short width);

public void GroupColumn(int sheetIndex, short startColumn, short endColumn);

public void GroupRow(int sheetIndex, int startRow, int endRow);

public void Merge(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex);

public void RowHeight(int sheetIndex, int startRowIndex, int endRowIndex, int height);

public void Split(int sheetIndex, int colSplit, int rowSplit);

위와 같다.

  • ColumnWidth - 컬럼의 폭을 결정한다, 모든 단위는 px 단위이다.
  • GroupColumn - 컬럼을 그룹화한다.
  • GroupRow - 로우를 그룹화한다.
  • Merge - 정의된 셀영역을 합친다.
  • RowHeight - 로우의 높이를 결정한다.
  • Split - 문서를 스플릿한다.

주의 할 것은 이 메서드들은 반드시 바인딩 후에 호출하여야 한다.는 것이다.

이제 마지막 단계이다.
이렇게 워크북을 만들었다면..이제 출력을 해야 할것이다.
출력은 2가지가 지원한다.
하나는 파일 출력이며 하나는 스트림출력이다.

  664 public void OutPut()

  665 {

  666   string fPath = this.fullName;

  667   if (fPath == "")

  668   {

  669     fPath = this.path + this.fileName;

  670   }

  671   using (Stream fileOut = new FileStream(fPath, FileMode.Create))

  672   {

  673     this.wb.write(fileOut);

  674   }

  675 }

OutPut()메서드는 파일 출력이다. 673라인의 write() 메서드로 정의된 파일명으로 파일이 생성된다.
윈도우즈 응용프로그램에서 사용하기 적합하다.

  681 public byte[] OutPutToWeb()

  682 {

  683   Stream fileOut = new MemoryStream();

  684   this.wb.write(fileOut);

  685   byte[] buffer = ((MemoryStream)fileOut).GetBuffer();

  686   fileOut.Close();

  687   return buffer;

  688 }

이 메서드는 byte[] 배열형으로 출력한다.  
웹의 경우 서버에 파일을 만들필요가 없기에 스트림으로 내려 주는 방식이 좋다. 샘플 예제는 웹방식으로 적용해 보았다.

지금까지...어셈블리에 대해서 알아 보았다.
이제 샘플코드를 보고..어떻게 사용하는지 알아보자.

   23 List<string> sheetNames = new List<string>(2);

   24 sheetNames.Add("sheet1");

   25 sheetNames.Add("sheet2");

   26 

   27 ExcelHandler handler = new ExcelHandler(sheetNames);

   28 handler.OnRowCount += new ExcelHandler.RowCount(handler_OnRowCount);

   29 

   30 handler.InsertTable(0, falsethis.MakeTitle());

   31 handler.InsertBlankRow(0, 3);

   32 handler.InsertTable(0, truethis.MakeDataTable("테스트"));

   33 handler.InsertTable(1, falsethis.MakeTitle());

   34 handler.InsertTable(1, truethis.MakeDataTable("테스트"));

   35 

   36 handler.SetCellStyle(0, 0, 0, 0, 0, this.MakeCellStyle1());

   37 handler.SetCellStyle(0, 0, 0, 10, 50, this.MakeCellStyle2(), CellType.CELL_TYPE_NUMERIC);

   38 handler.SetCellStyle(0, 1, 1, 10, 50, this.MakeCellStyle2(), CellType.CELL_TYPE_STRING);

   39 handler.SetCellStyle(0, 2, 2, 10, 50, this.MakeCellStyle2(), CellType.CELL_TYPE_DATETIME);

   40 

   41 handler.DataBinding();

   42 

   43 handler.RowHeight(0, 0, 0, 100);

   44 handler.ColumnWidth(0, 0, 300);

   45 handler.Merge(0, 0, 2, 0, 0);

   46 handler.Split(0, 0, 5);

   47 handler.GroupRow(0, 5, 10);

   48 

   49 //파일 다운로드

   50 this.Response.Clear();

   51 this.Response.AddHeader("Content-Disposition""attachment; filename=exceltest.xls");

   52 this.Response.ContentType = "application/vnd.ms-excel ";

   53 this.Response.BinaryWrite(handler.OutPutToWeb());

   54 this.Response.End();

22~25 라인에서 시트의 이름을 생성했다. 다시 말하지만 한글은 지원하지 않는다.
27라인에서 생성자를 만들고
28라인에서 이벤트를 등록하였다.(웹에서는 사실 이 이벤트가 소용없으나 선언된것이기 때문에 정의하지 않으면 예외를 던진다.)

30~34라인에서 DataTable을 각각의 시트에 입력하였다. 첫번째 시트에는 공란도 입력하였다. 

36~39 라인에서 스타일과 타입을 정의 하였다. 영역이 중복되어도 상관없지만..먼저 정의된것만 적용된다.^^;  테스트를 위하여 각각 숫자형, 문자형, 일시형을 정의해 보았다.

그리고 41라인 처럼 바인딩을 한다.

43~47라인에는 시트의 설정을 변경해 보았다. 어떻게 출력이 될까 하고 예측해보자...

50~54 라인에서는 웹상에서 출력하는 방법을 보여준 예이다.
주목 할 것은 바로 53라인이다.
Response.BinaryWriter() 메서드로 출력하는 것을 확인하기 바란다.

이제 샘플코드를 실행해서 엑셀파일을 다운 받아 보면..아래와 같은 결과가 나오겠다.

그리고 ..

마지막으로 스타일을 정의 하는 부분은 같이 업로드된 파일 처럼 코드스니핏을 이용하면 편하다.
혹시 이용법을 모르는 사람을 위해..
첨부된 excelStyle.snippet 파일을 C:\Documents and Settings\사용자이름\My Documents\Visual Studio 2005\Code Snippets\Visual C#\My Code Snippets 경로에 붙여 넣기를 한다.
그리고 VS2005를 재시동한다.

이제 화면에서 excel이라고 치면 인텔리센스가 뜬다...그게 코드 스니핏이다.



[출처] C#에서 OLEDB를 이용한 엑셀(EXCEL)파일 읽기 및 쓰기|작성자 이카루스


반응형
Posted by blueasa
, |

var 키워드

Programming/C# / 2012. 12. 9. 22:08

C# 3.0의 새로운 기능(part1: var키워드를 사용한 타입유추)

C#3.0이 등장 하면서 여러 가지 새로운 기능이 추가 되었습니다. 타입유추, 익명타입, 람다 표현식, 개체/컬렉션 초기화, Nullable 타입, 확장 메서드, 쿼리 키워드, 자동으로 구현된 속성(Automatic Property, 부분 매서드(partial method) 등등 상당히 많은 새로운 기능들이 추가 되었습니다. 이 기능 중 대부분의 기능은 LINQ의 기반이 되며 LINQ를 배우기 이전에 먼저 학습할 필요가 있습니다.

이번 강좌를 시작으로 앞에서 언급한 새로운 기능 중 LINQ의 기반이 되는 새로운 기능들에 대해 강좌를 연재 하려고 합니다. 언제 강좌가 마무리 될지 확실하지는 않지만 최대한 주기적으로 강좌를 연재하고 최대한 빠른 시간 안에 마무리 하도록 노력하겠습니다.

C# 3.0부터 지역 변수의 타입을 반드시 명시적으로 지정해줄 필요 없이 var키워드를 사용하여 컴파일러가 타입을 유추할 수 있도록 할 수 있습니다. 이는 뒤에서 배우게 될 명시적으로 타입을 지정하지 않는 익명 타입이나 가독성이 떨어지는 중첩된 제네릭(예: IEnumerable<IEnumerable<Product>>) 과 같이 특수한 경우에 var키워드를 사용하여 좀더 간단하게 타입을 표현할 수 있습니다. 
다른 개발자가 코드를 보았을 때 이해 할 수 있는 수준으로 작성이 되면 var키워드를 통한 타입 유추는 좀더 간결한 코드를 만들어 낼 수 있는 훌륭한 기능이라 할 수 있겠습니다.

1. 타입유추는 어떻게 이루어 지는가?

var키워드는 변수를 선언하고 그 값을 할당하는 초기화 구문에서 '='을 기준으로 오른쪽 부분의 형태에 따라 컴파일러에게 타입유추를 지시 하게 됩니다.

var i = 5

위의 코드를 보면 5라는 값이 변수 i에 할당되므로 컴파일러는 5라는 값이 숫자 즉 int타입이라는 것을 미리 알고 있기 때문에 변수 i를 int타입으로 유추하게 되는 것 입니다.

//string타입으로 유추 
var s = "Taeyo.NET"; 
//double타입으로 유추 
var d = 12.5; 
//char타입으로 유추 
var c = 'k';

C#의 기본 타입 뿐만 아니라 사용자 정의 타입(Class) 및 익명 타입(Anonymouse Type) 또한 유추 될 수 있습니다.

그럼 간단하게 사용자 정의 타입(Class)을 유추하는 코드를 살펴 보도록 하겠습니다.

Class Person 

    public string FirstName; 
    public string LastName; 
    public int Age; 


Person person = new Person(); 
person.FirstName = "경균"; 
person.LastName = "김"; 
person.Age = 19; 

//사용자 지정 타입으로 유추 
var p = person;

Person이라는 새로운 Class를 만들고 개체를 생성한 후 var 키워드로 선언된 p라는 변수에 person개체를 할당하게 되면 컴파일러에 의해 변수 p는 Person타입으로 유추 됩니다.

익명 타입 또한 컴파일러에 의해 유추가 가능 합니다. 
아래 코드를 보면 식의 오른쪽 부분이 익명 타입이고 그 타입을 변수 p가 받게 되면 컴파일러에 의해 변수 p는 익명 타입으로 유추가 됩니다.

//익명타입으로 유추 
var p = new { FirstName = "경균", LastName = "김", Age = 19 };

위의 예제 코드에서는 익명타입의 형태만 알아 두고 자세한 내용은 다음 강좌에서 자세히 알아 보도록 하겠습니다.

다음은 쿼리표현식의 타입을 유추하는 코드 입니다.

// IEnumerable타입으로 유추 
var products = 
      from p in Products 
      where p.ProductName == "chai" 
      select p;

위의 쿼리 표현식은 LINQ를 활용함에 있어서 가장 많이 사용되는 구조입니다. 코드에 대해 간단히 알아보면 Products테이블에서 ProductName이 ‘Chai’와 같은 상품을 출력하는 구문으로 그 결과는 IEnumerable<T>타입입니다. 그렇기 때문에 products변수는 IEnumerable<T>타입으로 유추 됩니다. 쿼리 표현식은 마지막 강좌에서 자세히 다루도록 할 것입니다.

2. 정말로 유추를 하는가?

간단한 예제 코드를 컴파일 하고 실제 어떻게 타입유추가 일어나는지를 확인 하기 위해 IL DisAssembler(ildasm.exe)를 사용하여 컴파일된 IL코드를 확인 해 보도록 하겠습니다.

아래는 Int타입과 직접 작성한 Person타입을 이용하는 예제 코드 입니다.

Class Person 

    public string FirstName; 
    public string LastName; 
    public int Age; 


class TypeInference 

    public int GetInt() 
    { 
        var i = 10; 
        int b = 11; 
        var p = new Person(); 
        return i; 
    } 
}

타입유추를 테스트 하기 위해 변수 i는 var키워드를 통해 초기화 했고 변수 b는 일반적인 방식으로 int타입으로 선언 하였습니다. 그리고 사용자 정의 타입인 Person타입을 변수 p에 할당 했습니다. 
위의 코드를 컴파일 한 후 IL DisAssembler를 사용해 IL코드를 확인 해 보면 아래와 같은 코드를 볼 수 있습니다.

변수 i와 변수 b 모두 int32형태로 초기화 된 것을 확인 할 수 있으며 변수 p는 정확히 Person타입으로 유추된 것을 확인 할 수 있습니다.

3. var키워드의 사용 범위

var키워드는 아무데서나 사용할 수 있는 것은 아닙니다. 매서드 내부의 지역변수나 for문, foreach문, using문의 초기화 구문에서 사용될 수 있습니다.

매서드 내의 지역변수로서 사용

public int GetTotalCount() 
{
    var cnt = db.TotalCount(); 
    return cnt; 
}

for문의 초기화 구문에서 사용

for(var i = 0 ; i <10 ; i++){…}

foreach문의 초기화 구문에서 사용

foreach(var dr in dt.Rows){…}

using문의 초기화 구문에서 사용

using(var file = new StreamReader("c:\test.txt")){….}

4. var키워드를 사용하면서 이것만은 주의하자.

var 키워드는 자바스크립트나 VB의 "Variant"로 오해할 수 있습니다. 하지만 C#의 var키워드는 런타임에 바인딩 되지 않고 컴파일 타임에 컴파일러가 적합한 타입을 결정하고 할당하게 됩니다. 
그렇기 때문에 컴파일러가 적합한 타입을 결정하려면 선언과 동시에 초기화가 이루어 져야 하며 Null은 사용할 수 없습니다.(Null은 타입이 없기 때문이죠) 또한 클래스 범위에서는 사용할 수 없고 반드시 매서드 내의 지역변수로 사용해야 합니다.

var키워드의 상당히 편리하지만 명시적으로 타입이 선언되어 있지 않기 때문에 다른 개발자가 코드를 보게 되면 코드를 이해하기 난해 할 수 있습니다. var키워드를 반드시 필요한 경우(익명타입, 쿼리표현식의 결과타입, 중첩된 제네릭 타입 등)에 사용하면 보다 나은 개발 편의성 및 생산성 향상에 도움이 될 것입니다. 타입유추는 LINQ를 사용하기 위해 반드시 알아야 할 중요한 내용이기 때문에 첫번째 강좌에서 이렇게 다루었습니다. 다음 강좌는 익명타입 과 개체/컬렉션 초기화에 대해 알아 보도록 하겠습니다. 
감사합니다.


출처 : http://www.taeyo.pe.kr/Lecture/ASPNET2/KK_CSharp3_01.asp


반응형
Posted by blueasa
, |

안녕하세요? 맨날맑음 입니다.

WPF나 Silverlight 와는 다르게 Windows Form에는 '로드 완료' 이벤트가 없습니다. Load 이벤트가 제공 되고 있기는 하지만 MSDN에 나온 의미는 '폼이 처음으로 표시되기 전에 발생합니다.' 입니다. 즉 로드 완료가 아니라는 것입니다.

이것이 왜 문제일까?
로드 완료가 아닌 로드중 이벤트만 제공하여 생기는 문제가 있습니다.
예를 들어 프로그램을 시작할 때 폼을 숨기려고 한다고 하면, Hide() 메서드를 호출 해야합니다.
실행 할때 숨기려 했으니 당연히 폼의 생성자에 Hide()를 넣어보지만 전혀 동작 하지 않습니다.
두번째 방법으로 폼의 Load 이벤트 핸들러 안에 Hide()를 넣어봅니다. 역시 동작하지 않네요;
정확하지 는 않지만, MSDN의 설명대로 폼이 아직 표시되기 전이라 Hide()가 먹히지 않는 것 같습니다.
이밖에 Focus()나 몇몇 다른 메서드도 동작하지 않습니다.

어떻게 해결할까?
위의 문제를 해결하려면 '로드완료' 이벤트를 찾아야 하는데 아무리 뒤져보아도 로드완료는 존재하지 않았습니다.
그래서 임시 방편으로 Application.Idle 이벤트를 사용하여 해결 하였습니다. Application.Idle 이벤트는 말그대로 프로그램이 아이들 상태(쉬고 있을때) 발생합니다. 이걸 응용하면, 생성자에서 이벤트를 걸어주고, 이벤트 핸들러에서 이벤트를 빼주는 방법으로 로드 완료된 시점을 잡을 수 있습니다. 물론 엄밀히 말하면 프로그램이 더이상 할 일이 없어 쉬고 있을 시점을 잡아내는 것입니다.

using System;

using System.Windows.Forms;

namespace Sample

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

            //Idle이벤트를 걸어준다.

            Application.Idle += Application_Idle;

        }

        void Application_Idle(object senderEventArgs e)

        {

            //Idle이벤트를 없앤다.

            Application.Idle -= Application_Idle;

            this.Hide();

        }

    }

}


위의 코드에서 Idle 이벤트 핸들러에서 Application.Idle -= Application_Idle; 이벤트를 빼주는 방법으로 프로그램이 처음으로 쉴때(로드완료)를 잡아내는 것입니다. 이렇게 임시 방편이나마 로드 완료 시점을 잡아서 hide() 메서드를 동작시킬 수 있습니다.




반응형
Posted by blueasa
, |

Invoke 를 써서 델리게이트를 간단히 하는 방법이 있다고 한다.

1곳에서만 메인쓰레드의 권한이 필요할경우, 이 방법을 쓰면 좋다고 한다.

만약 그렇지 않다면, 쓰는데마다 저렇게 선언을 해줘야한다고 한다.

 참고 해보길 바란다.

 

이 포스트를 보고있는 여러분들은, 모두 닷넷 프로그래밍에 관심이 있으시고,

현재도 개발툴로 사용중인 분이실 것이다. 쓰레드기반 프로그래밍을 할때(.Net)에서

폼(엄밀히 말하면 윈도우)의 Child컨트롤(요곳도 윈도우)들의 멤버변수들을 조작

하기 위해서는, 메인쓰레드의 권한이 필요하다. 따라서, 메인쓰레드의 권한이 필요

할경우, this.Invoke() 메서드를 쓰게되는데, 이때 이 메서드를 쓰면, 별별놈의

Delegate를 선언해줘야 하고, 또 선언한 델리게이트의 인스턴스를 할당해, 사용

해야해서 코드가 매우 더러워지는것을 느꼈을것이다.

 

예전에 아는 동생녀석의 C#코드를 보고있던 중. this.Invoke()를 아주 이쁘게

잘 써놓은 것을 발견했다. 물론 그놈은.. 예전부터 프로그래밍을 해왔던지라,

모르는 언어도 없고, 시스템 프로그래밍을 가지고 노는애였다.(__)

아무튼 내가 닷넷프로그래밍을 처음배울때, 그놈에게 C#.Net으로된 채팅서버

및 클라이언트 프로그램의 소스를 봐서, Invoke()메서드를 이쁘게 잘쓰는방법을

우연히 터득하게 되었다. 보통의 사람들이라면 Delegate 선언, 인스턴스할당,

Delegate변수명명 까지의 귀찮은 일을 모두 하겠지만, 이 포스트를 봤다면,

이제부터 그렇게 하지말자^^;

 

일단 사용하는 방법은 아주 간단하다. 그냥 this.Invoke()메서드 인자부분에

new MethodInvoker( delegate() { 코드 })를 써주면 끝이다. 이렇게 처리하게

되면, 저런 귀찮은 방법을 사용하지 않고, 그냥 저기안에 코드만 쓰면된다^^;

물론 저렇게 일자로 쓰면, 안이쁘겠지만, 적절히 엔터누질러주시면,

엄청 이쁜코드가 된다. ^^; 동생녀석이 썼던 코드를 보여주겠다.

 

             this.Invoke(new MethodInvoker(
                 delegate()
                 {
                     strCurTime = string.Format("{0:D2}:{1:D2}:{2:D2}", DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
                     string[] strData = new string[3];
                     strData[0] = lstLog.Items.Count.ToString();
                     strData[1] = strCurTime;
                     strData[2] = strTLog;
                     lstLog.Items.Add(new ListViewItem(strData));
                 }
                 )
            );

 

이렇게쓰면, 아주이쁘게 써진다, 또한, Form의 전역공간에 델리게이트를 선언할

필요도 없어져서, 전체적으로 코드가 안정적으로 보인다^^;

저 코드는, 채팅서버에서 로그를 추가하는 델리게이트를 기술하는 것이다^^;

물론 이방법은, 이런 델리게이트가 필요한 곳이 1곳일때만 쓰는것을 추천한다.

만약 그렇지 않다면, 쓰는데마다 저렇게 선언을 해줘야한다.(__)

하지만, 1곳에서만 메인쓰레드의 권한이 필요할경우, 저렇게 하는것이

더 바람직하고, 이쁘다^^;



출처 : http://xmlangel2.blogspot.kr/2009/05/c-%EC%9C%88%ED%8F%BC%EA%B8%B0%EB%B0%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%ED%95%A0%EB%95%8C-invoke-%EC%9D%B4%EC%81%98%EA%B2%8C%EC%93%B0%EA%B8%B0.html

반응형
Posted by blueasa
, |

Invoke, MethodInvoker, BeginInvoke - EndInvoke

 Introduction

  UI Control들은 폼 구동시 실행되는 하나의 쓰레드에서 구동된다. 따라서 사용자가 실행시킨 쓰레드는 별도로 실행 되기 때문에 이 메인 쓰레드에 적절한 마샬링 없이 다른쓰레드에서 직접 접근하면 다른 쓰레드를 침범하는 것이다. (Cross Thread Problem) 이런 경우에는 프로그램이 개발자가 설계한대로 잘 동작하지 않을 수 있다.(Race Condition,DeadLock)  따라서, 안전하게 동작하게 하기위하여 .Net 환경에서는 Invoke를 제공하고 있다.

 본 내용을 무시한 채 프로그램을 작성하면 InvalidOperationException을 발생시키고 . Debug 창에서 "컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다."라는 메세지가 표시 된다. 하지만 디버깅 환경이 아니라면 프로그램은 겉보기에 정상 동작하는 것 처럼 보일 수 있으므로 조심해야 된다. !

 

난 대충 좀 안되도 상관 없다고 생각하시는 분들은 이 예외를 비활성화 할 수도 있습니다.

 

CheckForIllegalCrossThreadCalls 속성 값을 false로 설정하여 이 예외를 비활성화할 수 있습니다. 그러면 컨트롤이 Visual Studio 2003에서와 같은 방식으로 실행됩니다.

 

이 노트에서 학습하실 때 유념하셔야 될 내용은

  1. Control Invoke 와 Delegate Invoke의 차이점을 숙지한다.
  2. Invoke와 BeginInvoke의 차이점과 사용법을 숙지한다.
  3.  

 자 그럼 쓰레드에서 안전하게 Windows Form control을 제어하게 하는 방법에 대해 알아보자.

우선,  InvokerRequired 를 알고 시작하자.  이 속성은 Invoke메쏘드를 호출해야되는 상황인지 알려준다.

 

Invoke

 

 쓰레드에서 폼 컨트롤 하기 위해서는 별도의(SetTextononTextBox1) 메쏘드를 만들어 사용하면 좋다. 예를들어, Textbox에 값을 입력한다면 아래와 같은 'SetTextonTextBox1' 메쏘드를 만들어서 사용하면 Cross thread 환경이든 내부 쓰레드 사용환경에서든 둘다 사용할 수 있다. 또는 Delegate 메쏘드가 FormControl을 인자로 받아서 사용할 수도 있다.

 Invoke를 사용할 때 인자를 넘겨줘야 하는 메쏘드 필요하면 할때는 반드시 delegate를 선언한 후에 사용하여야 한다.

 

  1.         delegate void SetTextCallback(string txt);                    
            private void SetTextonTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
                    this.Invoke(new SetTextCallback(SetTxtCB), new object[] { txt }); //그냥 txt를 넘겨줘도 된다
                }
                else
                {
                    this.textBox1.Text += txt;
                }
            }
            private void SetTxtCB(string txt)
            {
                this.textBox1.Text+=txt;
            }

 

  1.         public delegate void UpdateText(Control ctrl, string text);
            public void UpdateTextFunc(Control ctrl, string text)
            {
                if (ctrl.InvokeRequired)
                {
                    ctrl.Invoke(new UpdateText(UpdateTextFunc), new object[] { ctrl, text });
                }
                else
                    ctrl.Text += text;
            }

 

인자가 없는 메쏘드를 호출할 때는 간단하게 MethodInvoker를 사용하면 좋다.

 

  1.         private void SetTextonTextBox1(string txt)
            {
                if (this.textBox1.InvokeRequired)
                {
  2.                this.Invoke(new MethodInvoker( delegate { this.textBox1.Text+=txt; }) );

                }
                else
                {
                    this.textBox1.Text += txt;
                }
            }

 

BeginInvoke , EndInvoke

 

 우선 BeginInvoke를 설명하기 전에 Windows Form 의 Control.BeginInvoke 메쏘드와 Delegate.BeginInvoke 메쏘드에 대해서 차이점을 설명하고 본 단락에서는 Winfows Form Control의 BeginInvoke만 설명하도록 하겠다.

 Delegate.BeginInvoke는 Asynchronous Delegate를 만들어서 콜백을 하는 것이라고 생각하면 되겠다. 다시말해서, CLR에서 관리하는 쓰레드풀에서 해당 메쏘드를 큐잉한다. 쉽게 얘기해서 별도의 쓰레드를 만들어서 Delegate를 실행한다고 보면 된다.  장점은 IAsyncResult를 이용해서 Object 결과값을 넘겨 받을 수 있다. 보다 자세한 사항은 이곳을 참조!

  Windows Form의 Control.BeginInvoke는 Control 내부 핸들이 작성된 쓰레드(메인쓰레드)에서 지정된 대리자를 비동기식으로 실행한다. 비동기식이므로 실행을 대기하지 않고 BeginInvoke는 즉시 Return 한다.

 차이점을 정리 하자면 Control.BeginInvoke는 실행코드의 GUI Thread에 작성된 코드이고 Delegate.BeginInvoke는 쓰레드풀 쓰레드에 사용된다. 또한 Control.BeginInvoke의 경우는 EndInvoke를 호출하지 않아도 되나 Delegate.BeginInvoke의 경우는 반드시 Delegate.EndInvoke를 호출해줘야 한다 안그러면 메모리 릭이 발생한다.

 

 

  1. public delegate void InvokeDelegate();
  2. private void Invoke_Click(object sender, EventArgs e)
    {
       myTextBox.BeginInvoke(new InvokeDelegate(InvokeMethod));
    }
    public void InvokeMethod()
    {
       myTextBox.Text = "Executed the given delegate";
    }
  3.  

 

 Endinvoke

 Reference
  1. http://msdn.microsoft.com/ko-kr/library/ms171728.aspx
  2. http://xmlangel.textcube.com/6
  3. http://jongkok4.net/entry/펌c-UI-쓰레드-마샬링-Invoke-BeginInvoke?TSSESSIONjongkok4net=6e5ec00b34c31e0126e9a64412f7a627
  4. http://timl.net/2008/01/begininvoke-methodinvoker-and-anonymous.html
  5. http://shiman.wordpress.com/2008/09/10/c-net-delegates-asynchronous-invocation-begininvoke-method/
  6. http://msdn.microsoft.com/ko-kr/library/0b1bf3y3(VS.80).aspx
  7. http://kristofverbiest.blogspot.com/2007/02/don-confuse-controlbegininvoke-with.html
  8. http://www.albahari.com/threading/#_Introduction
  9. http://www.yoda.arachsys.com/csharp/threadstart.html 




반응형
Posted by blueasa
, |