入职公司接到的第一个项目,开发一个C#小程序给同事用。之前有实习生写过一版,被同事们吐槽慢不准且易崩。所以我的目标是,参考之前的代码,保证逻辑清晰和准确率的同时提升速度。
程序功能:比较两个版本xml配置文件内容差异。比较的是所有叶节点属性值的区别。属性值可多选。
程序输入:用户指定两个待比较文件。
程序输出:一个excel表格。用颜色标识“新增项”,“删除项”,“更改项”等。
xml文件长这样: 两个文件有各有增添删改的情况。
最终得到的Excel长这样:
目录
组成文件
1. Main_frm.cs:窗体文件。主界面,用户指定两个待比较的xml文件,选择待比较的属性。
2.Export.cs :窗体文件。导出界面,用于指定保存比较结果的Excel路径,显示导出进度,以及结束按钮。
3. readXmlToDt.cs:类文件。用于将xml文件读到一个dataTable中。
4.compareDt.cs:类文件。在两个dataTable所有列名相同的情况下,比较相同ID行的属性值是否相同。
5. exportDtToExcel.cs:类文件。将dataTable输出到Excel文件中。
6.colorExcel.cs:类文件。用C#在Excel文件中创建宏(VB),应用此宏来标识特定条件的文本。
组成文件
1. Main_frm.cs:窗体文件。主界面,用户指定两个待比较的xml文件,选择待比较的属性。
主界面读取三个值,分别为路径A、路径B、被选属性(以字符串的形式读入)
public static string pathA = "";//用来存路径A
public static string pathB = "";//用来存路径B
public static string attrSelected = "";//用来存放被选择的EC属性
选择文件A: 应用知识点 打开文件对话框OpenFileDialog
MessageBox.Show 方法
private void btnSelectFileA_Click(object sender, EventArgs e)
{//选择待比较文件A
OpenFileDialog fileAPath = new OpenFileDialog();
fileAPath.Title = "选择文件A";
fileAPath.Filter = "xml文件(*.xml)|*.xml";//过滤文件类型只能为xml
fileAPath.RestoreDirectory = false;//每次打开位置与上一次相同
if (fileAPath.ShowDialog() == DialogResult.OK)
{
txtFileAPath.Text = System.IO.Path.GetFullPath(fileAPath.FileName);
pathA = txtFileAPath.Text;//将路径A保存
if (pathA == pathB)
{//如果与另一路径重复,将清空
MessageBox.Show("与FileB重复!请重新选择", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
pathA = "";
txtFileAPath.Text = "";
}
}
}
选择文件B,同上。
点击继续按钮,将被选择的属性值和路径AB保存。此处将属性存入一个字符串,后面再分割字符串。
private void btnGoon_Click(object sender, EventArgs e)
{ //选择属性Attr
attrSelected = (chkattr1.Checked ? "attr1 " : "") +
(chkattr2.Checked ? "attr2 " : "") +
(chkattr3.Checked ? "attr3 " : "") +
(chkattr4.Checked ? "attr4 " : "") +
(chkattr5.Checked ? "attr5 " : "") +
(chkattr6.Checked ? "attr6 " : "") +
(chkattr7.Checked ? "attr7 " : "") +
(chkattr8.Checked ? "attr8 " : "");
//点击Goon开启另一个窗口
//点击Goon将两个绝对路径pathA和pathB保存
//点击Goon将属性们attrSelected保存
if (attrSelected == "")
{//如果属性未选择
MessageBox.Show("请选择属性!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{//如果有路径未选择
if (pathA == "" || pathB == "")
{
MessageBox.Show("请确认文件选择是否正确!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
Export result_frm = new Export();//开启Export界面
result_frm.StartPosition = FormStartPosition.CenterScreen;//开始在屏幕中央
result_frm.Show();
}
}
}
2.Export.cs :窗体文件。导出界面,用于指定保存比较结果的Excel路径,显示导出进度,以及结束按钮。
点击按钮选择比较结果的保存路径:
string strsavePath = "";
private void btnSelectSavePath_Click(object sender, EventArgs e)
{//选择保存路径,并显示在textbox中
SaveFileDialog savePath = new SaveFileDialog();
savePath.Title = "保存文件";
savePath.Filter = "xls文件(*.xls)|*.xls";
savePath.RestoreDirectory = false;
if (savePath.ShowDialog() == DialogResult.OK)
{
strsavePath = savePath.FileName;
}
else
{
strsavePath = "";
}
txtSavePath.Text = strsavePath;
}
如果文件路径被用户手动更改,需要重新读入:
private void txtSavePath_TextChanged(object sender, EventArgs e)
{
strsavePath = txtSavePath.Text;
prgSave.Value = 0;
lblExport.Visible = false;
picsSuccess.Visible = false;
}
点击导出按钮,开始读入,比较,保存,显示。
步骤1:检测文件是否被打开。如果保存路径的文件已经存在并被打开,则不能写入结果,因此要检测此文件是否被占用。
思路是,使用文件流查看此文件是否可以被打开,若可以被打开说明没有被占用。
using System.IO;
//检查Excel文件是否已被打开
if (File.Exists(strsavePath))
{
bool op = IsFileInUse(strsavePath);
if (op)
{
MessageBox.Show("您要保存的文件被占用!\n请关闭文件或重新选择路径");
return;
}
}
public static bool IsFileInUse(string fileName)
{//检测文件是否被占用
bool inUse = true;
FileStream fs = null;
try
{
fs = new FileStream(fileName, FileMode.Open, FileAccess.Read,
FileShare.None);
inUse = false;
}
catch
{
}
finally
{
if (fs != null)
fs.Close();
}
return inUse;//true表示正在使用,false没有使用
}
步骤2:读AB文件为dtA和dtB
//读A文件
readXmlToDt readxmlA = new readXmlToDt();
DataTable dtA = new DataTable();
dtA = readxmlA.read_forList(Main_frm.pathA);
//读B文件
readXmlToDt readxmlB = new readXmlToDt();
DataTable dtB = new DataTable();
dtB = readxmlB.read_forList(Main_frm.pathB);
步骤3:比较dtA和dtB,将结果存入dtC
//比较AB,得到结果
compareDt resultDt = new compareDt();
DataTable dtC = new DataTable();
dtC=resultDt.compare(dtA, dtB, Main_frm.attrSelected);
步骤4:将dtC导出到Excel文件并存入指定路径(此时并未上色)
//导出Excel
exportDtToExcel ex = new exportDtToExcel();
ex.dtToExcel(dtC, strsavePath);
步骤5:执行宏(上色)。第一次启用程序之前需要在Excel中勾选对VBA开发的信任选项,否则无法启用宏。所以在这里使用了一个抛出异常,如果不能正确启用宏,给出提示信息。
//执行宏
try
{
colorExcel ce = new colorExcel();
ce.colorExc(strsavePath);
}
catch
{
MessageBox.Show("您未开启宏信任,请在excel→文件→选项→信任中心→信任中心设置→宏设置→勾选“信任对VBA工程对象模型的访问”,然后重启程序", "提示信息",MessageBoxButtons.OK, MessageBoxIcon.Error);
}
函数整体代码如下: 这里有一点偷懒,进度条的值在各个阶段直接赋值了,以后有时间研究一下如何使用进度条。
#region 主要处理过程
private void btnSave_Click(object sender, EventArgs e)
{
if (strsavePath == "")
{//如果保存路径为空
MessageBox.Show("请选择要保存的路径!", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
//检查文件是否已被打开
if (File.Exists(strsavePath))
{
bool op = IsFileInUse(strsavePath);
if (op)
{
MessageBox.Show("您要保存的文件被占用!\n请关闭文件或重新选择路径");
return;
}
}
prgSave.Value = 0;
if (lblExport.Visible == false) { lblExport.Visible = true; }//如果导出的标志不可见,将它可见
if (picsSuccess.Visible == true) { picsSuccess.Visible = false; }//如果成功的标志可见,将它不可见
prgSave.Value = 10;
//读A文件
readXmlToDt readxmlA = new readXmlToDt();
DataTable dtA = new DataTable();
dtA = readxmlA.read_forList(Main_frm.pathA);
//读B文件
readXmlToDt readxmlB = new readXmlToDt();
DataTable dtB = new DataTable();
dtB = readxmlB.read_forList(Main_frm.pathB);
prgSave.Value = 30;
//比较AB,得到结果
compareDt resultDt = new compareDt();
DataTable dtC = new DataTable();
dtC=resultDt.compare(dtA, dtB, Main_frm.attrSelected);
prgSave.Value = 90;
//导出Excel
exportDtToExcel ex = new exportDtToExcel();
ex.dtToExcel(dtC, strsavePath);
//进度条、进度标签、成功标志
prgSave.Value = 100;
lblExport.Visible = false;
picsSuccess.Visible = true;
//执行宏
try
{
colorExcel ce = new colorExcel();
ce.colorExc(strsavePath);
}
catch
{
MessageBox.Show("您未开启宏信任,请在excel→文件→选项→信任中心→信任中心设置→宏设置→勾选“信任对VBA工程对象模型的访问”,然后重启程序", "提示信息",MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
public static bool IsFileInUse(string fileName)
{//检测Excel文件是否被打开
bool inUse = true;
FileStream fs = null;
try
{
fs = new FileStream(fileName, FileMode.Open, FileAccess.Read,
FileShare.None);
inUse = false;
}
catch
{
}
finally
{
if (fs != null)
fs.Close();
}
return inUse;//true表示正在使用,false没有使用
}
Excel文件保存后,再打开启用宏指令的文件其实是一个副本,用户如果选择将这个带颜色的文件保存那么原始的Excel文件就没有用了,因此点击完成图片后将它删除并关闭程序。这里如果不点击完成而是直接关掉程序,那么原始Excel文件就不会被删除。
private void picsSuccess_Click(object sender, EventArgs e)
{
Application.Exit();
File.Delete(strsavePath);//删除文件
}