반응형

안녕하세요~

 

코딩연습생입니다

 

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값을 기억헀다가 종료시에 강제 삭제해주게 됩니다

 

 

실행 순서는 이렇게 됩니다

 

 

버튼을 누루게 되면

 

 

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

 

 

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

 

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

 

 

감사합니다~

반응형
반응형

안녕하세요

 

코딩연습생입니다

 

이번 포스팅은 C#에서 데이터뷰로 많이 사용하는 기본컨트롤러인 데이터 그리드뷰에서

 

엑셀의 자료를 복사하여 붙여넣기 하는 기능을 구현해보고자 합니다

 

장점은 대량의 자료를 한번에 가져올수 있다는게 장점이고

 

단점은 그리드뷰와 엑셀의 셀형식과 구조 같아야하고, 행의 수를 조정해 줘야 한다는게 단점입니다

 

그래도 일단 기초적인 구조를 설명드리고 개선은 여러분의 노력이겠지요?ㅎ

 

[*개발 환경]

- Microsoft Visual Studio 2017

 

신규 폼을 하나 생성하고 그 위에 데이터드리드 뷰 컨트롤러를 배치 합니다

(간단한 부분이라 상세 설명은 생략하고 완성 이미지를 보여드릴께요)

폼 Init부분에 그리드에 대한 속성을 설정 합니다

(저의 폼 이름은 subCustList입니다)

        public subCustList()
        {
            InitializeComponent();

            //데이터그리드뷰 복&붙을 위한 초기 설정.
            this.dataGridView1.AllowUserToDeleteRows = false;
            this.dataGridView1.AllowUserToOrderColumns = true;
            this.dataGridView1.AllowUserToResizeRows = false;
            this.dataGridView1.RowHeadersVisible = false;
            this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
            this.dataGridView1.KeyUp += new System.Windows.Forms.KeyEventHandler(this.dataGridView_KeywordBidAmt_KeyUp);
        }

 

다음 KeyUp과 Null값 처리를 위한 함수를 작성 합니다

private void dataGridView_KeywordBidAmt_KeyUp(object sender, KeyEventArgs e)
        {
            //if user clicked Shift+Ins or Ctrl+V (paste from clipboard)
            DataGridView grid = sender as DataGridView;
            if (e.Control && e.KeyCode == Keys.V)
            {
                try
                {
                    char[] rowSplitter = { '\r', '\n' };
                    char[] columnSplitter = { '\t' };
                    int[] columnsOrderArray = dataGridView1.Columns.Cast<DataGridViewColumn>().Where(x => x.Visible).OrderBy(x => x.DisplayIndex).Select(x => x.Index).ToArray();

                    //get the text from clipboard
                    IDataObject dataInClipboard = Clipboard.GetDataObject();
                    string stringInClipboard = (string)dataInClipboard.GetData(DataFormats.Text);

                    //split it into lines
                    string[] rowsInClipboard = stringInClipboard.Split(rowSplitter, StringSplitOptions.RemoveEmptyEntries);

                    //get the row and column of selected cell in grid
                    int r = grid.SelectedCells[0].RowIndex;
                    int c = columnsOrderArray[grid.SelectedCells[0].ColumnIndex];

                    //add rows into grid to fit clipboard lines
                    if (grid.Rows.Count < (r + rowsInClipboard.Length))
                    {
                        grid.Rows.Add(r + rowsInClipboard.Length - grid.Rows.Count);
                    }

                    // loop through the lines, split them into cells and place the values in the corresponding cell.
                    for (int iRow = 0; iRow < rowsInClipboard.Length; iRow++)
                    {
                        //split row into cell values
                        string[] valuesInRow = rowsInClipboard[iRow].Split(columnSplitter);

                        //cycle through cell values
                        for (int iCol = 0; iCol < valuesInRow.Length; iCol++)
                        {
                            //assign cell value, only if it within columns of the grid
                            if (columnsOrderArray.Count() - 1 >= c + iCol)
                            {
                                int idx = columnsOrderArray[c + iCol];
                                grid.Rows[r + iRow].Cells[idx].Value = valuesInRow[iCol];
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("알 수 없는 데이터 형식입니다. Excel에서 복사 후 붙여넣기해주세요" + Environment.NewLine + ex.Message);
                }
            }

            if (e.KeyCode == Keys.Delete)
            {
                foreach (DataGridViewCell cell in grid.SelectedCells)
                {
                    //선택된 Cell을 모두 null로 변경해버린다.
                    cell.Value = null;
                }

                NullDataGridViewRowDelete(grid);
            }

        }

        private void NullDataGridViewRowDelete(DataGridView dgv)
        {
            //아 머리아파서 짜기 귀찮어 그냥 맨뒤꺼인 추가안된에 제외하고 나머지 모두삭제 이때 역순으로 해야지 인덱스안꼬여
            List<DataGridViewRow> temp = dgv.Rows.Cast<DataGridViewRow>().Where(x => x.Cells.Cast<DataGridViewCell>().Where(y => y.Value == null || string.IsNullOrEmpty(y.Value.ToString())).Count() == x.Cells.Count).ToList();
            for (int i = temp.Count() - 2; i >= 0; i--)
            {
                dgv.Rows.RemoveAt(temp[i].Index);
            }
        }

 

그리고 버튼을 통해 Row를 추가 하는 기능을 만듭니다

 

제일 상단 부분에 DataTable를 하나 선언합니다

 

private DataTable dt;

 

그리고 버튼을 하나 생성하고 버튼 클릭 이벤트에 다음과 같이 작성합니다

 

                        dt = new DataTable();
                        dt = dataGridView1.DataSource as DataTable;
                        string[] row0 = { "", "", "", "", "", "", "", "", "", "", "" };
                        dt.Rows.Add(row0);
                        dataGridView1.DataSource = dt;

 

추가 버튼을 통한 Row가 추가된 화면

버튼을 눌러 붙여넣기 할 만큼의 Row를 미리 생성한뒤에 붙여 넣기 하면 그리드뷰에 데이터가 붙여넣기가 됩니다

 

 

Ctl + V를 통해 붙여넣기 된 화면

 

이상 C#에서 데이터그리드뷰에 Clipboard를 이용한 붙여넣기 구현 방법이였습니다

 

감사합니다~

반응형
반응형

안녕하세요

 

코딩연습생입니다

 

C# Winform에서 엑셀 연동시 데이터를 읽어오거나 쓰기를 할때 Cells를 많이 사용하게 됩니다

 

저번 포스팅에서도 한번 언급했던 적이 있는데 300건 이후로 속도가 현저히 느려집니다

 

그래서 속성 설정으로 속도를 개선하는것을 예전에 포스팅 했던 적이 있는데요

 

https://codingman.tistory.com/101

 

[C#] 엑셀 Cells 사용시 속도 문제 해결하기

안녕하세요. 코딩연습생입니다. 정말 오랜만에 글을 쓰는거 같습니다.. 회사 프로젝트 진행 때문에 시간을 너무 빼앗겨 버리네요ㅎㅎ 근데 저도 이제 블로거가 다 된거 같습니다 하루에도 몇번�

codingman.tistory.com

근데 위의 방법도 근본적인 속도 문제가 해결 되질 않습니다

 

정말 미비한 차이만 발생하더라구요

 

그래서 이번 포스팅에서는 근본적인 속도 문제를 해결 해보려고 합니다

 

일단 기본적인 엑셀 값 쓰기 방식인데요 이전 포스팅을 참조해 주세요

 

https://codingman.tistory.com/100

 

[C#] 엑셀 템플릿파일 불러오기 및 값 넣기

안녕하세요 코딩 연습생입니다 아직도 코로나19로 인해서 기업들 소상인 분들 모두 참 어렵게 지내고 계시는거 같습니다 저 또한 회사원으로 회사가 많이 힘들어 지고 있다고 체감할 정도니깐��

codingman.tistory.com

위의 방식으로 구현을 할 경우 조건이 엑셀의 Row수가 대략 100개 이하일 경우에 사용하세요

 

그 이상 넘어가게 되면 속도가 많이 걸립니다 대략 300~500개의 Row의 값을 쓸 경우 대략 10~15분 가량이 소요

 

됩니다

 

이 문제를 해결하기 위해 고민도 많이 하고 검색도 많이 했는데 뚜렸한 답변이 없더라구요

 

OLEDB를 사용한 방법도 있긴 한데 해당 방법은 쿼리 형식으로 데이터를 읽고/쓰기를 해야 해서

 

특정 Cell 지정이나 이미 생성되어 있는 양식화에 적용하기에는 어렵고 복잡하더라구요

 

그래서 검색 내용한 내용과 기존 방식을 응용해서 구현해 봤습니다

 

일단 소스 코드 부터 보여 드리겠습니다

 

private void hoverGradientButton21_Click(object sender, EventArgs e)
        {
            DataGridView_Change(false);

            string ExcelPath = Environment.CurrentDirectory + @"\Excel\Shipping_sample.xlsx";
            string ExcelEndPath;
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Title = "저장경로 지정하세요";
            saveFileDialog.OverwritePrompt = true;
            saveFileDialog.Filter = "Excel File(*.xlsx)|*.xlsx";

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                ExcelEndPath = saveFileDialog.FileName;

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

                Excel.Application app = new Excel.Application();
                app.DisplayAlerts = false;
                app.Visible = false;
                app.ScreenUpdating = false;
                app.DisplayStatusBar = false;
                app.EnableEvents = false;

                Excel.Workbooks workbooks = app.Workbooks;
                Excel.Workbook workbook = workbooks.Open(ExcelPath, 0, true, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
                Excel.Sheets sheets = workbook.Worksheets;
                Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);
                Excel.Range rng = worksheet.get_Range("A1", "CG3000");


                //Excel.Range totalRange = worksheet.get_Range(worksheet.get_Range("A1"), lastCell);
                object[,] only_data = (object[,])rng.get_Value();
                

                try
                {
                    //엑셀 시트 인덱스 번호는 0,0 부터 시작 하는 것이 아니라 1,1 A1 부터 시작 함. 0,0 으로 시작하면 오류... 
                    //시트에 값 쓰기... 
                    string query = "EXEC SP_SHIP_CUST_PLAN_EXCEL";
                    query += "           'SELECT',";
                    query += "           '" + WORK_FROM_KEY.Text + "',";
                    query += "           ''";
                    SAC_MesDB db = new SAC_MesDB();
                    DataTable dt = db.ExcuteQuery(query);

                    if (dt.Rows.Count > 0)
                    {
                        int row = dt.Rows.Count + 30;
                        int column = rng.Columns.Count;
                        object[,] data = new object[row, column];

                        data = only_data;

                        //해더 복사
                        for (int h = 1; h <= column - 1; h++)
                        {
                            data[1, h] = only_data[1, h];
                            data[2, h] = only_data[2, h];
                            data[3, h] = only_data[3, h];
                            data[4, h] = only_data[4, h];
                            data[5, h] = only_data[5, h];
                            data[6, h] = only_data[6, h];
                        }

                        //고객사 계획 일자 헤더 생성.
                        DateTime Today = Convert.ToDateTime(WORK_FROM_KEY.Text);
                        data[5, 25] = string.Format("{0:M/d}", Today);

                        //총일
                        data[2, 8] = dt.Rows[0]["TOTAL_CNT"].ToString();
                        //지정일수
                        data[2, 9] = dt.Rows[0]["BACK_CNT"].ToString();

                        for (int i = 0; i <= dt.Rows.Count - 1; i++)
                        {
                            data[7 + i, 1] = dt.Rows[i]["Cust_Code"].ToString();
                            data[7 + i, 2] = dt.Rows[i]["VEND_NAME"].ToString();
                            data[7 + i, 3] = dt.Rows[i]["Delivery_Code"].ToString();
                            data[7 + i, 4] = dt.Rows[i]["Delivery_Name"].ToString();
                            data[7 + i, 5] = dt.Rows[i]["Part_No"].ToString();
                            data[7 + i, 6] = dt.Rows[i]["Cust_GridNo"].ToString();
                            data[7 + i, 7] = dt.Rows[i]["Part_Name"].ToString();
                            data[7 + i, 8] = dt.Rows[i]["LAST_FINQTY"].ToString();
                            data[7 + i, 9] = dt.Rows[i]["SELL_FINQTY"].ToString();
                            data[7 + i, 10] = dt.Rows[i]["SELL_RESULT"].ToString();
                            data[7 + i, 14] = dt.Rows[i]["AVG_CNT"].ToString();
                            data[7 + i, 15] = dt.Rows[i]["Min_Storage"].ToString();
                            data[7 + i, 16] = dt.Rows[i]["Max_Storage"].ToString();
                            data[7 + i, 17] = dt.Rows[i]["STORAGE_FINQTY"].ToString();
                            data[7 + i, 18] = dt.Rows[i]["WIP_FINQTY"].ToString();
                            data[7 + i, 20] = dt.Rows[i]["Cust_Plan_Qty"].ToString();
                            data[7 + i, 21] = dt.Rows[i]["Add_Cust_Plan_Qty"].ToString();
                            data[7 + i, 22] = dt.Rows[i]["Ship_LotQty"].ToString();
                            data[7 + i, 23] = dt.Rows[i]["Cust_SafeQty"].ToString();
                            data[7 + i, 25] = dt.Rows[i]["Cust_Qty"].ToString();
                            data[7 + i, 72] = dt.Rows[i]["D0"].ToString();
                            data[7 + i, 73] = dt.Rows[i]["D1"].ToString();
                            data[7 + i, 74] = dt.Rows[i]["D2"].ToString();
                            data[7 + i, 75] = dt.Rows[i]["D3"].ToString();
                            data[7 + i, 76] = dt.Rows[i]["D4"].ToString();
                            data[7 + i, 77] = dt.Rows[i]["D5"].ToString();
                            data[7 + i, 78] = dt.Rows[i]["D6"].ToString();
                            data[7 + i, 79] = dt.Rows[i]["D7"].ToString();
                            data[7 + i, 80] = dt.Rows[i]["D8"].ToString();
                            data[7 + i, 81] = dt.Rows[i]["D9"].ToString();
                            data[7 + i, 82] = dt.Rows[i]["D10"].ToString();
                            data[7 + i, 83] = dt.Rows[i]["D11"].ToString();
                            data[7 + i, 84] = dt.Rows[i]["D12"].ToString();
                            data[7 + i, 85] = dt.Rows[i]["D13"].ToString();
                        }

                        rng.Value = data;
                    }

                    SubForms.SplashWnd.SplashClose(this);

                    if (File.Exists(ExcelEndPath)) File.Delete(ExcelEndPath);
                    workbook.SaveAs(ExcelEndPath);

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

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                    workbook.Close(true, null, null);
                    app.Quit();

                    ReleaseExcelObject(app);
                    ReleaseExcelObject(workbook);
                    ReleaseExcelObject(workbooks);
                    ReleaseExcelObject(worksheet);
                    ReleaseExcelObject(sheets);
                }
                finally
                {
                    workbook.Close(true, null, null);
                    app.Quit();

                    ReleaseExcelObject(app);
                    ReleaseExcelObject(workbook);
                    ReleaseExcelObject(workbooks);
                    ReleaseExcelObject(worksheet);
                    ReleaseExcelObject(sheets);
                }
            }
        }
        
private void ReleaseExcelObject(object obj)
        {
            try
            {
                if (obj != null)
                {
                    Marshal.ReleaseComObject(obj);
                    obj = null;
                }
            }
            catch (Exception ex)
            {
                obj = null;
                throw ex;
            }
            finally
            {
                GC.GetTotalMemory(false);
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                GC.GetTotalMemory(true);
            }
        }

 

전체 소스입니다

 

해당 문제 해결 방법은 object[,] 였습니다

 

템플릿화 된 문서를 불러와서 object[,] 배열에 담아주고 object[,] 배열의 값을

 

수정하여 완성된 데이터를 배열에서 만들어 준뒤에 range 함수의 Value를 통해 BulkInsert를 실행해 주는것입니다

 

구문별로 설명을 다시 한면

 

 

위의 소스 코드 영역은 엑셀 파일(템플릿)을 불러와 첫번째 시트 중 A1부터 CG3000까지를 영역 설정을 지정하는

 

구문 입니다

 

선택된 영역의 내용을 only_data라는 object[,] 배열에 담아 줍니다

 

 

only_data의 object[,]배열을 동일한 object 배열인 data의 배열로 복사 합니다

 

이유는 onlu_data를 직접수정하게되면  기존 속도하고 큰 차이가 없어지게 됩니다

 

예를들면 100 = 100으로 복사한 뒤에 10, 50, 60의 수치를 변경한다고 헀을 경우 

 

결국 100을 모두 조회 해야 하기 때문입니다

 

하지만 위의 처럼 오브젝트를 두개를 선언해서 1개의 오브젝는 100

 

나머지 한개의 오브젝트에는 필요한 수지만 10, 50, 60만 지정합니다

 

그럼 데이터를 쓰기 할 경우 10, 50, 60만 조회 하면 되기 때문에 속도가 빨라지겠죠

 

 

data 오브젝트 배열에 필요한 값 만큼만 지정하여 입력해 줍니다

 

직접 Cells를 사용하여 엑셀 문서에 데이터를 조합하는것이 아닌 object[,]의 내용을 조합하는 것 입니다

 

 

다음 조합된 오브젝트 배열을 엑셀 함수인 Range.Value를 사용하여 오브젝트를 바로 넣어주게 되면

 

끝이됩니다

 

속도 차이는 

* 500개 Row 기준

   1) Cells 함수 사용시 10~15분 소요

   2) 1개의 오브젝트 사용시 5~10분 소요

   3) 2개의 오브젝트 사용시 1분 미만

 

이런 결과가 테스트 되었습니다

 

혹시 저 처럼 속도 문제로 머리 썪고 계시다면 한번 적용 해보시기 바랍니다

 

반응형
반응형

안녕하세요

 

코딩 연습생 입니다

 

이전 포스팅에서 직접 엑셀의 API를 활용하여 엑셀파일의 내용을 불러와 데이터그리드뷰와 연동하는 포스팅을

 

업로드했었는데요

 

https://codingman.tistory.com/100

 

[C#] 엑셀 템플릿파일 불러오기 및 값 넣기

안녕하세요 코딩 연습생입니다 아직도 코로나19로 인해서 기업들 소상인 분들 모두 참 어렵게 지내고 계시는거 같습니다 저 또한 회사원으로 회사가 많이 힘들어 지고 있다고 체감할 정도니깐요 그래도 국가에서..

codingman.tistory.com

 

이전 포스팅 방식으로 직접 구현을 해보신 분이나 관련 내용을 아시는분들이라면 아실것 같은데요

 

위의 방법으로 구현했을시(직접 엑셀을 임포트하는 방식) 제일 큰 단점은 대량을 엑셀 파일을 읽을때

 

엄청 느리다는것 입니다

 

그것을 조금이나마 해결하고자 엑셀의 문서 속성을 변경하여 이용하는데

 

https://codingman.tistory.com/101

 

[C#] 엑셀 Cells 사용시 속도 문제 해결하기

안녕하세요. 코딩연습생입니다. 정말 오랜만에 글을 쓰는거 같습니다.. 회사 프로젝트 진행 때문에 시간을 너무 빼앗겨 버리네요ㅎㅎ 근데 저도 이제 블로거가 다 된거 같습니다 하루에도 몇번씩 포스팅 걱정을 하..

codingman.tistory.com

 

이것도 전에 포스팅했었네요ㅎㅎ 근데 이 방법도 미미한 차이일뿐 사용자는 크게 느끼지 못합니다

 

그래서 이번에 포스팅할 내용은 직접 엑셀을 임포는하는 방식이 아닌 OleDbConnection을 이용한 엑셀 연동 방식으로

 

구현을 해볼려고 합니다

 

아마 이미 정보가 많이 있어서 한번쯤 구현을 해보시지 않았을까 싶은데요

 

하나씩 쉽게 이해할수 있도록 순서대로 포스팅해보도록 할께요

 

첫번재는 해당 From에서 OleDb를 사용하기 위한 선언부 입니다

 

아마 닷넷 4버전 이상을 사용하시는분이라면 기본적으로 포함되어 있을건데

 

혹시 프로젝트의 참조부분에 System.Data가 있는지 확인한 후에 없으시면 참조추가를 사용해서 추가해 줍니다

 

 

그리고 From의 제일 상단위에 다음과 같이 선언을 합니다

 

using System.Data.OleDb;

 

사실 윈도우에서 기본 사용이 가능해야 하는데 오류가 발생을 많이 합니다

 

그래서 사전 배포를 용이하게 하기 위해서 몇가지 설치 파일을 설치 해야합니다

 

해당 패키지를 설치 하지 않고 배포 하시면

 

"Microsoft.ACE.OLEDB.12.0 공급자는 로컬 컴퓨터에 등록할 수 없습니다.(System.Data)"

위와 같은 오류를 경험하게 되십니다ㅎㅎ

 

저와 같은 경험을 하지 않게 해드리기 위해서 미리 패키지 설치까지 합니다

 

http://www.microsoft.com/ko-kr/download/details.aspx?id=13255

 

Microsoft Access Database Engine 2010 재배포 가능 패키지

이 다운로드를 실행하면 2010 Microsoft Office System 파일과 Microsoft Office가 아닌 다른 응용 프로그램 사이에서 데이터를 쉽게 전송할 수 있는 구성 요소 집합이 설치됩니다.

www.microsoft.com

위의 주소로 접속해서 32비트 & 64비트 설치 파일을 모두 설치 해주세요

 

그다음 비쥬얼스튜디오로 돌아와서 From에 버튼을 하나 생성해 줍니다

 

저는 기존 소스를 이용하겠습니다

 

 

이렇게 버튼을 하나 준비해 주시구요

 

버튼의 Click 이벤트를 만들어 주세요

 

만드신 이벤트안에 아래의 소스코드를 만들어 줍니다

 

private void hoverGradientButton10_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Title = "엑셀 파일 선택하세요";
            openFileDialog.Filter = "Excel Files|*.xls;*.xlsx;*.xlsm";
            DialogResult result = openFileDialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                SubForms.SplashWnd.SplashShow();

                this.Cursor = Cursors.WaitCursor;
                //엑셀 앱
                Excel.Application app = new Excel.Application();
                app.DisplayAlerts = false;
                app.Visible = false;
                app.ScreenUpdating = false;
                app.DisplayStatusBar = false;
                app.EnableEvents = false;

                Excel.Workbooks workbooks = app.Workbooks;

                //엑셀 워크북(파일경로읽어서)
                //Excel.Workbook workbook = workbooks.Open(openFileDialog.FileName);
                Excel.Workbook workbook = workbooks.Open(openFileDialog.FileName, 0, true, 5, "", "", true, Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
                // 엑셀 워크싯 객체
                Excel.Sheets sheets = workbook.Worksheets;
                Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);

                // 엑셀파일 이름이 나온다
                string excelFileName = workbook.Name;
                //excelFileName = excelFileName.Substring(book.Name.Length-3);
                string[] str = excelFileName.Split('.');
                // 워크시트 첫번째 이름
                string workSheetName = worksheet.Name;

                try
                {
                    if (str[1].Equals("xls"))
                    {
                        // 연결 string
                        string constr = "provider=Microsoft.Jet.OLEDB.4.0;Data Source='"
                      + openFileDialog.FileName + "';Extended Properties=Excel 8.0;";

                        //경로까지 다 포함해서 .xls라고 뜨네;
                        //MessageBox.Show(openFileDialog.FileName);

                        // excel conn
                        OleDbConnection conn = new OleDbConnection(constr);

                        // excel cmd
                        OleDbCommand cmd = new OleDbCommand("SELECT * FROM [" + workSheetName + "$]", conn);
                        conn.Open();

                        OleDbDataAdapter sda = new OleDbDataAdapter(cmd);
                        DataTable dt = new DataTable();
                        sda.Fill(dt);
                        this.Cursor = Cursors.WaitCursor;
                        dataGridView1.DataSource = dt;
                        this.Cursor = Cursors.Default;
                        conn.Close();


                    }
                    else if (str[1].Equals("xlsx"))
                    {
                        // 연결 string
                        String constr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" +
                        openFileDialog.FileName +
                        ";Extended Properties='Excel 12.0 XML;HDR=No;IMEX=1';";

                        //경로까지 다 포함해서 .xls라고 뜨네;
                        //MessageBox.Show(openFileDialog.FileName);

                        // excel conn
                        OleDbConnection conn = new OleDbConnection(constr);

                        // excel cmd
                        OleDbCommand cmd = new OleDbCommand("SELECT * FROM [" + workSheetName + "$]", conn);
                        conn.Open();

                        OleDbDataAdapter sda = new OleDbDataAdapter(cmd);
                        DataTable dt = new DataTable();
                        sda.Fill(dt);

                        int j = 0;
                        //dataGridView1.DataSource = dt;
                        for(int i=6; i<=dt.Rows.Count-1; i++)
                        {
                            
                            dataGridView2.Rows.Add(1);
                            dataGridView2.Rows[j].Cells[0].Value = dt.Rows[i][0].ToString();
                            dataGridView2.Rows[j].Cells[1].Value = dt.Rows[i][2].ToString();
                            dataGridView2.Rows[j].Cells[2].Value = dt.Rows[i][4].ToString();
                            dataGridView2.Rows[j].Cells[3].Value = dt.Rows[i][5].ToString();
                            dataGridView2.Rows[j].Cells[4].Value = dt.Rows[i][6].ToString();
                            dataGridView2.Rows[j].Cells[5].Value = dt.Rows[i][7].ToString();
                            dataGridView2.Rows[j].Cells[6].Value = dt.Rows[i][8].ToString();
                            dataGridView2.Rows[j].Cells[7].Value = dt.Rows[i][9].ToString();
                            dataGridView2.Rows[j].Cells[8].Value = dt.Rows[i][10].ToString();
                            dataGridView2.Rows[j].Cells[9].Value = dt.Rows[i][11].ToString();
                            dataGridView2.Rows[j].Cells[10].Value = dt.Rows[i][12].ToString();
                            dataGridView2.Rows[j].Cells[11].Value = dt.Rows[i][13].ToString();
                            dataGridView2.Rows[j].Cells[12].Value = dt.Rows[i][14].ToString();
                            dataGridView2.Rows[j].Cells[13].Value = dt.Rows[i][15].ToString();
                            dataGridView2.Rows[j].Cells[14].Value = dt.Rows[i][16].ToString();
                            dataGridView2.Rows[j].Cells[15].Value = dt.Rows[i][17].ToString();
                            dataGridView2.Rows[j].Cells[16].Value = dt.Rows[i][18].ToString();
                            dataGridView2.Rows[j].Cells[17].Value = dt.Rows[i][19].ToString();
                            dataGridView2.Rows[j].Cells[18].Value = dt.Rows[i][20].ToString();
                            dataGridView2.Rows[j].Cells[19].Value = dt.Rows[i][21].ToString();
                            dataGridView2.Rows[j].Cells[20].Value = dt.Rows[i][22].ToString();
                            dataGridView2.Rows[j].Cells[21].Value = dt.Rows[i][23].ToString();
                            dataGridView2.Rows[j].Cells[22].Value = dt.Rows[i][24].ToString();
                            dataGridView2.Rows[j].Cells[23].Value = dt.Rows[i][25].ToString();
                            dataGridView2.Rows[j].Cells[24].Value = dt.Rows[i][26].ToString();
                            dataGridView2.Rows[j].Cells[25].Value = dt.Rows[i][27].ToString();
                            dataGridView2.Rows[j].Cells[26].Value = dt.Rows[i][28].ToString();
                            dataGridView2.Rows[j].Cells[27].Value = dt.Rows[i][29].ToString();
                            dataGridView2.Rows[j].Cells[28].Value = dt.Rows[i][30].ToString();
                            dataGridView2.Rows[j].Cells[29].Value = dt.Rows[i][31].ToString();
                            dataGridView2.Rows[j].Cells[30].Value = dt.Rows[i][32].ToString();
                            dataGridView2.Rows[j].Cells[31].Value = dt.Rows[i][33].ToString();
                            dataGridView2.Rows[j].Cells[32].Value = dt.Rows[i][34].ToString();
                            dataGridView2.Rows[j].Cells[33].Value = dt.Rows[i][35].ToString();
                            dataGridView2.Rows[j].Cells[34].Value = dt.Rows[i][36].ToString();
                            dataGridView2.Rows[j].Cells[35].Value = dt.Rows[i][37].ToString();
                            dataGridView2.Rows[j].Cells[36].Value = dt.Rows[i][38].ToString();
                            dataGridView2.Rows[j].Cells[37].Value = dt.Rows[i][39].ToString();
                            dataGridView2.Rows[j].Cells[38].Value = dt.Rows[i][40].ToString();
                            dataGridView2.Rows[j].Cells[39].Value = dt.Rows[i][41].ToString();
                            dataGridView2.Rows[j].Cells[40].Value = dt.Rows[i][42].ToString();
                            dataGridView2.Rows[j].Cells[41].Value = dt.Rows[i][43].ToString();
                            dataGridView2.Rows[j].Cells[42].Value = dt.Rows[i][44].ToString();
                            dataGridView2.Rows[j].Cells[43].Value = dt.Rows[i][45].ToString();
                            dataGridView2.Rows[j].Cells[44].Value = dt.Rows[i][46].ToString();
                            dataGridView2.Rows[j].Cells[45].Value = dt.Rows[i][47].ToString();
                            dataGridView2.Rows[j].Cells[46].Value = dt.Rows[i][48].ToString();
                            dataGridView2.Rows[j].Cells[47].Value = dt.Rows[i][49].ToString();
                            dataGridView2.Rows[j].Cells[48].Value = dt.Rows[i][50].ToString();
                            dataGridView2.Rows[j].Cells[49].Value = dt.Rows[i][51].ToString();
                            dataGridView2.Rows[j].Cells[50].Value = dt.Rows[i][52].ToString();
                            dataGridView2.Rows[j].Cells[51].Value = dt.Rows[i][53].ToString();
                            dataGridView2.Rows[j].Cells[52].Value = dt.Rows[i][54].ToString();
                            dataGridView2.Rows[j].Cells[53].Value = dt.Rows[i][55].ToString();
                            dataGridView2.Rows[j].Cells[54].Value = dt.Rows[i][56].ToString();
                            dataGridView2.Rows[j].Cells[55].Value = dt.Rows[i][57].ToString();
                            dataGridView2.Rows[j].Cells[56].Value = dt.Rows[i][58].ToString();
                            dataGridView2.Rows[j].Cells[57].Value = dt.Rows[i][59].ToString();
                            dataGridView2.Rows[j].Cells[58].Value = dt.Rows[i][60].ToString();
                            dataGridView2.Rows[j].Cells[59].Value = dt.Rows[i][61].ToString();
                            dataGridView2.Rows[j].Cells[60].Value = dt.Rows[i][62].ToString();
                            dataGridView2.Rows[j].Cells[61].Value = dt.Rows[i][63].ToString();
                            dataGridView2.Rows[j].Cells[62].Value = dt.Rows[i][64].ToString();
                            dataGridView2.Rows[j].Cells[63].Value = dt.Rows[i][65].ToString();
                            dataGridView2.Rows[j].Cells[64].Value = dt.Rows[i][66].ToString();
                            dataGridView2.Rows[j].Cells[65].Value = dt.Rows[i][67].ToString();
                            dataGridView2.Rows[j].Cells[66].Value = dt.Rows[i][68].ToString();
                            dataGridView2.Rows[j].Cells[67].Value = dt.Rows[i][69].ToString();
                            dataGridView2.Rows[j].Cells[68].Value = dt.Rows[i][70].ToString();
                            dataGridView2.Rows[j].Cells[69].Value = dt.Rows[i][71].ToString();
                            dataGridView2.Rows[j].Cells[70].Value = dt.Rows[i][72].ToString();
                            dataGridView2.Rows[j].Cells[71].Value = dt.Rows[i][73].ToString();
                            dataGridView2.Rows[j].Cells[72].Value = dt.Rows[i][74].ToString();
                            dataGridView2.Rows[j].Cells[73].Value = dt.Rows[i][75].ToString();
                            dataGridView2.Rows[j].Cells[74].Value = dt.Rows[i][76].ToString();
                            dataGridView2.Rows[j].Cells[75].Value = dt.Rows[i][77].ToString();
                            dataGridView2.Rows[j].Cells[76].Value = dt.Rows[i][78].ToString();
                            dataGridView2.Rows[j].Cells[77].Value = dt.Rows[i][79].ToString();
                            dataGridView2.Rows[j].Cells[78].Value = dt.Rows[i][80].ToString();
                            dataGridView2.Rows[j].Cells[79].Value = dt.Rows[i][81].ToString();
                            dataGridView2.Rows[j].Cells[80].Value = dt.Rows[i][82].ToString();
                            dataGridView2.Rows[j].Cells[81].Value = dt.Rows[i][83].ToString();
                            
                            j++;
                        }

                        this.Cursor = Cursors.Default;
                        conn.Close();

                        Finish_Data_Insert();
                        SubForms.SplashWnd.SplashClose(this);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                    workbook.Close(true, null, null);
                    app.Quit();

                    ReleaseExcelObject(worksheet);
                    ReleaseExcelObject(sheets);
                    ReleaseExcelObject(workbook);
                    ReleaseExcelObject(workbooks);
                    ReleaseExcelObject(app);
                }
                finally
                {
                    workbook.Close(true, null, null);
                    app.Quit();

                    ReleaseExcelObject(worksheet);
                    ReleaseExcelObject(sheets);
                    ReleaseExcelObject(workbook);
                    ReleaseExcelObject(workbooks);
                    ReleaseExcelObject(app);
                }
            }
        }

 

오브젝트 초기화를 위해 아래 함수로 하나 작성해 줍니다

 

private void ReleaseExcelObject(object obj)
        {
            try
            {
                if (obj != null)
                {
                    Marshal.ReleaseComObject(obj);
                    obj = null;
                }
            }
            catch (Exception ex)
            {
                obj = null;
                throw ex;
            }
            finally
            {
                GC.Collect();
            }
        }

 

이렇게 해주시고 생성하신 버튼을 누구게 되면 OpenFileDialog가 열리고 거기서 엑셀 파일을 선택해 주시면

 

해당 엑셀의 문서 내용이 데이터그리드뷰에 옴겨지게 됩니다

 

위의 소스 내용중 "SubForms.SplashWnd.SplashShow();" 구문은 주석 또는 삭제 하셔도 무관합니다

 

해당 코드는 사용자에게 진행중을 표시하기 위한 ProgressBar를 구현한것인데 없어도 구동하는데 전혀 문제가 없습니다

 

해당 내용은 다른 포스팅에서 정리해서 올리도록 할께요

 

이전 방식과 현재의 방식을 모두 구현을 하셨다면 속도 비교를 해보시면 깜짝 놀라실수 있을겁니다

 

동일 엑셀 문서기준 (대략 Row가 1000개)의 파일을 불러오기 하였을때

 

기존방식 : 10~15분 소요

현재방식 : 30초 미만

 

엄청난 차이를 보여주더라구요

 

엑셀과 연동을 구현하기 위해 고민중이시라면 위의 방법을 통해 구현해 보시길 바랍니다

반응형
반응형

안녕하세요

 

코딩 연습생입니다

 

아직도 코로나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