本人近期接到一个任务,要求开发一个可以操作和读取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 = " 识别行 设置列"; } } } 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 } }