最近在学习winform,国庆前被布置了一个小任务,好不容易大致做出来了,决定记录下来,以此加深印象。
先说下需求:这是一个导入话单标记后并导出的功能
1. 选择excel文件
2. 定义字段
日期 时间 对方号码 通话时长 呼叫类型
3. 点击datagridview 标题 出现下拉菜单 显示定义的字段
4. 标记定义字段列
5. 保存定义字段数据 到 datatable
6 导出datatable
按照需求一步一步来,先设计界面,需要一个DataGridView和两个Button,一个导入,一个导出,我加了个Label和TextBox来提示文件路径。
先在类里面定义几个全局变量,下面的代码中会用到。
1 int colIndex;//点击的单元格列索引2 int rowIndex;//点击的单元格行索引 3 Dictionary<int, string> dic = new Dictionary<int, string>();//存放excel标题4 List<string> list = new List<string>(); //存放标记后的标题5 DataTable dt;//导入的table6 string filename = "";//Excel文件名
View Code
第一步:导入Excel预览,我从网上找了一段excel导入datagridview的代码,具体如下:
1 private DataTable ExcelToDataTable(string path) 2 { 3 4 FileStream fs = File.OpenRead(path); //打开.xls文件 5 6 HSSFWorkbook wk = new HSSFWorkbook(fs); //把xls文件中的数据写入wk中 7 8 var sheet = wk.GetSheetAt(0); //提取第一个sheet 9 var headerRow = sheet.GetRow(0);//提取sheet第一行10 var cellCount = headerRow.LastCellNum;//提取行的最后一列11 DataTable table = new DataTable();12 //给table添加一个列13 for (int i = headerRow.FirstCellNum; i < cellCount; i++)14 {15 DataColumn col = new DataColumn(headerRow.GetCell(i).StringCellValue);16 table.Columns.Add(col);17 }18 //获取sheet的行数19 var rowCount = sheet.LastRowNum;20 //循环逐行将sheet中数据写入table21 for (int i = (sheet.FirstRowNum + 1); i < rowCount; i++)22 {23 var row = sheet.GetRow(i);24 DataRow datarow = table.NewRow();25 for (int j = row.FirstCellNum; j < cellCount; j++)26 {27 if (row.GetCell(j) != null)28 {29 datarow[j] = row.GetCell(j).ToString();30 }31 }32 table.Rows.Add(datarow);33 }34 wk = null;35 sheet = null;36 return table;37 }
导入方法
这是个导入的方法,双击导入按钮,在事件里添加如下代码:
1 private void btnImport_Click(object sender, EventArgs e) 2 { 3 OpenFileDialog ofd = new OpenFileDialog(); 4 ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); 5 ofd.Filter = "Excel Files(*.xls)|*.xls"; 6 if (ofd.ShowDialog() != DialogResult.OK) 7 { return; } 8 filename = ofd.FileName; 9 textBox1.Text = ofd.FileName;10 dt = ExcelToDataTable(filename);11 dataGridView1.DataSource = dt;//ExcelToDataTable(ofd.FileName);12 13 int ColCount = dataGridView1.Columns.Count;14 //将Excel所有标题存入dic中15 for (int i = 0; i < ColCount; i++)16 {17 dic.Add(i, dataGridView1.Columns[i].HeaderText);18 }19 }
导入预览
为了方便我将测试的Excel文档放到桌面,Excel的标题我存到了字典里,下面会用到。
到这里基本实现了第一步,点击导入按钮,选中excel文件,显示在datagridview上预览。
第二步、第三步:要求的样式大概是这样:
点击datagridview的标题,弹出下拉菜单,显示定义的字段。因此添加一个ContextMenuStrip控件,将字段一个个添加进去。
要求点击标题弹出下拉菜单,我用到了datagridview的CellCilck事件,这个事件里代码非常简单,只要弹出contexmenustrip就行了,然后加一个对是否为标题行的判断。
View Code
第四步比较复杂,大概思路是,下拉菜单的选项是固定的,通过contextMenuStrip1_ItemClicked事件里的e.ItemClicked.Text可以获取到你选择的菜单,因此我用了switch case语句来进行判断。
首先是点击datagridview一列的标题,然后选中一个菜单,这一列的标题要变成e.itemClicked,并且标记这一列,所以我写了一个标记的方法表示标记后操作和样式。
1 private void Mark(string item)2 {3 dt.Columns[colIndex].ColumnName = item;4 dataGridView1.Columns[colIndex].HeaderText = item;5 dataGridView1.Columns[colIndex].DefaultCellStyle.BackColor = Color.LightSteelBlue;6 dataGridView1.EnableHeadersVisualStyles = false;7 dataGridView1.Columns[colIndex].HeaderCell.Style.BackColor = Color.LightSlateGray;8 }
Mark
因为涉及到匹配的问题,例如日期格式的列无法标记成“呼叫类型”,“通话时间”等,所以加了一些正则判断,正则表达式以前从没用过,也是网上现学的,写得比较差。
又写了一个判断后的方法:
1 private void MatchItem(string match, string str, string item) 2 { 3 Match m = Regex.Match(match, str); 4 if (m.Success) 5 { 6 Mark(item); 7 //如果该列已标记,则不添加item到list 8 foreach (var i in list) 9 {10 if (item == i)11 return;12 }13 list.Add(item);14 }15 else16 {17 contextMenuStrip1.Hide();18 MessageBox.Show("该列不为" + item + "!");19 }20 }
判断格式
至于contextmenustrip_itemClicked事件里只要在每个case里调用MatchItem方法就可以了。
1 private void contextMenuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e) 2 { 3 //dt = ExcelToDataTable(filename); 4 //获取想要标记的的标题 5 string item = e.ClickedItem.Text; 6 //获取用于判断的内容 7 var BeMatch = dataGridView1.Rows[rowIndex + 1].Cells[colIndex].Value.ToString(); 8 //当前列在table是否已经存在 9 int i = 0;10 foreach (DataColumn dataCol in dt.Columns)11 {12 if (i != colIndex && dataCol.ColumnName == item)13 {14 contextMenuStrip1.Hide();15 var index = i + 1;16 MessageBox.Show("该表第" + index + "列为" + dataCol.ColumnName + ",无法标记");17 return;18 }19 i++;20 }21 22 switch (item)23 {24 case "呼叫类型":25 {26 string strType = @"[\u4e00-\u9fbb]";27 MatchItem(BeMatch, strType, item);28 }29 break;30 case "对方号码":31 {32 string strNum = @"0?[1]+[358]+\d{9}";33 MatchItem(BeMatch, strNum, item);34 }35 break;36 case "日期":37 {38 string strDate = @"^2\d{7}$";39 MatchItem(BeMatch, strDate, item);40 }41 break;42 case "时间":43 {44 string strTime = @"^[0-2]\d{1}[0-5]\d{1}[0-5]\d{1}$";45 MatchItem(BeMatch, strTime, item);46 }47 break;48 case "通话时长":49 {50 string strOften = @"\d";51 MatchItem(BeMatch, strOften, item);52 }53 break;54 case "取消设置":55 {56 //遍历原标题,获取取消设置的列,移出dt57 foreach (var kv in dic)58 {59 if (kv.Key == colIndex)60 {61 list.Remove(dataGridView1.Columns[colIndex].HeaderText);62 //list.Insert(colIndex,kv.Value);63 item = kv.Value;64 dt.Columns[colIndex].ColumnName = item;65 }66 }67 //还原标题和样式 68 dataGridView1.Columns[colIndex].DefaultCellStyle.BackColor = Color.White;69 dataGridView1.Columns[colIndex].HeaderCell.Style.BackColor = DefaultBackColor;70 }71 break;72 }73 }
标记列
第五步 :保存标记后的字段数据到datatable里,我在网上找了一个方法,附上链接:http://www.jb51.net/article/80620.htm
1 private void btnExport_Click(object sender, EventArgs e) 2 { 3 //添加标记的列到table 4 DataTable table = dt.DefaultView.ToTable(false, list.ToArray()); 5 6 try 7 { 8 SaveFileDialog sfd = new SaveFileDialog(); 9 //sfd.FileName = "测试导出.xls";10 sfd.Filter = "Excel Files(*.xls)|*.xls";11 sfd.FileName = "测试导出";12 if (sfd.ShowDialog() == DialogResult.OK)13 {14 filename = sfd.FileName;15 DataToExcel(table, filename, "话单", false);16 }17 }18 catch (Exception ex)19 {20 MessageBox.Show(ex.Message);21 }
导出标记后的列
做完运行的效果是这样的:
到这里基本结束了,运行后没什么大问题,可能有的bug还没有测出来。第一次写博客,写的不好大家见谅,欢迎提出各种意见和建议。谢谢支持!
原标题:实现标记datagridview标题并导出Excel的功能
关键词:GridView