本人近期接到一個任務,要求開發一個可以操作和讀取Excel文件的軟件。
花了幾天時間開發完成。本機跑的可以,兼容性還待檢測。
解決方案:
dsoframer.ocx + Microsoft.Office.Interop.Excel
dsoframer.ocx 是用來可視化操作office文件的C#控件,Microsoft.Office.Interop.Excel 是用來操作和讀取的控件。
效果圖如下:
這個是可視化操作,導入excel文件,可以刪除行,當然所有的excel操作都可以,已測試wps和Microsoft office 軟件都可以正常讀取和操作。導入數據是將excel數據讀取到DataTable 中。
第一行和第一列是通過代碼添加的,具體實現方式將在下面貼出來。
DataTable 讀取到數據展示如下:
代碼部分:
using System; using System.Data; using System.Diagnostics; using System.IO; using System.Reflection; using System.Windows.Forms; using Microsoft.Office.Interop.Excel; using System.Data.OleDb; using System.Collections.Generic; using System.Text.RegularExpressions; namespace ReadExcel { public partial class Form1 : Form { private AxDSOFramer.AxFramerControl m_axFramerControl = new AxDSOFramer.AxFramerControl(); System.Data.DataTable dtEnd = new System.Data.DataTable(); bool isOpenExcel = false; public Form1() { InitializeComponent(); } #region 按鈕事件 private void button1_Click(object sender, EventArgs e) { //讀取excel OpenFileDialog dia = new OpenFileDialog(); dia.Filter = "公路計價文件(*.xls)|*.xls;*.xlsx"; dia.FilterIndex = 0; dia.ShowDialog(); if (string.IsNullOrEmpty(dia.FileName)) { return; } if (isOpenExcel == true)//先關閉已打開的excel { try { CloseFrom(); } catch (Exception) { } } //復制到本地 因為快捷方式可以用,修改后可以保存,會導致修改源文件 string tempFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TempFile"); if (!System.IO.Directory.Exists(tempFile)) System.IO.Directory.CreateDirectory(tempFile); string tempExcelPath = Path.Combine(tempFile, "tempExcel" + Path.GetExtension(dia.FileName)); System.IO.File.Copy(dia.FileName, tempExcelPath, true); try { //打開本地excel Init(tempExcelPath); isOpenExcel = true; try { //所有的sheet都應該增加 object documentExcel = m_axFramerControl.ActiveDocument; //// 獲取當前工作薄 Workbook workbook = (Microsoft.Office.Interop.Excel.Workbook)documentExcel; foreach (Worksheet worksheet in workbook.Sheets) { if (worksheet != null) { int columnsint = worksheet.UsedRange.Cells.Columns.Count;//得到列數 int rowsint = worksheet.UsedRange.Rows.Count; //得到行數 if (rowsint == 1) continue; #region 增加行 Range range = (Range)worksheet.Rows[1, Type.Missing]; //object Range.Insert(object shift, object copyorigin); //shift: Variant類型,可選。指定單元格的調整方式。可以為下列 XlInsertShiftDirection 常量之一: //xlShiftToRight 或 xlShiftDown。如果省略該參數,Microsoft Excel 將根據區域形狀確定調整方式。 range.Insert(XlInsertShiftDirection.xlShiftDown, Type.Missing); System.Data.DataTable dt = new System.Data.DataTable(); dt.Columns.Add("Name"); DataRow dr = dt.NewRow(); dr[0] = "姓名"; dt.Rows.Add(dr); DataRow dr1 = dt.NewRow(); dr1[0] = "性別"; dt.Rows.Add(dr1); DataRow dr2 = dt.NewRow(); dr2[0] = "年齡"; dt.Rows.Add(dr2); DataRow dr3 = dt.NewRow(); dr3[0] = "地址"; dt.Rows.Add(dr3); range = worksheet.get_Range("A1", ToName(columnsint - 1) + "1"); range.RowHeight = 30; range.Font.Bold = true; string strName = GetNameFromDt(dt); //綁定下拉列表 range.Validation.Add(XlDVType.xlValidateList, XlDVAlertStyle.xlValidAlertStop, XlFormatConditionOperator.xlBetween, strName, Type.Missing);//Name就是上面獲取的列表 // range.Validation.Modify(XlDVType.xlValidateList, XlDVAlertStyle.xlValidAlertStop, Type.Missing, strName, Type.Missing);//單元格已設置數據有效性,只能用代碼修改有效性;如果單元格未設置有效性,需要使用 Add 方法 // 填充值 worksheet.Cells[1, 1] = "姓名"; worksheet.Cells[1, 2] = "性別"; worksheet.Cells[1, 3] = "年齡"; worksheet.Cells[1, 4] = "地址"; #endregion #region 增加列 Range xlsColumns = (Range)worksheet.Columns[1, Type.Missing]; xlsColumns.Insert(XlInsertShiftDirection.xlShiftToRight, Type.Missing); System.Data.DataTable dtColumn = new System.Data.DataTable(); dtColumn.Columns.Add("Name"); DataRow drcol = dtColumn.NewRow(); drcol[0] = "有效行"; dtColumn.Rows.Add(drcol); DataRow drcol1 = dtColumn.NewRow(); drcol1[0] = "無效行"; dtColumn.Rows.Add(drcol1); strName = GetNameFromDt(dtColumn); //綁定下拉列表 //xlsColumns = (Range)worksheet.Columns[1, Type.Missing]; xlsColumns = worksheet.get_Range("A2", "A"+ (rowsint+1).ToString()); xlsColumns.Validation.Add(XlDVType.xlValidateList, XlDVAlertStyle.xlValidAlertStop, XlFormatConditionOperator.xlBetween, strName, Type.Missing);//Name就是上面獲取的列表 xlsColumns.ColumnWidth = 20; // 填充值 xlsColumns.Value = "有效行"; #endregion Range m_objRange = worksheet.get_Range("A1", Type.Missing); m_objRange.Borders[XlBordersIndex.xlDiagonalDown].ColorIndex = XlColorIndex.xlColorIndexAutomatic; m_objRange.Value = " 識別行\r\n 設置列"; } } } catch (Exception ex) { throw; } } catch (Exception ex) { MessageBox.Show("打開失敗!" + ex.ToString()); } } /// <summary> /// 讀取쫽뻝 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { if (!isOpenExcel) { MessageBox.Show("未導入Excel文件!"); return; } object documentExcel = m_axFramerControl.ActiveDocument; #region 保存本地讀取方法 存在的問題是多個sheet 不知道讀取哪個,下面的方法可以讀取當前活動頁 ////另存為excel //string filePath = "F:\\111.xls"; //m_axFramerControl.SaveAs(filePath, true);//另存為xls ////讀取excel 到datatable //System.Data.DataTable dt = GetDataFromExcel(filePath); #endregion try { //先保存本地,不然在修改中的單元格依然讀取的是原數據 m_axFramerControl.SaveAs(m_axFramerControl.DocumentFullName, true);//另存為xls //// 獲取當前工作薄 Workbook workbook = (Microsoft.Office.Interop.Excel.Workbook)documentExcel; Worksheet worksheet = (Microsoft.Office.Interop.Excel.Worksheet)workbook.ActiveSheet; if (worksheet != null) { // 取得總記錄行數(包括標題列) int rowsint = worksheet.UsedRange.Rows.Count; //得到行數 int columnsint = worksheet.UsedRange.Cells.Columns.Count;//得到列數 //取得數據范圍區域 (標題列) Range rng1 = worksheet.Cells.get_Range("A1", ToName(columnsint - 1) + "1"); //item object[,] arryItem = (object[,])rng1.Value2; string[] tmp = new string[columnsint]; for (int i = 0; i < columnsint; i++) { if (arryItem[1, i + 1] == null) continue; tmp[i] = arryItem[1, i + 1].ToString(); } Range mRange = worksheet.get_Range("A2", ToName(columnsint - 1) + rowsint); object[,] mArray = (object[,])mRange.Formula; string[,] myStrArr = new string[rowsint - 1, columnsint]; for (int i = 0; i < rowsint - 1; i++) { for (int j = 0; j < columnsint; j++) { myStrArr[i, j] = mArray[i + 1, j + 1].ToString(); } } dtEnd = ConvertToDataTable(tmp, myStrArr); MessageBox.Show("讀取成功!"); } } catch (Exception ex) { MessageBox.Show("讀取失敗!" + ex.ToString()); } } private void btn_deleteRow_Click(object sender, EventArgs e) { if (!isOpenExcel) { MessageBox.Show("未導入Excel文件!"); return; } //先保存本地,不然在修改中的單元格依然讀取的是原數據 m_axFramerControl.SaveAs(m_axFramerControl.DocumentFullName, true);//另存為xls object documentExcel = m_axFramerControl.ActiveDocument; Range excelRange = GetSelectionCell(documentExcel); if (excelRange == null) { MessageBox.Show("未選擇單元格或行!"); return; } excelRange.Select(); excelRange.EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp); } /// <summary> /// 顯示數據 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button3_Click(object sender, EventArgs e) { Form2 frm = new Form2(this.dtEnd); frm.ShowDialog(); } #endregion #region AxFramerControl Excel 的加載 //總的加載Excel方法 private void Init(string _ExcelFilePath) { try { RegControl(); if (!File.Exists(_ExcelFilePath)) { MessageBox.Show("文件不存在或未標識的文件格式!", "提示信息"); return; //throw new ApplicationException("文件不存在或未標識的文件格式!"); } AddAxFramerControl();//加載填充控件 m_axFramerControl.Titlebar = false;//是否顯示excel標題欄 m_axFramerControl.Menubar = false;//是否顯示excel的菜單欄 m_axFramerControl.Toolbars = false;//是否顯示excel的工具欄 InitOfficeControl(_ExcelFilePath); } catch (Exception ex) { throw ex; } } //第二步:向panel填充AxFramerControl控件 private void AddAxFramerControl() { try { this.panel1.Controls.Add(m_axFramerControl); m_axFramerControl.Dock = DockStyle.Fill; } catch (Exception ex) { MessageBox.Show(ex.Message); throw; } } //第一步:注冊AxFramerControl public bool RegControl() { try { Assembly thisExe = Assembly.GetExecutingAssembly(); System.IO.Stream myS = thisExe.GetManifestResourceStream("NameSpaceName.dsoframer.ocx"); string sPath = System.AppDomain.CurrentDomain.BaseDirectory + @"\dsoframer.ocx"; ProcessStartInfo psi = new ProcessStartInfo("regsvr32", "/s " + sPath); Process.Start(psi); } catch (Exception ex) { MessageBox.Show(ex.Message); } return true; } //下面這個方法是dso打開文件時需要的一個參數,代表office文件類型 /// <summary> /// 根據后綴名得到打開方式 /// </summary> /// <param name="_sExten"></param> /// <returns></returns> private string LoadOpenFileType(string _sExten) { try { string sOpenType = ""; switch (_sExten.ToLower()) { case "xls": case "xlsx": sOpenType = "Excel.Sheet"; break; case "doc": case "docx": sOpenType = "Word.Document"; break; case "ppt": sOpenType = "PowerPoint.Show"; break; case "vsd": case "vsdx": sOpenType = "Visio.Drawing"; break; default: sOpenType = "Word.Document"; break; } return sOpenType; } catch (Exception ex) { throw ex; } } /// <summary> /// 第三步:初始化office控件,加載Excel /// </summary> /// <param name="_sFilePath">本地文檔路徑</param> private void InitOfficeControl(string _sFilePath) { try { if (m_axFramerControl == null) { throw new ApplicationException("請先初始化office控件對象!"); } //this.m_axFramerControl.SetMenuDisplay(48); //這個方法很特別,一個組合菜單控制方法,我還沒有找到參數的規律,有興趣的朋友可以研究一下 string sExt = System.IO.Path.GetExtension(_sFilePath).Replace(".", ""); //this.m_axFramerControl.CreateNew(this.LoadOpenFileType(sExt));//創建新的文件 this.m_axFramerControl.Open(_sFilePath, false, this.LoadOpenFileType(sExt), "", "");//打開文件 //隱藏標題 this.m_axFramerControl.Titlebar = false; } catch (Exception ex) { throw ex; } } /// <summary> /// 關閉當前界面 /// </summary> public void CloseFrom() { try { if (this.m_axFramerControl != null) { this.m_axFramerControl.Close(); } } catch (Exception ex) { throw ex; } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { try { CloseFrom(); } catch (Exception) { } } #endregion #region 方法 // 獲得當前窗體 Window GetActiveWindow(object Document) { if (Document == null) { return null; } Workbook workbook = null; Worksheet worksheet = null; try { //// 獲取當前工作薄 workbook = (Microsoft.Office.Interop.Excel.Workbook)Document; //// 獲取當前工作頁 worksheet = (Microsoft.Office.Interop.Excel.Worksheet)workbook.ActiveSheet; Window window = null; if (worksheet != null) { //// 獲取當前活動窗口 window = worksheet.Application.ActiveWindow; } return window; } catch { return null; } } Range GetSelectionCell(object Document) { if (Document == null) { return null; } Workbook workbook = null; Worksheet worksheet = null; try { workbook = (Microsoft.Office.Interop.Excel.Workbook)Document; worksheet = (Microsoft.Office.Interop.Excel.Worksheet)workbook.ActiveSheet; Range range = null; if (worksheet != null) { //// 獲取所選區域的第一次選中的單元格 range = worksheet.Application.ActiveCell; } return range; } catch { return null; } } public System.Data.DataTable GetDataFromExcel(string filePath) { string connStr = ""; string fileType = System.IO.Path.GetExtension(filePath); if (string.IsNullOrEmpty(fileType)) return null; if (fileType == ".xls") connStr = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + filePath + ";" + ";Extended Properties=\"Excel 8.0;HDR=NO;IMEX=1\""; else connStr = "Provider=Microsoft.ACE.OLEDB.12.0;" + "Data Source=" + filePath + ";" + ";Extended Properties=\"Excel 12.0;HDR=NO;IMEX=1\""; string sql_F = "Select * FROM [{0}] "; OleDbConnection conn = null; OleDbDataAdapter da = null; System.Data.DataTable dataTable = new System.Data.DataTable(); try { // 初始化連接,並打開 conn = new OleDbConnection(connStr); conn.Open(); da = new OleDbDataAdapter(); da.SelectCommand = new OleDbCommand(String.Format(sql_F, "Sheet1$"), conn); da.Fill(dataTable); } catch (Exception ex) { } finally { // 關閉連接 if (conn.State == ConnectionState.Open) { conn.Close(); da.Dispose(); conn.Dispose(); } } conn.Close(); da.Dispose(); conn.Dispose(); return dataTable; } #region - 由數字轉換為Excel中的列字母 - public int ToIndex(string columnName) { if (!Regex.IsMatch(columnName.ToUpper(), @"[A-Z]+")) { throw new Exception("invalid parameter"); } int index = 0; char[] chars = columnName.ToUpper().ToCharArray(); for (int i = 0; i < chars.Length; i++) { index += ((int)chars[i] - (int)'A' + 1) * (int)Math.Pow(26, chars.Length - i - 1); } return index - 1; } public string ToName(int index) { if (index < 0) { throw new Exception("invalid parameter"); } List<string> chars = new List<string>(); do { if (chars.Count > 0) index--; chars.Insert(0, ((char)(index % 26 + (int)'A')).ToString()); index = (int)((index - index % 26) / 26); } while (index > 0); return String.Join(string.Empty, chars.ToArray()); } #endregion /// <summary> /// 反一個M行N列的二維數組轉換為DataTable /// </summary> /// <param name="ColumnNames">一維數組,代表列名,不能有重復值</param> /// <param name="Arrays">M行N列的二維數組</param> /// <returns>返回DataTable</returns> /// <remarks>柳永法 http://www.yongfa365.com/ </remarks> public static System.Data.DataTable ConvertToDataTable(string[] ColumnNames, string[,] Arrays) { System.Data.DataTable dt = new System.Data.DataTable(); foreach (string ColumnName in ColumnNames) { dt.Columns.Add(ColumnName, typeof(string)); } for (int i1 = 0; i1 < Arrays.GetLength(0); i1++) { DataRow dr = dt.NewRow(); bool isData = false; for (int i = 0; i < ColumnNames.Length; i++) { if (Arrays[i1, i] != null) { isData = true; dr[i] = Arrays[i1, i].ToString(); } } if (isData) dt.Rows.Add(dr); } return dt; } private string GetNameFromDt(System.Data.DataTable dt) { string str = ""; foreach (DataRow dr in dt.Rows) { str += dr["Name"].ToString() + ","; } return str.TrimEnd(','); } #endregion } }