实现Excel合并工具功能
- 添加EPPlus NuGet包用于Excel文件处理 - 设计完整的UI界面(文件夹选择、按钮、进度条、日志显示) - 实现Excel文件合并核心逻辑,支持多个xlsx文件合并 - 添加错误处理和进度跟踪功能 - 提供用户友好的操作界面和提示信息 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
7abace1d8a
commit
1dffb81325
135
WinFormsApp1/Form1.Designer.cs
generated
135
WinFormsApp1/Form1.Designer.cs
generated
@ -29,11 +29,142 @@
|
||||
private void InitializeComponent()
|
||||
{
|
||||
components = new System.ComponentModel.Container();
|
||||
|
||||
// 主窗体设置
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(800, 450);
|
||||
Text = "Form1";
|
||||
ClientSize = new Size(600, 400);
|
||||
Text = "Excel合并工具";
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
|
||||
// 创建控件
|
||||
grpSourceFolder = new GroupBox();
|
||||
lblSourcePath = new Label();
|
||||
txtSourcePath = new TextBox();
|
||||
btnBrowseSource = new Button();
|
||||
|
||||
grpOutputFolder = new GroupBox();
|
||||
lblOutputPath = new Label();
|
||||
txtOutputPath = new TextBox();
|
||||
btnBrowseOutput = new Button();
|
||||
|
||||
pnlButtons = new Panel();
|
||||
btnMerge = new Button();
|
||||
btnClear = new Button();
|
||||
btnExit = new Button();
|
||||
|
||||
grpProgress = new GroupBox();
|
||||
progressBar = new ProgressBar();
|
||||
txtLog = new TextBox();
|
||||
|
||||
// 设置源文件夹组
|
||||
grpSourceFolder.Text = "源文件夹";
|
||||
grpSourceFolder.Location = new Point(10, 10);
|
||||
grpSourceFolder.Size = new Size(580, 60);
|
||||
grpSourceFolder.TabStop = false;
|
||||
|
||||
lblSourcePath.Text = "路径:";
|
||||
lblSourcePath.Location = new Point(10, 25);
|
||||
lblSourcePath.Size = new Size(30, 23);
|
||||
|
||||
txtSourcePath.Location = new Point(45, 22);
|
||||
txtSourcePath.Size = new Size(470, 23);
|
||||
|
||||
btnBrowseSource.Text = "浏览";
|
||||
btnBrowseSource.Location = new Point(520, 20);
|
||||
btnBrowseSource.Size = new Size(50, 25);
|
||||
btnBrowseSource.Click += new EventHandler(btnBrowseSource_Click);
|
||||
|
||||
grpSourceFolder.Controls.Add(lblSourcePath);
|
||||
grpSourceFolder.Controls.Add(txtSourcePath);
|
||||
grpSourceFolder.Controls.Add(btnBrowseSource);
|
||||
|
||||
// 设置输出文件夹组
|
||||
grpOutputFolder.Text = "输出文件夹";
|
||||
grpOutputFolder.Location = new Point(10, 80);
|
||||
grpOutputFolder.Size = new Size(580, 60);
|
||||
grpOutputFolder.TabStop = false;
|
||||
|
||||
lblOutputPath.Text = "路径:";
|
||||
lblOutputPath.Location = new Point(10, 25);
|
||||
lblOutputPath.Size = new Size(30, 23);
|
||||
|
||||
txtOutputPath.Location = new Point(45, 22);
|
||||
txtOutputPath.Size = new Size(470, 23);
|
||||
|
||||
btnBrowseOutput.Text = "浏览";
|
||||
btnBrowseOutput.Location = new Point(520, 20);
|
||||
btnBrowseOutput.Size = new Size(50, 25);
|
||||
btnBrowseOutput.Click += new EventHandler(btnBrowseOutput_Click);
|
||||
|
||||
grpOutputFolder.Controls.Add(lblOutputPath);
|
||||
grpOutputFolder.Controls.Add(txtOutputPath);
|
||||
grpOutputFolder.Controls.Add(btnBrowseOutput);
|
||||
|
||||
// 设置按钮面板
|
||||
pnlButtons.Location = new Point(10, 150);
|
||||
pnlButtons.Size = new Size(580, 40);
|
||||
|
||||
btnMerge.Text = "合并";
|
||||
btnMerge.Location = new Point(10, 8);
|
||||
btnMerge.Size = new Size(75, 25);
|
||||
btnMerge.Click += new EventHandler(btnMerge_Click);
|
||||
|
||||
btnClear.Text = "清空";
|
||||
btnClear.Location = new Point(100, 8);
|
||||
btnClear.Size = new Size(75, 25);
|
||||
btnClear.Click += new EventHandler(btnClear_Click);
|
||||
|
||||
btnExit.Text = "退出";
|
||||
btnExit.Location = new Point(190, 8);
|
||||
btnExit.Size = new Size(75, 25);
|
||||
btnExit.Click += new EventHandler(btnExit_Click);
|
||||
|
||||
pnlButtons.Controls.Add(btnMerge);
|
||||
pnlButtons.Controls.Add(btnClear);
|
||||
pnlButtons.Controls.Add(btnExit);
|
||||
|
||||
// 设置进度组
|
||||
grpProgress.Text = "进度";
|
||||
grpProgress.Location = new Point(10, 200);
|
||||
grpProgress.Size = new Size(580, 180);
|
||||
grpProgress.TabStop = false;
|
||||
|
||||
progressBar.Location = new Point(10, 25);
|
||||
progressBar.Size = new Size(560, 23);
|
||||
progressBar.Style = ProgressBarStyle.Continuous;
|
||||
|
||||
txtLog.Location = new Point(10, 55);
|
||||
txtLog.Size = new Size(560, 115);
|
||||
txtLog.Multiline = true;
|
||||
txtLog.ScrollBars = ScrollBars.Vertical;
|
||||
txtLog.ReadOnly = true;
|
||||
|
||||
grpProgress.Controls.Add(progressBar);
|
||||
grpProgress.Controls.Add(txtLog);
|
||||
|
||||
// 添加所有控件到窗体
|
||||
Controls.Add(grpSourceFolder);
|
||||
Controls.Add(grpOutputFolder);
|
||||
Controls.Add(pnlButtons);
|
||||
Controls.Add(grpProgress);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private GroupBox grpSourceFolder;
|
||||
private Label lblSourcePath;
|
||||
private TextBox txtSourcePath;
|
||||
private Button btnBrowseSource;
|
||||
private GroupBox grpOutputFolder;
|
||||
private Label lblOutputPath;
|
||||
private TextBox txtOutputPath;
|
||||
private Button btnBrowseOutput;
|
||||
private Panel pnlButtons;
|
||||
private Button btnMerge;
|
||||
private Button btnClear;
|
||||
private Button btnExit;
|
||||
private GroupBox grpProgress;
|
||||
private ProgressBar progressBar;
|
||||
private TextBox txtLog;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,5 +6,244 @@ namespace WinFormsApp1
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void btnBrowseSource_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
|
||||
{
|
||||
dialog.Description = "选择Excel文件夹";
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
txtSourcePath.Text = dialog.SelectedPath;
|
||||
txtOutputPath.Text = dialog.SelectedPath; // 默认输出到源文件夹
|
||||
Log($"源文件夹: {dialog.SelectedPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnBrowseOutput_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
|
||||
{
|
||||
dialog.Description = "选择输出文件夹";
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
txtOutputPath.Text = dialog.SelectedPath;
|
||||
Log($"输出文件夹: {dialog.SelectedPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnClear_Click(object sender, EventArgs e)
|
||||
{
|
||||
txtSourcePath.Clear();
|
||||
txtOutputPath.Clear();
|
||||
txtLog.Clear();
|
||||
progressBar.Value = 0;
|
||||
}
|
||||
|
||||
private void btnExit_Click(object sender, EventArgs e)
|
||||
{
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private void btnMerge_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 前置校验
|
||||
if (string.IsNullOrEmpty(txtSourcePath.Text) || string.IsNullOrEmpty(txtOutputPath.Text))
|
||||
{
|
||||
MessageBox.Show("请选择源文件夹和输出文件夹", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
MergeExcel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"合并过程中发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
Log($"合并失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void MergeExcel()
|
||||
{
|
||||
var sourceDir = txtSourcePath.Text;
|
||||
var outputDir = txtOutputPath.Text;
|
||||
var outputFile = Path.Combine(outputDir, "合并结果.xlsx");
|
||||
|
||||
if (!Directory.Exists(sourceDir))
|
||||
{
|
||||
MessageBox.Show("源文件夹不存在", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找xlsx文件(排除合并结果本身)
|
||||
var xlsxFiles = Directory.GetFiles(sourceDir, "*.xlsx")
|
||||
.Where(f => !Path.GetFileName(f).Equals("合并结果.xlsx", StringComparison.OrdinalIgnoreCase))
|
||||
.ToArray();
|
||||
|
||||
if (xlsxFiles.Length == 0)
|
||||
{
|
||||
MessageBox.Show("未找到待合并的Excel文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
Log($"找到 {xlsxFiles.Length} 个文件,开始合并...");
|
||||
progressBar.Maximum = xlsxFiles.Length;
|
||||
progressBar.Value = 0;
|
||||
|
||||
// 设置EPPlus许可证上下文
|
||||
OfficeOpenXml.ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
|
||||
|
||||
var allData = new List<System.Data.DataTable>();
|
||||
var headers = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
// 处理每个文件
|
||||
for (int idx = 0; idx < xlsxFiles.Length; idx++)
|
||||
{
|
||||
var file = xlsxFiles[idx];
|
||||
var fileName = Path.GetFileName(file);
|
||||
|
||||
Log($"处理: {fileName}");
|
||||
|
||||
try
|
||||
{
|
||||
using (var package = new OfficeOpenXml.ExcelPackage(new FileInfo(file)))
|
||||
{
|
||||
if (package.Workbook.Worksheets.Count == 0)
|
||||
{
|
||||
Log($"跳过空文件: {fileName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var worksheet = package.Workbook.Worksheets[0]; // 使用第一个工作表
|
||||
var table = new System.Data.DataTable();
|
||||
|
||||
// 获取表头
|
||||
if (headers.Count == 0)
|
||||
{
|
||||
var columnCount = worksheet.Dimension.End.Column;
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
var header = worksheet.Cells[1, col].Text?.Trim() ?? $"Column{col}";
|
||||
headers.Add(header);
|
||||
table.Columns.Add(header);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 创建相同结构的表
|
||||
foreach (var header in headers)
|
||||
{
|
||||
table.Columns.Add(header);
|
||||
}
|
||||
}
|
||||
|
||||
// 跳过表头行,从第二行开始读取数据
|
||||
var rowCount = worksheet.Dimension.End.Row;
|
||||
for (int row = 2; row <= rowCount; row++)
|
||||
{
|
||||
var dataRow = table.NewRow();
|
||||
var isEmpty = true;
|
||||
|
||||
for (int col = 0; col < headers.Count; col++)
|
||||
{
|
||||
var cellValue = worksheet.Cells[row, col + 1].Text;
|
||||
dataRow[col] = cellValue;
|
||||
if (!string.IsNullOrWhiteSpace(cellValue))
|
||||
{
|
||||
isEmpty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEmpty)
|
||||
{
|
||||
table.Rows.Add(dataRow);
|
||||
}
|
||||
}
|
||||
|
||||
if (table.Rows.Count > 0)
|
||||
{
|
||||
allData.Add(table);
|
||||
Log($"成功读取 {fileName}: {table.Rows.Count} 行数据");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"跳过无数据文件: {fileName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"处理失败: {fileName} - {ex.Message}");
|
||||
continue;
|
||||
}
|
||||
|
||||
progressBar.Value = idx + 1;
|
||||
Application.DoEvents();
|
||||
}
|
||||
|
||||
// 合并数据并保存
|
||||
if (allData.Count == 0)
|
||||
{
|
||||
MessageBox.Show("无有效数据可合并", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建合并后的Excel文件
|
||||
using (var outputPackage = new OfficeOpenXml.ExcelPackage())
|
||||
{
|
||||
var outputSheet = outputPackage.Workbook.Worksheets.Add("合并结果");
|
||||
|
||||
// 写入表头
|
||||
for (int col = 0; col < headers.Count; col++)
|
||||
{
|
||||
outputSheet.Cells[1, col + 1].Value = headers[col];
|
||||
}
|
||||
|
||||
int currentRow = 2;
|
||||
int totalRows = 0;
|
||||
|
||||
// 合并所有数据
|
||||
foreach (var table in allData)
|
||||
{
|
||||
foreach (System.Data.DataRow row in table.Rows)
|
||||
{
|
||||
for (int col = 0; col < headers.Count; col++)
|
||||
{
|
||||
outputSheet.Cells[currentRow, col + 1].Value = row[col];
|
||||
}
|
||||
currentRow++;
|
||||
totalRows++;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
outputPackage.SaveAs(new FileInfo(outputFile));
|
||||
|
||||
Log($"合并完成!文件路径: {outputFile}");
|
||||
Log($"总行数: {totalRows} | 总列数: {headers.Count}");
|
||||
|
||||
MessageBox.Show($"合并成功!\n文件:{outputFile}\n行数:{totalRows}",
|
||||
"完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"保存失败: {ex.Message}");
|
||||
MessageBox.Show($"保存文件失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void Log(string msg)
|
||||
{
|
||||
txtLog.AppendText($"{msg}\r\n");
|
||||
txtLog.ScrollToCaret();
|
||||
Application.DoEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,4 +8,8 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EPPlus" Version="7.5.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
x
Reference in New Issue
Block a user