반응형

안녕하세요~

 

코딩연습생입니다

 

C# WINFORM에서 그리드뷰의 내용은 엑셀로 내보개기에 대한 포스팅을 할려고 합니다

 

예전 OLEDB를 이용한 엑셀 연동을 포스팅한적이 있는데요

 

 

https://codingman.tistory.com/103?category=715728

 

[C#] OLEDB를 통한 엑셀파일 불러오기(데이터그리드뷰)

안녕하세요 코딩 연습생 입니다 이전 포스팅에서 직접 엑셀의 API를 활용하여 엑셀파일의 내용을 불러와 데이터그리드뷰와 연동하는 포스팅을 업로드했었는데요 https://codingman.tistory.com/100 [C#] ��

codingman.tistory.com

 

해당 예제를 사용하여 구현하셔도 상관은 없습니다~ 하지만 이번엔 Interop.Excel를 사용하여 구현을 해볼껀데요

 

프로젝트를 하나 생성하시고 윈폼을 하나 만듭니다

 

 

저는 위와 같은 화면을 디자인해서 생성하였습니다

 

아래쪽에 보이는 그리드뷰 영역에 "엑셀"이라는 버튼을 누루면 내보내기가 가능한 기능을 구현을 해볼려고 해요

 

일단 첫번째로 엑셀을 사용하기 위한 선언을 합니다

using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
using System.IO;

 

두번째는 버튼을 추가하여 클릭 이벤트를 생성한뒤에 다음과 같이 코딩을 합니다

 

        private void hoverGradientButton3_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Title = "Save as Excel File";
            sfd.Filter = "Excel Documents (*.xls)|*.xls";
            sfd.FileName = "납품처마스터.xls";

            if (sfd.ShowDialog() == DialogResult.OK)
            {
                copyAlltoClipboard();

                object misValue = System.Reflection.Missing.Value;
                Excel.Application xlexcel = new Excel.Application();

                xlexcel.DisplayAlerts = false;
                Excel.Workbook xlWorkBook = xlexcel.Workbooks.Add(misValue);
                Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);

                try
                {
                    Excel.Range rng = xlWorkSheet.get_Range("D:D").Cells;
                    rng.NumberFormat = "@";
                    Excel.Range CR = (Excel.Range)xlWorkSheet.Cells[1, 1];
                    CR.Select();
                    xlWorkSheet.PasteSpecial(CR, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, true);

                    xlWorkBook.SaveAs(sfd.FileName, Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
                    xlexcel.DisplayAlerts = true;
                    xlWorkBook.Close(true, misValue, misValue);

                    //파일 닫기... 
                    uint processId = 0;
                    GetWindowThreadProcessId(new IntPtr(xlexcel.Hwnd), out processId);
                    xlexcel.Quit();
                    if (processId != 0)
                    {
                        System.Diagnostics.Process excelProcess = System.Diagnostics.Process.GetProcessById((int)processId);
                        excelProcess.CloseMainWindow();
                        excelProcess.Refresh();
                        excelProcess.Kill();
                    }

                    MessageBox.Show("엑셀 파일 생성이 완료 되었습니다.");

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                    xlWorkBook.Close();
                    uint processId = 0;
                    GetWindowThreadProcessId(new IntPtr(xlexcel.Hwnd), out processId);
                    xlexcel.Quit();
                    if (processId != 0)
                    {
                        System.Diagnostics.Process excelProcess = System.Diagnostics.Process.GetProcessById((int)processId);
                        excelProcess.CloseMainWindow();
                        excelProcess.Refresh();
                        excelProcess.Kill();
                    }
                }
            }
        }

 

몇가지 오류가 뜨실껀데 아래의 내용을 적용하면 모두 해결 되실겁니다

 

두번째는 그리드뷰의 내용을 클립보드(Clipboard)안에 담는 함수 부분입니다

 

        private void copyAlltoClipboard()
        {
            dataGridView1.SelectAll();
            DataObject dataObj = dataGridView1.GetClipboardContent();
            if (dataObj != null)
                Clipboard.SetDataObject(dataObj);
        }

 

그리고 C#에서 엑셀을 연동하여 사용하시면 항상 프로세스에 엑셀 찌꺼기(?)가 남아 있습니다

 

그걸 해결하기 위해 몇번 삽질을 했는데요

 

이를 해결하기 위해 프로젝트 소스 상단에 DLL 하나를 Import 시켜줍니다

 

        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

 

이렇게 하시면 엑셀 프로세스를 불러와 사용한뒤에 해당 PID값을 기억헀다가 종료시에 강제 삭제해주게 됩니다

 

 

실행 순서는 이렇게 됩니다

 

 

버튼을 누루게 되면

 

 

이렇게 저장 위치를 선택하실수 있게 나오고

 

 

저장이 완료되면 위의 사진과 같이 그리드가 전체 선택이 되면서 저장되어집니다

 

엑셀 파일을 열어보시면 이렇게 헤더까지 그대로 내보내기 되어 진 모습을 볼 수 있네요

 

 

감사합니다~

반응형
반응형

안녕하세요

 

코딩 연습생입니다

 

아직도 코로나19로 인해서 기업들 소상인 분들 모두 참 어렵게 지내고 계시는거 같습니다

 

저 또한 회사원으로 회사가 많이 힘들어 지고 있다고 체감할 정도니깐요

 

그래도 국가에서 이래저래 도움을 줄 방법을 많이 고민하고 시행할려고 하는거 같은데 어찌 될지는 잘 모르겠네요

 

그래도 저희는 의지에 한국인 아니겠습니까?

 

이 또한 잘 이겨내리라 생각하고 열심히 살아봐야겠죠!?ㅋㅋ

 

블로그를 운영하면서 참 은근히 스트레스를 받네요 빨리 빨리 더 많은 정보를 공유하고 싶은데

 

글하나 쓰기까지 참 시간이 많이 소요 됩니다~ 거기에 회사 일을 하면서 해야 하는지라 쉽지가 않네요

 

평소 인터넷 검색을 통해 많은 블로거 분들의 글을 아무 생각없이 읽어 넘겼었는데 요즘은 참 많은 생각이 듭니다

 

역시 사람을 겪어봐야 느끼는 동물이라는게 새삼 느껴지네요ㅎ

 

예전 포스팅중에 엑셀 파일을 읽어서 그리드뷰에 불러오기하는 포스팅을 올린적이 있는데

 

그거와 유사한 기능을 구현해 볼겁니다

 

https://codingman.tistory.com/11

 

[C#] 엑셀파일 내보내기(그리드뷰)

///

/// 데이타 테이블을 엑셀로 받아온다. //////원본 데이타 테이블. ///원본 데이타 테이블의 컬럼들. ///codingman.tistory.com

 

블로그 초기 운영때 올린 글이라 별다른 설명이 없이 기능 소스만 공개 했었네요ㅎ

 

위의 포스팅과 차이점은 이미 템플릿화된 파일을 프로젝트에 삽입한뒤에 DB의 값을 불러오기하여 엑셀이 뿌려주는

 

겁니다

 

그렇게 되면 엑셀파일에는 항상 최산의 데이터가 자동 삽이된 상태로 사용자가 문서를 열어볼수 있는거죠

 

구현이 어렵지는 않지만 생각의 발상 차이라고 할수 있을거 같습니다

 

첫번째로 프로젝트에 템플릿으로 사용할 엑셀 문서를 포함 시켜 줍니다

 

 

프로젝트명칭은 회사에서 사용되는 시스템을 수정한것이라 혹시 몰라 숨김 처리 하였습니다ㅎㅎ

 

저기에 삽입된 엑셀 문서에는 셀마다 자동 수식이 걸려있는 문서 말그대로 템플릿 파일입니다

 

빈 셀에 DB의 값을 넣어주면 자동으로 수식에 의해 계산이 되겠죠?

 

음...엑셀(템플릿)파일의 내용을 열어서 보여드리고 싶지만 회사정보가 나오기 때문에 생략하겠습니다

 

메인 화면에서 아래와 같이 Excel 내려받기를 실행할 버튼을 생성해 줍니다

 

 

대충 감이 오실겁니다 고객 생상 일정 정보를 토대로 회사내 출하 계획을 생성하는 프로그램을 만들고 있는 화면중에

 

하나입니다ㅎㅎㅎ

 

자 그러면 해당 버튼을 클릭했을때 시행될 이벤트 코드를 생성해야겠지요?

 

Excel 내려받기 버튼의 클릭이벤트를 생성합니다

 

 

생성된 이벤트에 다음과 같이 코딩을 해줍니다

 

private void hoverGradientButton1_Click(object sender, EventArgs e)
        {
            string ExcelPath = Environment.CurrentDirectory + @"\Excel\Shipping_semple.xlsx";
            string ExcelEndPath = Environment.CurrentDirectory + @"\Excel\Shipping_semple_" + DateTime.Now.ToString("yyyyMMdd") + ".xlsx";

            //파일생성유무 확인(기준 하루 한번 생성이 가능 재생성시 그존 파일 삭제.
            if (File.Exists(ExcelEndPath))
            {
                File.Delete(ExcelEndPath);
            }


            try
            {
                ExcelApp = new Excel.Application();
                wb = ExcelApp.Workbooks.Open(ExcelPath,
                                             0,
                                             true,
                                             5,
                                             "",
                                             "",
                                             true,
                                             Excel.XlPlatform.xlWindows,
                                             "\t",
                                             false,
                                             false,
                                             0,
                                             true,
                                             1,
                                             0);
                ws = wb.Worksheets["Sheet1"] as Excel.Worksheet;

                //엑셀 시트 인덱스 번호는 0,0 부터 시작 하는 것이 아니라 1,1 A1 부터 시작 함. 0,0 으로 시작하면 오류... 
                //시트에 값 쓰기... 
                string query = "SELECT 고객,";
                query += "             납품처,";
                query += "             품목정보,";
                query += "             목표품번,";
                query += "             제품단위,";
                query += "             제품스팩";
                query += "        FROM 품목마스터";
                
                //DB 연결 클래스 입니다
                DB db = new DB();
                
                DataTable dt = db.ExcuteQuery(query);

                if (dt.Rows.Count > 0)
                {
                    for (int i = 0; i <= dt.Rows.Count - 1; i++)
                    {
                        ws.Cells[7 + i, 1] = dt.Rows[i]["고객"].ToString();
                        ws.Cells[7 + i, 2] = dt.Rows[i]["납품처"].ToString();
                        ws.Cells[7 + i, 3] = dt.Rows[i]["품목정보"].ToString();
                        ws.Cells[7 + i, 4] = dt.Rows[i]["목표품번"].ToString();
                        ws.Cells[7 + i, 5] = dt.Rows[i]["제품단위"].ToString();
                        ws.Cells[7 + i, 6] = dt.Rows[i]["제품스팩"].ToString();
                    }

                }

                //다른 이름으로 저장하기... 
                //wb.Save(); 
                // => 오픈한 파일 그대로 저장... 
                if (File.Exists(ExcelEndPath)) File.Delete(ExcelEndPath);
                wb.SaveAs(ExcelEndPath);

                //파일 닫기... 
                wb.Close(false, Type.Missing, Type.Missing);
                wb = null;
                ExcelApp.Quit();

                MessageBox.Show("엑셀 파일 생성이 완료 되었습니다.");

            }
            catch (Exception ex)
            {
                //객체들 메모리 해제 
                ReleaseExcelObject(ws);
                ReleaseExcelObject(wb);
                ReleaseExcelObject(ExcelApp);
                GC.Collect();
            }
            finally
            {
                //객체들 메모리 해제 
                ReleaseExcelObject(ws);
                ReleaseExcelObject(wb);
                ReleaseExcelObject(ExcelApp);
                GC.Collect();
            }
        }

 

버튼을 클릭해서 템플릿 파일을 생성하게 되면 원본파일이 아닌 생성일자가 붙어 있는 신규 파일이 생성됩니다

 

 

내가 원하는 Cell위치에 DB 값이 들어가 있는 템플릿 파일이 생성된거죠

 

제가 개발로 사용하고 있는 노트북이 구형이라 속도가 많이 느릴경우 메모리를 늘려보시는것도 방법이 될거 같습니다

 

 

반응형

+ Recent posts