一、引言
在我们日常办公中,我们经常可能遇到一些重复性的工作的,比如,我们在写毕业设计的时候,有时候我们写的过程中不注意,当整篇毕业论文写完之后,发现在毕业论文中存在很多空白的段落,这是我们就需要人工重新审阅一遍论文,再手动删除一些空白行,由于毕业论文也不是一篇,有开题报告啊,文献翻译等等,这样就可能需要我们人工都去审阅一篇把一些空白行删除,这样既花时间,我们也看的累。然后还有一个例子就是——我们人事部门的MM们,一到月末的时候就需要给本月的寿星员工发送邮件来通知参加生日会,如果员工信息是在Excel中的话,这时候人事的MM就要手动地从中查找本月寿星的邮箱,然后用Outlook一个一个添加邮件地址来给本月寿星发送邮件的,为了让人事MM们不再那么累,所以就想能不能用程序自动化地完成这一系列的过程呢?答案是肯定的,下面就让我来实现上面的两个需求的,使我们(尤其是人事MM们)的办公更加Easy。
二、自动删除Word中的空白行和页
在引言部分,我们已经提出了这个需求的。记得当时在写毕业论文的时候,我也做过这些重复的事情,经常写完之后会再去审阅一遍毕业论文中的所有文档,然后手动把一些空白行删除掉,由于当时并不知道可以对Word来进行自动化编程,所以只能傻傻地做这样一些重复的事情。但是现在不一样了,自从接触了VSTO之后,才知道Office一系列产品都是提供了一些公开的API的,我们可以利用这些对象使我们自定义Office程序和使Office自动化地工作,下面就具体讲讲如何实现这个小的工具的。
首先,我们先明确下这个工具需要实现的功能——自动移除Word文档中的空白行。然后向大家解释下实现该工具的思路:
-
我们打开一个Word文档,该Word文档就是一个Word.Document对象
-
Word文档中内容都是段落组成的,然而段落在Word对象模型中是Word.Paragraph对象
-
空白行或空白页也就是段落的内容为空,明白了这点,我们就可以在程序中对段落对象的文本进行判断,如果段落内容为空,我们就删除该段落,这样也就实现了移除空白行的功能了。
有了上面的思路之后,然后大家只需要了解Word中对象模型,然后通过对象模型找到段落对象,然后再判断它的文本是否为空,为空就删除段落,不为空就什么都不做。所以思路有了之后,就是要了解Word对象模型了,对于这部分内容,大家可以参考我博客的中的——创建Word解决方案。由于代码中都有注释,这里就直接看实现该工具的核心代码:
-
string[] wordPatharray = null;
-
// 打开需要操作的Word文档
-
privatevoid btnOpen_Click(object sender, EventArgs e)
-
{
-
using (OpenFileDialog openFileDialog = new OpenFileDialog())
-
{
-
openFileDialog.Filter = "Word document(*.doc;*.docx)|*.dox;**.docx|All Files(*.*)|*.*";
-
// 设置允许选择多个文件,该属性默认为false的,即只允许选择一个文件
-
openFileDialog.Multiselect = true;
-
if (openFileDialog.ShowDialog() == DialogResult.OK)
-
{
-
txtWordPath.Text = openFileDialog.FileName;
-
// 获得所有选定文件的文件名
-
wordPatharray = openFileDialog.FileNames;
-
}
-
}
-
}
-
// 移除Word中的所有空页
-
privatevoid btnRemove_Click(object sender, EventArgs e)
-
{
-
Word.Application wordapp = null;
-
Word.Document doc = null;
-
try
-
{
-
// 启动Word应用程序并设置不可见
-
wordapp = new Word.Application();
-
// 如果不设置该属性,就可以看到Word程序的启动过程,这个和我们手动启动Word是一样的
-
wordapp.Visible = false;
-
// 遍历每个文件名
-
foreach (var wordpath in wordPatharray)
-
{
-
doc = wordapp.Documents.Open(wordpath);
-
// 删除所有空白页面
-
Word.Paragraph paragraph;
-
Word.Paragraphs paragraphs = doc.Paragraphs;
-
for (int i = paragraphs.Count; i > 0; i--)
-
{
-
paragraph = paragraphs[i];
-
// 如果段落的文本为空的话,首先选择该段落,然后再调用Word中Selection对象的Delete方法来删除
-
// 不为空什么都不做
-
if (paragraph.Range.Text.Trim() == string.Empty)
-
{
-
paragraph.Range.Select();
-
wordapp.Selection.Delete();
-
}
-
}
-
if (doc != null)
-
{
-
// 先保存所有修改再关闭Word文档
-
doc.Save();
-
((Word._Document)doc).Close();
-
}
-
}
-
MessageBox.Show("删除空白行成功");
-
}
-
catch (Exception ex)
-
{
-
MessageBox.Show("异常发生,异常信息为:" + ex.Message);
-
}
-
finally
-
{
-
// 释放资源
-
// 退出Word程序
-
if (wordapp != null)
-
{
-
((Word._Application)wordapp).Quit();
-
}
-
doc = null;
-
wordapp = null;
-
}
-
}
为了测试该程序的正确性,这里我建立了两个测试文档,为了测试,我故意在文档中删除了空白行和空白页面,下面是两个测试文档的截图:
下面就看看该工具的运行效果(效果图是一段动画,认为这样可以更加说明运行效果)
三、人事部门的福音——自动给本月寿星员工发送邮件提醒
为了帮助大家更好地理解该程序,还是像之前一样,首先说说实现该程序的思路:
-
我们首先需要打开员工信息表,此时我们可以利用Excel对象模型中的Excel.Application.Workbooks.Open方法来获得一个工作簿对象,关于更多Excel对象模型的内容可以转向——创建Excel解决方案。
-
通过第一步我们已经获得了工作簿对象了,然后通过遍历工作簿中的激活表,即表格一(Sheet1),我们可以通过workbook.ActiveSheet来获得表格一对象。
-
遍历表格一中的所有行来找到生日信息中的月份,如果月份等于当前月份,就给该员工的邮箱进行发邮件。
-
对于自动发送邮件的实现,该实现和我们手动操作Outlook过程是一样,手动操作时,我们需要手动打开Outlook(在程序中就是创建Outlook应用程序对象),然后点击新建邮件(在程序中就是通过Applicatin对象的CreateItem(Outlook.OlItemType.olMailItem)方法来创建一个邮件项目),在新建邮件窗口中指定收件人,主题,邮件内容之后,点击Outlook中的发送邮件按钮(在程序中就是通过指定 Outlook.MailItem对象(即代表一个邮件窗体)的To(收件人)、Subject(主题)、Body(邮件内容)属性,然后再调用Send方法来发送邮件)
明白了思路之后,我们理解代码会更加容易了,具体实现代码为:
-
using System;
-
using System.IO;
-
using System.Runtime.InteropServices;
-
using System.Windows.Forms;
-
// 引用Excel和Outlook的命名空间
-
using Excel = Microsoft.Office.Interop.Excel;
-
using Outlook = Microsoft.Office.Interop.Outlook;
-
string excelpath = string.Empty;
-
// 打开员工表格
-
privatevoid btnOpen_Click(object sender, EventArgs e)
-
{
-
using (OpenFileDialog openFileDialog = new OpenFileDialog())
-
{
-
openFileDialog.Filter = "Excel File(*.xls;*.xlsx)|*.xls;**.xlsx|All Files(*.*)|*.*";
-
if (openFileDialog.ShowDialog() == DialogResult.OK)
-
{
-
txtExcelPath.Text = openFileDialog.FileName;
-
excelpath = openFileDialog.FileName;
-
}
-
}
-
}
-
// 自动给本月寿星发邮件通知
-
privatevoid btnSendEmail_Click(object sender, EventArgs e)
-
{
-
if (!File.Exists(txtExcelPath.Text))
-
{
-
MessageBox.Show("员工表路径不存在,请确保输入正确的文件路径");
-
return;
-
}
-
if (txbBirthday.Text.Trim() == string.Empty || txbEmail.Text.Trim() == string.Empty)
-
{
-
MessageBox.Show("请先输入员工表中生日信息所在的列和邮箱信息所在的列!");
-
return;
-
}
-
// 输入信息都正确时开始发送邮件
-
SendEmail(int.Parse(txbBirthday.Text.Trim()), int.Parse(txbEmail.Text.Trim()));
-
}
-
// 发送邮件方法
-
privatevoid SendEmail(int birthDayColumn,int emailColumn)
-
{
-
// 获得当前月份
-
int nowmonth = DateTime.Now.Month;
-
// 发送邮件地址字符串
-
string toEmailString = string.Empty;
-
string emailBody="请收到邮件的员工,请本月28号到休闲室来参加生日Party";
-
Excel.Application excelApp = null;
-
Excel.Workbook workbook =null;
-
Excel.Worksheet worksheet = null;
-
Excel.Range range = null;
-
try
-
{
-
// 新建Excel应用程序被设置它不可见
-
excelApp = new Excel.Application();
-
excelApp.Visible = false;
-
workbook = excelApp.Workbooks.Open(excelpath);
-
// 获得打开文件的激活表格
-
worksheet= workbook.ActiveSheet;
-
// 遍历表格中的所有行
-
for (int row = 2; row < worksheet.UsedRange.Rows.Count + 1; row++)
-
{
-
// 因为我的测试表格中第四列是生日信息,在Excel中第一行的下标是从1开始的
-
// 这里本来需要在页面设置一个文本框让用户填写生日信息是在那一列的
-
// 这里为了测试就直接在程序中指定
-
// 下面的Range就代表生日列中每一个单元格
-
range = worksheet.Cells[row, birthDayColumn];
-
// 我们可以通过Range.Value来获得单元格中的生日信息
-
// 因为我生日单元格中为日期格式,所以获得的是日期类型,所以直接通过Month属性来获得月份
-
int month = range.Value.Month;
-
// 如果我们的Excel文档中生日时间设置为文本格式的话,这时候就需要通过分割字符串的方式来获得月份
-
// 通过Split函数来把生日信息以'/'符号分隔,分隔的数组的第二个就是月份
-
//int month = Int32.Parse(birthday.Split('/')[1]);
-
// 如果月份等于当前月的话,就给这个人发邮件
-
if (month == nowmonth)
-
{
-
// 获得本月生日员工的邮件地址
-
toEmailString += ";" + ((Excel.Range)worksheet.Cells[row, emailColumn]).Value;
-
}
-
}
-
}
-
catch (Exception ex)
-
{
-
MessageBox.Show("读取员工表格时出错,异常信息为:" + ex.Message);
-
return;
-
}
-
finally
-
{
-
workbook.Close(Excel.XlSaveAction.xlDoNotSaveChanges);
-
excelApp.Quit();
-
if (workbook != null)
-
{
-
Marshal.FinalReleaseComObject(workbook);
-
workbook = null;
-
}
-
if (excelApp != null)
-
{
-
Marshal.FinalReleaseComObject(excelApp);
-
excelApp = null;
-
}
-
}
-
if (CreateEmailItem("生日提醒", toEmailString, emailBody))
-
{
-
MessageBox.Show("成功给本月寿星发送邮件提醒");
-
}
-
}
-
// 创建邮件项
-
privatebool CreateEmailItem(string subjectEmail,string toEmail,string bodyEmail)
-
{
-
Outlook.Application outlookapp = null;
-
Outlook.MailItem email =null;
-
try
-
{
-
// 创建邮件项,就如你手动点新建邮件一样
-
outlookapp = new Outlook.Application();
-
email = outlookapp.CreateItem(Outlook.OlItemType.olMailItem);
-
// 指定邮件的主题,收件人和内容,就如你在新建邮件窗体中输入收件人,主题和内容一样
-
email.Subject = subjectEmail;
-
email.To = toEmail;
-
email.Body = bodyEmail;
-
email.Importance = Outlook.OlImportance.olImportanceHigh;
-
// 发送邮件,就如你点界面上的发送邮件操作一样
-
((Outlook._MailItem)email).Send();
-
}
-
catch(Exception ex)
-
{
-
MessageBox.Show("发送邮件的时候失败,异常信息为:" + ex.Message);
-
returnfalse;
-
}
-
finally
-
{
-
// 释放资源
-
((Outlook._Application)outlookapp).Quit();
-
if (email != null)
-
{
-
Marshal.FinalReleaseComObject(email);
-
email = null;
-
}
-
if (outlookapp != null)
-
{
-
Marshal.FinalReleaseComObject(outlookapp);
-
outlookapp = null;
-
}
-
}
-
returntrue;
-
}
为了测试程序,我新建了一个员工信息表,表格的格式如下(你当然可以根据自己的需要更改格式):
现在就让我们看看该程序的运行效果:
四、 小结
到这里,本专题的内容就和大家介绍完了,在下一个专题中将向大家介绍下如何通过Office提供的API的来遥控幻灯片。如果大家对本专题中两个工具的实现源码有任何的疑问,都可以在下面留言给我。觉得不错的话,帮忙赞下,感谢大家的支持。