有个需求是,程序导出一份word报告,报告中有各种各样的表格,导出时还需要插入图片。
脑海中迅速闪过好几种组件,openxml组件,com组件,npoi。为了减少程序画复杂表格,我们选用了com组件+word模板的方式,程序只需要对word中的书签进行赋值即可。
不知道这几种组件的(或者还有其他写入word的组件可以推荐)优缺点各是什么,还请各路大拿评论区指点一二。
com组件唯一让人不爽的就是他过于依赖word,因为版本带来的不兼容问题,及各种会生成WORD半途会崩溃的问题.而且很难解决。
不说这么悲伤的事情了,反正坑都踩了,文章后面我会附上各种深坑的解决方案,今天主要分享的是com组件写入word的各种操作。
1.书签赋值
/// <summary>
/// 给特定书签赋值
/// </summary>
/// <param name="bookMarkName"></param>
/// <param name="value"></param>
public void EditTablestring bookMarkName, string value, Document doc)
{
try
{
if !doc.Bookmarks.ExistsbookMarkName))
return;
object s = bookMarkName;
Range rng = doc.Bookmarks.get_Itemref s).Range;
rng.Text = value;
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
2.获取指定书签的范围
/// <summary>
/// 获取指定书签的Range
/// </summary>
/// <param name="bookMarkName">书签名称</param>
public Range GetBookMarkRangestring bookMarkName, Document doc)
{
object oBookMarkName = bookMarkName;
return doc.Bookmarks.get_Itemref oBookMarkName).Range;
}
3.添加书签
/// <summary>
/// 添加书签
/// </summary>
/// <param name="bookMarkName">书签名</param>
/// <param name="activeRange">要添加书签的范围</param>
public void AddBookMarkstring bookMarkName, Range activeRange, Document doc)
{
object oActiveRange = activeRange;
doc.Bookmarks.AddbookMarkName, ref oActiveRange);
}
4.复制书签内容到另一个书签
/// <summary>
/// 复制书签内容至另一书签
/// </summary>
/// <param name="sourceBookMarkName">源书签</param>
/// <param name="toBookMarkName">目标书签</param>
public void CopyRangestring sourceBookMarkName, string toBookMarkName, Document doc)
{
object oSourceBookMarkName = sourceBookMarkName;
object oToBookMarkName = toBookMarkName;
Range roRange = CopyRangedoc.Bookmarks.get_Itemref oSourceBookMarkName).Range, doc.Bookmarks.get_Itemref oToBookMarkName).Range);
doc.Bookmarks.get_Itemref oToBookMarkName).Delete);
AddBookMarktoBookMarkName, roRange, doc);
}
/// <summary>
/// 复制选定范围内容至另一范围
/// </summary>
/// <param name="sourceRange">源范围</param>
/// <param name="activeRange">目标范围</param>
public Range CopyRangeRange sourceRange, Range activeRange)
{
try
{
lock copyLock)
{
sourceRange.Copy);
activeRange.Paste);
}
return activeRange;
}
catch
{
KillwordProcess);
throw;
}
}
5.打开word文件
/// <summary>
/// 打开文件
/// </summary>
/// <param name="Path">文件路径及文件名</param>
/// <param name="IsVisible">是否可见</param>
public void OpenWordstring Path, bool IsVisible, Document doc)
{
object oMissing = System.Reflection.Missing.Value;
GUIDCaption = Guid.NewGuid).ToString);
app.Visible = IsVisible;//是否实时预览
app.Caption = GUIDCaption;
object oPath = Path;
doc = app.Documents.Openref oPath, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
}
6.表格纵向合并
/// <summary>
/// 纵向合并
/// </summary>
/// <param name="bookMarkName"></param>
/// <param name="tableIndex"></param>
/// <param name="row_a">开始行号</param>
/// <param name="col_a">开始列号</param>
/// <param name="row_b">结束行号</param>
/// <param name="col_b">结束列号</param>
public void MergeColumnCellstring bookMarkName, int tableIndex, int row_a, int col_a, int row_b, int col_b, Document doc)
{
try
{
object oBookMarkName = bookMarkName;
Range activeRange = doc.Bookmarks.get_Itemref oBookMarkName).Range;
var newTable = activeRange.Tables[tableIndex];
newTable.Cellrow_a, col_a).MergenewTable.Cellrow_b, col_b));
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
7.插入图片
/// <summary>
/// 插入图片
/// </summary>
/// <param name="bookMarkName"></param>
/// <param name="fileName">图片路径</param>
/// <param name="type">图片版式:四周型,嵌入型,环绕型</param>
public void InsertPicturestring bookMarkName, string fileName, WdWrapType type)
{
try
{
if !doc.Bookmarks.ExistsbookMarkName))
{
return;
}
object oBookMarkName = bookMarkName;
Microsoft.Office.Interop.Word.Range activeRange = doc.Bookmarks.get_Itemref oBookMarkName).Range;
object linkToFile = false;
object saveWithDocument = true;
InlineShape inlineShape = doc.InlineShapes.AddPicturefileName, ref linkToFile, ref saveWithDocument, activeRange);
inlineShape.ConvertToShape).WrapFormat.Type = type;
}
catch Exception ex)
{
KillwordProcess);
//记录日志
AppCommon.AppLogger.WriteLog"插入图片,错误为:" + ex.ToString), int)AppCommon.Unitity.AppChannelEnmu.SS).ToString));
}
}
8.设置图片大小
/// <summary>
/// 设置图片大小
/// </summary>
/// <param name="filePath"></param>
/// <param name="width"></param>
/// <param name="height"></param>
public string SetPicturestring filePath, int width, int height)
{
Bitmap bm = new BitmapfilePath);
Bitmap thumb = new Bitmapwidth, height);
Graphics g = Graphics.FromImagethumb);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImagebm, new System.Drawing.Rectangle0, 0, width, height), new System.Drawing.Rectangle0, 0, bm.Width, bm.Height), GraphicsUnit.Pixel);
g.Dispose);
string path = ConfigurationManager.AppSettings["MapPath"].ToString) + "/Assets/internal/img/new.png";
thumb.Savepath, System.Drawing.Imaging.ImageFormat.Png);
bm.Dispose);
thumb.Dispose);
return path;
}
9.书签范围内添加一行
/// <summary>
/// 在书签范围内插入一行到指定表中
/// </summary>
/// <param name="BookMarkName">书签名称</param>
/// <param name="tableIndex">表的索引</param>
/// <param name="rowIndex">插入行的位置</param>
public void InsertRowstring bookMarkName, int tableIndex, int rowIndex)
{
try
{
if !doc.Bookmarks.ExistsbookMarkName))
{
return;
}
object oBookMarkName = bookMarkName;
Microsoft.Office.Interop.Word.Range activeRange = doc.Bookmarks.get_Itemref oBookMarkName).Range;
InsertRowactiveRange, tableIndex, rowIndex);
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
/// <summary>
/// 在所选范围内插入一行到指定表中
/// </summary>
/// <param name="activeRange">范围对象</param>
/// <param name="tableIndex">所选范围内表的索引</param>
/// <param name="rowIndex">插入行的位置</param>
public void InsertRowMicrosoft.Office.Interop.Word.Range activeRange, int tableIndex, int rowIndex)
{
try
{
object NumRows = 1;
activeRange.Tables[tableIndex].Rows[rowIndex].Select);
doc.ActiveWindow.Panes[1].Selection.InsertRowsBelowref NumRows);
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
10.删除书签范围内容
/// <summary>
/// 删除书签范围内容
/// </summary>
/// <param name="bookMarkName">书签名</param>
public void DeleteBookMarkRangestring bookMarkName)
{
try
{
object oBookMarkName = bookMarkName;
if doc.Bookmarks.ExistsbookMarkName))
{
DeleteRangedoc.Bookmarks.get_Itemref oBookMarkName).Range);
}
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
/// <summary>
/// 删除所选范围内容
/// </summary>
/// <param name="activeRange">所选范围对象</param>
public void DeleteRangeRange activeRange)
{
try
{
if null != activeRange)
{
object oMissing = Missing.Value;
activeRange.Deleteref oMissing, ref oMissing);
foreach Table dTable in activeRange.Tables)
{
dTable.Delete);
}
}
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
11.删除书签范围的行
/// <summary>
/// 在书签范围内指定表删除行
/// </summary>
/// <param name="bookMarkName">书签名称</param>
/// <param name="tableIndex">表索引</param>
/// <param name="rowIndex">行号</param>
public void DeleteRowstring bookMarkName, int tableIndex, int rowIndex)
{
try
{
object oBookMarkName = bookMarkName;
if doc.Bookmarks.ExistsbookMarkName))
{
Microsoft.Office.Interop.Word.Range activeRange = doc.Bookmarks.get_Itemref oBookMarkName).Range;
DeleteRowactiveRange, tableIndex, rowIndex);
}
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
/// <summary>
/// 在所选范围内指定表删除行
/// </summary>
/// <param name="activeRange">所选范围对象</param>
/// <param name="tableIndex">表索引</param>
/// <param name="rowIndex">行号</param>
public void DeleteRowMicrosoft.Office.Interop.Word.Range activeRange, int tableIndex, int rowIndex)
{
try
{
if activeRange.Tables[tableIndex].Rows.Count >= rowIndex)
{
activeRange.Tables[tableIndex].Rows[rowIndex].Delete);
}
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
12.插入分页符
public static void InsertBreakDocument doc, string bookmark)
{
object oBookMarkName = bookmark;
if doc.Bookmarks.Existsbookmark))
{
object pBreak = int)WdBreakType.wdPageBreak;
doc.Bookmarks.get_Itemref oBookMarkName).Range.InsertBreakref pBreak);
}
//object oBookMarkName = bookmark;
//Range perRange = doc.Bookmarks.get_Itemref oBookMarkName).Range;
//object oEnd = perRange.End;
//object oWdBreak = int)WdBreakType.wdPageBreak;
//doc.Rangeref oEnd, ref oEnd).InsertBreakref oWdBreak);
//doc.Rangeref oEnd, ref oEnd).InsertParagraph);
}
13.结束word进程
异常的时候一定要执行此方法,否则,word进程就会被遗留在内存中,后续如果继续执行,会积累更多的进程,导致进程卡死,内存飙升。
/// <summary>
/// 结束word进程
/// </summary>
private void KillwordProcess)
{
try
{
Process[] myProcesses;
//DateTime startTime;
myProcesses = Process.GetProcessesByName"WINWORD");
//通过进程窗口标题判断
foreach Process myProcess in myProcesses)
{
if null != GUIDCaption && GUIDCaption.Length > 0 && myProcess.MainWindowTitle.EqualsGUIDCaption))
{
myProcess.Kill);
}
}
}
catch Exception e)
{
AppCommon.AppLogger.WriteLog"结束Word,错误为:" + e.ToString), int)AppCommon.Unitity.AppChannelEnmu.SS).ToString));
throw e;
}
}
14.删除空行
这段代码厉害了,是用来精确控制的,你可以用它获取到word的每一寸区域,这样你想在哪里写值就在哪里写。下面这段代码是用来删除书签范围上方的空行的,代码中会判断上方是空行的区域,
如果上方区域有文字,则
iEnd为文字所在行-1,
range.Start表示该书签区域开头所在的行
titleRange就是要删除的空行区域
/// <summary>
/// 删除Range 上面的空行
/// </summary>
/// <param name="range"></param>
/// <param name="holdblank">下面保留几个空行,默认保留一个</param>
public void DeleteRangeUpBlankRowDocument doc, Range range, int holdblank = 1)
{
try
{
int iStart = range.Start - holdblank;
int iEnd = range.Start - holdblank;
object oStart = range.Start - holdblank;
object oEnd = range.Start - holdblank;
Range titleRange = doc.Rangeref oStart, ref oEnd);
//获取标题范围 从表格范围起始位置开始 每次循环范围向前增加1
while true)
{
iStart = iStart - 1;
titleRange.SetRangeiStart, iEnd);
if iStart < 0 || IsTitleRangeOvertitleRange))
{
titleRange.SetRangeiStart + 1, iEnd);
break;
}
}
//删除空行
object oMissing = Missing.Value;
titleRange.Deleteref oMissing, ref oMissing);
}
catch Exception e)
{
KillwordProcess);
throw e;
}
}
/// <summary>
/// 判断要删除表的标题范围是否超出
/// </summary>
/// <param name="titleRange"></param>
/// <returns></returns>
public bool IsTitleRangeOverRange titleRange)
{
if titleRange.Tables.Count > 0)//范围内包含表格
{
return true;
}
if titleRange.Paragraphs[1].PageBreakBefore == 1)//有分页符
{
return true;
}
if !string.IsNullOrEmptytitleRange.Text.Trim)))//内容不为空了
{
return true;
}
return false;
}
常见错误及处理方案:
1.报告生成期间异常:System.Runtime.InteropServices.COMException 0x8001010A): 消息筛选器显示应用程序正在使用中。 异常来自 HRESULT:0x8001010A RPC_E_SERVERCALL_RETRYLATER))
解决方案:出现这种错误有限检查COM组件权限是否正常,检查步骤:
(1)打开组件服务
(2)打开DCOM配置
(3)找到word97-2003
(4)按照如图依次配置
(5)一般这里我们选择用户名密码 的方式,这个密码必须在web.Config中进行配置
<identity impersonate=”true” userName=”服务器登录名” password=”服务器登陆密码” />
2.System.Runtime.InteropServices.COMException 0x800A1735): 集合所要求的成员不存在。
解决方案:这个是程序错误,只要调试代码即可发现错误。
3.System.Runtime.InteropServices.COMException 0x800A1710): 无法编辑 Range。
解决方案:这个说明编辑某个区域的时候,超出了范围,或者找不到这个区域范围,这个也是代码的问题,需要仔细查找,不是很好定位。
——————————————-(正文完)—————————————————–
还有一些其他的问题没有收集,大家遇到了可以在评论区发出来,我能帮大家解答的一定尽我所能!
好啦,今天的分享就到这里……
向着高级,进发!
更多精彩内容,请关注我的V信公众号:程序员不帅哥