我现在在一家大型制造业公司从事生产管理相关工作,管理离不开计划,计划离不开数据,数据离不开统计分析处理。产品的工序多,涉及到的人员、场地、设备、图纸、天气、潮水、原材料等等,关系复杂,不作讨论。45个项目,分为45个Excel表中,大约数据为1M个单元格。
这个软件的功能主要实现了ERP或者叫CIMS中计划管理的核心功能,此类ERP基本就是按企业管理定制,很难有软件能适用所有企业,每个企业管理水平,资源配置,人员素质等等都 不一样,
原本想采用BS结构做一个数据库程序,但是需求是不断改变的,而且任务非常急。采用EXCEL做平台可以省去做界面的时间,也可以省去做数据库的时间,同时还省去了培训的时间。软件是随时管理需求在不断变化。Excel在不需要安装特别的客户端,有很多现成的图表可以使用。现在的代码量大约是10K行,当然也有许多垃圾在里面,
不吹牛了,两年前采用C++的一个类写了一个仪器的上位机程序,最后数据存到EXCEL,是通过COM来操作的,但C++在处理这些不确定类型的时候感觉比较麻烦(也许是自己水平不够),类型转换到现在都还是晕的,, 要用的时候只能去查了。虽然 我一直希望把C++坚持下去,但C++做这些的高层应用,是太麻烦了。
C#之前开发过一个AUTOCAD的插件,虽然最后做了大半中止了,但核心功能都实现了。
采用VSTO也是看了微软的说明 ,推荐使用.net,正好自己也用过C#,感觉效率比C++要高,
核心代码主要经过了几次大修改。
第1版主要是采用全部采用COM来读写cell数据。
第2版采用OLE DB来读数据,写采用COM。
第3版采用OLE DB读 ,,写是通过WIN剪切板。
第4版采用OLE DB读,写是通过TOXY/NPOI
每次的修改都是为了读写性能的提升,程序运行在至强CPU上面,第4版之前,统计全部数据最长时间需要4小时。第4版统计现在最多2分钟。
本想第4版把读取也改为toxy/NPOI,最近太忙也没时间,性能已完全满足需求了,所以也不花时间在上面了。
以下函数是初始化EXCEL模板的,有个问题要注意就是EXCEL的XML文件中,所有的样式都是引用的样式的ID,而不是引用样式本身,如果有5种样式就要创建5个
workbook.CreateCellStyle(),分别赋予,,而不能只能创建一个。
void init_npoi_workbook()
{
workbook = new XSSFWorkbook();
titlecellstyle = workbook.CreateCellStyle();
titlecellstyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
titlecellstyle.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;
titlecellstyle.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
titlecellstyle.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
titlecellstyle.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
titlecellstyle.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
titlecellstyle.WrapText = true;
for (int j = 0; j < block_count_weight_array.GetLength(1); j++)
{
cellstyle_array[j] = workbook.CreateCellStyle();
cellstyle_array[j].Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
cellstyle_array[j].VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.Center;
cellstyle_array[j].BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
cellstyle_array[j].BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
cellstyle_array[j].BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
cellstyle_array[j].BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
cellstyle_array[j].WrapText = true;
cellstyle_array[j].BottomBorderColor = rowcolor[j];
cellstyle_array[j].TopBorderColor = rowcolor[j];
cellstyle_array[j].LeftBorderColor = rowcolor[j];
cellstyle_array[j].RightBorderColor = rowcolor[j];
cfont1[j] = workbook.CreateFont();
cfont1[j].Color = rowcolor[j];
cellstyle_array[j].SetFont(cfont1[j]);
}
foreach (int column in count_columns)
{
//按周、月统计周期0为周,1为月
int cols_4_count = 0;
//if (0 == count_type)
//{
cols_4_count = count_countperoids;
ISheet sht = workbook.CreateSheet(column.ToString());
for (int j = 0; j < wlcountline_start; j++)
{
for (int i = 0; i < count_countperoids + 2; i++)
{
sht.CreateRow(j).CreateCell(i);
}
}
//用来滚动周次日期
DateTime date_roll = new DateTime();
DateTime date_current_start = cal_week_year_start(count_startdate);
//表头周历的第一周的第一天,循环过程中累加日期
date_roll = date_current_start.AddDays((cal_date_Year_week(count_startdate) - 1) * 7);
//周历第一天,计算起点
if (1 == count_type)
{
d_year_start = date_roll;
}
IRow row = sht.CreateRow(4 - 1);
for (int m = 0; m < cols_4_count; m++)
{
//填写表头上统计周期
sht.GetRow(4 - 1).CreateCell(wlcountline_column_start + m * wlcountline_column_per_month);
//sht.CreateRow(4 - 1).CreateCell(wlcountline_column_start + m * wlcountline_column_per_month - 1);
//sht.CreateRow(4 - 1).CreateCell(wlcountline_column_start + m * wlcountline_column_per_month );
// regon = new CellRangeAddress(4 - 1, 4 - 1, wlcountline_column_start + m * wlcountline_column_per_month - 1, wlcountline_column_start + m * wlcountline_column_per_month);
// sht.AddMergedRegion(regon);
//日
if (0 == count_type)
{
sht.GetRow(4 - 1).CreateCell(wlcountline_column_start + m * wlcountline_column_per_month - 1).SetCellValue(d_year_start.AddDays(m).ToShortDateString());
// sht.GetRow(4 - 1).GetCell( wlcountline_column_start + m * wlcountline_column_per_month - 1).SetCellValue() = "yyyy-m-d";
}
//周
else if (1 == count_type)
{
string st1 = "";
st1 = cal_date_year(date_roll).ToString() + "-W" + cal_date_Year_week(date_roll).ToString() + "\n(" + date_roll.ToShortDateString() + ")";
row.CreateCell(wlcountline_column_start + m * wlcountline_column_per_month - 1).SetCellValue(st1);
date_roll = date_roll.AddDays(7);
}
//月
else if (2 == count_type)
{
DateTime d_start;
if (count_startdate.Day >= count_month_start)
{
//下月起始时间
d_start = new DateTime(count_startdate.AddMonths(m + 1).Year, count_startdate.AddMonths(m + 1).Month, count_month_start);
}
else
{
//本月的起始时间
d_start = new DateTime(count_startdate.AddMonths(m).Year, count_startdate.AddMonths(m).Month, count_month_start);
}
sht.GetRow(4 - 1).CreateCell(wlcountline_column_start + m * wlcountline_column_per_month - 1).SetCellValue(d_start.Year.ToString() + "-M" + d_start.Month.ToString());
}
regon = new CellRangeAddress(4 - 1, 4 - 1, wlcountline_column_start + m * wlcountline_column_per_month - 1, wlcountline_column_start + m * wlcountline_column_per_month );
sht.AddMergedRegion(regon);
}
}
}
以下是将统计结果从内存数组写到文件中,
public void toxy_writecache()
{
Excel.Application app = App;
// app.ScreenUpdating = false;
//try
//{
// workbook = new XSSFWorkbook();
//}
//catch (Exception ex)
//{
// MessageBox.Show(ex.Message + ex.StackTrace);
//}
ICell cell, cell2;
foreach (int column in count_columns)
{
ISheet sheet = workbook.GetSheet(column.ToString());
IRow row;
for (int j = 0; j < block_count_weight_array.GetLength(1); j++)
{
row = sheet.CreateRow(wlcountline_start + shipindex * temp_rows_4_ship + 3 * j - 1);
//边框颜色
//cellstyle.BottomBorderColor = rowcolor[j];
//cellstyle.TopBorderColor = rowcolor[j];
//cellstyle.LeftBorderColor = rowcolor[j];
//cellstyle.RightBorderColor = rowcolor[j];
cellstyle = cellstyle_array[j];
for (int m = 0; m < 2; m++)
{
cell = row.CreateCell(m);
cell.CellStyle = cellstyle;
}
int length = 0;
// row = sheet.CreateRow( j);
//分段清单明细,每2列填1列,
for (int k = 0; k < block_count_weight_array.GetLength(2); k += 2)
{
cell = row.CreateCell(k + 2);
cell2 = row.CreateCell(k + 1 + 2);
regon = new CellRangeAddress(cell.RowIndex, cell.RowIndex, cell.ColumnIndex, cell.ColumnIndex + 1);
cell.CellStyle = cellstyle;
cell2.CellStyle = cellstyle;
sheet.AddMergedRegion(regon);
cell.SetCellValue(block_array[column, j, k]);
if (length < block_array[column, j, k].Length)
{
length = block_array[column, j, k].Length;
}
}
Int16 height = Convert.ToInt16(length / 17 * 15 * 8);
if (height < 15)
{
height = 15;
}
// row.Height = 80;
row = sheet.CreateRow(wlcountline_start + shipindex * temp_rows_4_ship + 3 * j);
for (int m = 0; m < 2; m++)
{
cell = row.CreateCell(m);
cell.CellStyle = cellstyle;
}
for (int k = 0; k < block_count_weight_array.GetLength(2); k++)
{
if (k % 2 == 1)
{
cell = row.CreateCell(k + 2);
cell.CellStyle = cellstyle;
cell.SetCellValue("数量");
}
else
{
cell = row.CreateCell(k + 2);
cell.CellStyle = cellstyle;
cell.SetCellValue("重量");
}
}
row = sheet.CreateRow(wlcountline_start + shipindex * temp_rows_4_ship + 3 * j + 1);
for (int m = 0; m < 2; m++)
{
cell = row.CreateCell(m);
cell.CellStyle = cellstyle;
}
for (int k = 0; k < block_count_weight_array.GetLength(2); k++)
{
cell = row.CreateCell(k + 2);
cell.CellStyle = cellstyle;
cell.SetCellValue(block_count_weight_array[column, j, k]);
}
regon = new CellRangeAddress(wlcountline_start + shipindex * temp_rows_4_ship + 3 * j, wlcountline_start + shipindex * temp_rows_4_ship + 3 * j + 1, 1, 1);
sheet.AddMergedRegion(regon);
sheet.GetRow(wlcountline_start + shipindex * temp_rows_4_ship + 3 * j - 1).GetCell(1).SetCellValue(shipprojectname);
sheet.GetRow(wlcountline_start + shipindex * temp_rows_4_ship + 3 * j).GetCell(1).SetCellValue(rowname[j]);
if (j == block_count_weight_array.GetLength(1) - 1)
{
regon = new CellRangeAddress(wlcountline_start + shipindex * temp_rows_4_ship - 1, wlcountline_start + shipindex * temp_rows_4_ship + 3 * j + 1, 0, 0);
sheet.AddMergedRegion(regon);
sheet.GetRow(wlcountline_start + shipindex * temp_rows_4_ship - 1).GetCell(0).SetCellValue(shipindex + 1);
}
}
}
}
附上剪切板写入EXCEL的函数
public void Write_cache()
{
Excel.Application app = App;
// app.ScreenUpdating = false;
Excel.Worksheet count_start_sheet = null;
object m_objOpt = System.Reflection.Missing.Value;
foreach (int column in count_columns)
{
//先按列名与统计表的表名比较得到表对象
for (int i = 0; i < wlbookcount.Worksheets.Count; i++)
{
if (wlbookcount.Worksheets.get_Item(i + 1).Name == column.ToString())
{
count_start_sheet = wlbookcount.Worksheets.get_Item(i + 1);
}
}
//输出至剪切板,再粘贴
int col_total = 0;
if (count_type == 0)
{
col_total = 54;
}
else
{
col_total = 12;
}
for (int j = 0; j < block_count_weight_array.GetLength(1); j++)
{
string sData = "";
bool data_ed = false;
for (int k = 0; k < block_count_weight_array.GetLength(2); k += 2)
{
if (k < block_count_weight_array.GetLength(2) - 2)
//if (block_array[column, j, k] == "")
// sData += "\t\t";
//else
{
sData += block_array[column, j, k] + "\t\t";
data_ed = true;
}
else
//if (block_array[column, j, k] == "")
// sData += "\t\r\n";
//else
{
sData += block_array[column, j, k] + "\t\r\n";
data_ed = true;
}
//if (k >= col_total)
//{
// break;
//}
}
if (data_ed)
{
System.Windows.Forms.Clipboard.SetDataObject(sData);
Excel.Range rg = count_start_sheet.Cells.get_Item(wlcountline_start + shipindex * temp_rows_4_ship + 3 * j, 3);
count_start_sheet.Activate();
count_start_sheet.Paste(rg, false);
}
}
for (int j = 0; j < block_count_weight_array.GetLength(1); j++)
{
string sData = "";
bool data_ed = false;
for (int k = 0; k < block_count_weight_array.GetLength(2); k++)
{
if (k < block_count_weight_array.GetLength(2) - 1)
//if (block_count_weight_array[column, j, k] > 0.1f)
{
sData += block_count_weight_array[column, j, k].ToString() + "\t";
data_ed = true;
}
//else
// sData += "0\t";
else
//if (block_count_weight_array[column, j, k] > 0.1f)
{
sData += block_count_weight_array[column, j, k].ToString() + "\r\n";
data_ed = true;
}
//else
// sData += "0\r\n";
}
if (data_ed)
{
System.Windows.Forms.Clipboard.SetDataObject(sData);
Excel.Range rg = count_start_sheet.Cells.get_Item(wlcountline_start + shipindex * temp_rows_4_ship + 2 + 3 * j, 3);
count_start_sheet.Activate();
count_start_sheet.Paste(rg, false);
}
}
/*
for (int j = 0; j < block_array.GetLength(1); j++)
{
for (int k = 0; k < block_array.GetLength(2); k += 2)
{
if (block_array[column, j, k] != "")
count_start_sheet.Cells.get_Item(wlcountline_start + shipindex * temp_rows_4_ship + 3 * j, wlcountline_column_start + k).Value = block_array[column, j, k];
}
}
for (int j = 0; j < block_count_weight_array.GetLength(1); j++)
{
for (int k = 0; k < block_count_weight_array.GetLength(2); k++)
{
if (block_count_weight_array[column, j, k] > 0.1)
count_start_sheet.Cells.get_Item(wlcountline_start + shipindex * temp_rows_4_ship + 2 + 3 * j, wlcountline_column_start + k).Value = block_count_weight_array[column, j, k];
}
}
*/
// app.ScreenUpdating = false;
}
}
另,引这个库的时候报了一个异常,在NPOI的群中询问也没得到结果,最后是直接在VS的异常监控中把这个项去掉就行了,程序工作正常。
TOXY、NPOI的功能远不止读写EXCEL文件,其他 的功能没有用过,就不作讨论了。
水平有限,如有错误 ,敬请指出。
在此要感谢TONY Qu,他是toxy /NPOI的作者,
npoi.codeplex.com
toxy.codeplex.com