Word自定义导出小记_C#


一、前言

记录一次word导出的实现。以前倒是做过几次Excel导出,但这次有一些特殊的格式要求。
这次打算用word模板,先简单建了一个word,往里塞DataTable测试功能实现
在这里插入图片描述

二、使用Microsoft.Office.Interop.Word

using static System.Net.Mime.MediaTypeNames;
using Microsoft.Office.Interop.Word;
using Application = Microsoft.Office.Interop.Word.Application;
   public class Export
 {
     public static void Word()
     {
         // 创建Word应用程序实例
         Application wordApp = new Application();
         wordApp.Visible = false; // 如果需要看到Word应用程序则设置为true
         // 打开Word模板文件
         Document doc = wordApp.Documents.Open(@"E:\temple.docx");
         // 找到文档中的表格   
         Table table = doc.Tables[1];
         // 假设表格有足够的行和列来接受数据
         for (int i = 1; i < 4; i++) // 假设有3行数据
         {
             Row row = table.Rows[i]; // 从第二行开始,第一行可能是表头
             for (int j = 1; j < 2; j++) // 假设每行有3列数据
             {
                 row.Cells[j].Range.Text = $"Cell {i},{j}"; // 填充数据
             }
         }
         // 保存文档
         object savePath = @"E:\temple1.docx";
         doc.SaveAs2(savePath);
         // 关闭文档和Word应用程序
         doc.Close();
         wordApp.Quit();
         // 释放对象
         System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
         System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp);
         Console.WriteLine("完成");
     }
 }

测试结果
在这里插入图片描述
能满足功能需求,但是对于Office.Interop的索引感觉有点怪,列表,不管table、row、cell,索引全是1开始,习惯性的tables [0]直接挂壁,
这时候想起了之前用过的NPOI,拿来试试

三、NPOI实现

在这里插入图片描述
前后端先分别nuget一下NPOI

using NPOI.XWPF.UserModel;
using System.IO;
public class Export
{
    public static void Word()
    {
        using (FileStream stream = File.OpenRead(@"E:\temple.docx"))
        {
            XWPFDocument doc = new XWPFDocument(stream);
            var table = doc.Tables[0];
            for (int i = 0; i < 3; i++) // 假设有3行数据
            {
                for (int j = 0; j < 1; j++)
                {
                    table.GetRow(i).GetCell(j).SetText($"Cell {i},{j}");
                }
            }
            //写文件
            FileStream outFile = new FileStream(@"E:\temple1.docx", FileMode.Create);
            doc.Write(outFile);
            outFile.Close();
        }
    }
}

在这里插入图片描述
嗯,异曲同工,妙啊

四、NPOI实现插入行

现在的问题是,单元格只能用不能建,
使用表格没有的单元格,就报错。
如果我们数据10条、50条怎么弄,模板不能自定义,又挂壁

 for (int i = 0; i < 3; i++) // 假设有3行数据
 {
     #region    --测试区--这一次改动主要在这里
     if (i == 2)
	{
	    XWPFTableRow newRow = table.CreateRow();//测试1---新建
	
	    var oldrow = table.Rows[i - 1];
	    var newRow2 = oldrow.CloneRow();//测试2---拷贝
	}
     #endregion --测试区--end
     
     for (int j = 0; j < 1; j++)
     {
         table.GetRow(i).GetCell(j).SetText($"Cell {i},{j}");
     }
 }

在这里插入图片描述
这新行写后面去了,插偏了,找找文档看看
CreateRow、AddRow、CloneRow都是到列表尾类。 AddNewRowBetween过时,连Office.Interop的InsertAfter都没。
正烦躁时,看到了InsertNewTableRow,就是要初始化单元格,试试

for (int i = 3; i < 10; i++) // 假设有3行数据
{
    #region    --测试区--这一次改动主要在这里
     XWPFTableRow row = table.InsertNewTableRow(i);
	 //使用insertNewTableRow必须填充列,且列数必须统一
	 for (int j = 0; j < columns; j++)
	 {
	     row.CreateCell(); //创建列
	 }
     #endregion --测试区--end
    for (int j = 0; j < columns; j++)
    {
        table.GetRow(i).GetCell(j).SetText($"Cell {i},{j}");
    }
}

在这里插入图片描述
完美

五、富文本不换行

在这里插入图片描述
加了一个占位符,想显示RichTextBox 录入的值。结果发现占位符替换后不换行,\n无效。
那就只有手动换行了。

//手动换行
string [] strs = str.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
strs.ToList().ForEach(p =>
{
	run.AppendText(p);
	run.AddCarriageReturn();//加回车
});

富文本按\n切段,循环加回车。
在这里插入图片描述

六、重构一下

using System;
using System.IO;
using System.Data;
using NPOI.XWPF.UserModel;
using System.Collections.Generic;
using Microsoft.Office.Interop.Word;
using DataTable = System.Data.DataTable;
public class Export
{
    public static void Word(string templePath,string outPath,Dictionary<string,string> parameters,DataTable dt)
    {
        using (FileStream stream = File.OpenRead(templePath))
        {
            try
            {
                XWPFDocument doc = new XWPFDocument(stream);
                var table = doc.Tables[0];
                int columns = table.GetRow(0).GetTableCells().Count;//列数
                //表格数据写入
                for (int i = 3; i < dt.Rows.Count+3; i++) // 循环行写入
                {
                    //原地建新行
                    XWPFTableRow row = table.InsertNewTableRow(i);
                    //--使用insertNewTableRow必须填充列,且列数必须统一
                    for (int j = 0; j < columns; j++)
                    {
                        row.CreateCell(); //创建列
                    }
                    //新行里写数据
                    for (int j = 0; j < columns; j++)
                    {
                        table.GetRow(i).GetCell(j).SetText(dt.Rows[i - 3][j].ToString());//单元格赋值
                    }
                }
                //模板占位符替换
                 TextReplace(parameters, doc);
                //写文件
                FileStream outFile = new FileStream(outPath, FileMode.Create);
                doc.Write(outFile);
                outFile.Close();
            }
            catch (Exception ex)
            {
            }
        }
    }
     /// <summary>
	 /// 占位符 替换
	 /// </summary>
	 private static void TextReplace(Dictionary<string, string> parameters, XWPFDocument doc)
	 {
	     foreach (var parameter in parameters)
	     {
	         string str = parameter.Value;
	         //时间换格式
	         if (parameter.Key.Contains("time")) {
	          str = DateTime.Parse(str.ToString()).ToString("yyyy-MM-dd");
	          doc.FindAndReplaceText("[" + parameter.Key + "]", str);
	         }
	         //富文本 手动换行
	         else if (parameter.Key.Contains("notes"))
	         {
	             //找到段落
	             var search = doc.Paragraphs.Where(p => p.Text.Contains(parameter.Key)).First();
	             //创建文本对象
	             var run = search.CreateRun();
	             //移除占位
	             search.ReplaceText("[" + parameter.Key + "]", "");
	             //手动换行
	             string[] strs = str.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
	             strs.ToList().ForEach(p =>
	             {
	                 run.AppendText(p);
	                 run.AddCarriageReturn();//加回车
	             });
	         }
	         //字符串替换
	         else
	             doc.FindAndReplaceText("[" + parameter.Key + "]", str);
	     }
	 }
}


测试数据

 private void test()
 {
     //测试数据
     Dictionary<string, string> dic = new Dictionary<string, string>();
     dic.Add("totalprice", "1000.02");
     dic.Add("comnotes", "第一行\n第二行\n第三行");
      
     DataTable dt = new DataTable();
     dt.Columns.Add(new DataColumn("sortid", typeof(int)));
     dt.Columns.Add(new DataColumn("name", typeof(string)));
     dt.Columns.Add(new DataColumn("model", typeof(string)));
     dt.Columns.Add(new DataColumn("unit", typeof(string)));
     dt.Columns.Add(new DataColumn("counts", typeof(decimal)));
     dt.Columns.Add(new DataColumn("price", typeof(decimal)));
     dt.Columns.Add(new DataColumn("totalpc", typeof(decimal)));
     dt.Columns.Add(new DataColumn("notes", typeof(string)));
     for (int i = 0; i < 10; i++)
     {
         DataRow dr = dt.NewRow();
         int sort = i + 1;
         dr["sortid"]= sort.ToString();
         dr["name"] = "name"+ sort.ToString();
         dr["model"] = "model"+ sort.ToString();
         dr["unit"] = "unit"+sort.ToString();
         dr["counts"] =  sort+1;
         dr["price"] = sort+2;
         dr["totalpc"] = sort+3;
         dr["notes"] = sort+"测试";
         dt.Rows.Add(dr);
     }         
     //测试
     Export.Word(@"E:\temple.docx", @"E:\temple1.docx", dic,dt);
 }

效果
在这里插入图片描述

七、总结

本文记录了C#使用NPOI导出数据到Word模板过程中所遇到的三个问题:
1、datatable循环原地插入行
2、模板占位符批替换
3、富文本不换行解决办法
在此基础上,可以扩展复杂的word模板、树状多级DataTable数据导出。这次就记录到这里了。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值