WinForm 应用程序,演示如何嵌入 Word 和 Excel 文档,并实现基本的查看、编辑和保存功能。
代码
1. 主窗体 (MainForm.cs)
csharp
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Microsoft.Office.Interop.Word;
using Microsoft.Office.Interop.Excel;
using Application = Microsoft.Office.Interop.Word.Application;
using ExcelApplication = Microsoft.Office.Interop.Excel.Application;
namespace OfficeEmbedder
{
public partial class MainForm : Form
{
private Application _wordApp;
private Document _wordDoc;
private ExcelApplication _excelApp;
private Workbook _excelBook;
private Worksheet _excelSheet;
private bool _isWordEmbedded = false;
private bool _isExcelEmbedded = false;
private string _currentFilePath = "";
public MainForm()
{
InitializeComponent();
InitializeUI();
}
private void InitializeUI()
{
// 主窗体设置
this.Text = "Office 文档嵌入器";
this.Size = new Size(1000, 700);
this.StartPosition = FormStartPosition.CenterScreen;
// 创建工具栏
var toolStrip = new ToolStrip();
toolStrip.Dock = DockStyle.Top;
toolStrip.Items.Add(new ToolStripButton("新建 Word", null, NewWord_Click));
toolStrip.Items.Add(new ToolStripButton("打开 Word", null, OpenWord_Click));
toolStrip.Items.Add(new ToolStripButton("保存 Word", null, SaveWord_Click));
toolStrip.Items.Add(new ToolStripSeparator());
toolStrip.Items.Add(new ToolStripButton("新建 Excel", null, NewExcel_Click));
toolStrip.Items.Add(new ToolStripButton("打开 Excel", null, OpenExcel_Click));
toolStrip.Items.Add(new ToolStripButton("保存 Excel", null, SaveExcel_Click));
toolStrip.Items.Add(new ToolStripSeparator());
toolStrip.Items.Add(new ToolStripButton("打印", null, Print_Click));
toolStrip.Items.Add(new ToolStripButton("关闭", null, CloseDocument_Click));
this.Controls.Add(toolStrip);
// 创建选项卡控件
tabControl = new TabControl();
tabControl.Dock = DockStyle.Fill;
tabControl.SelectedIndexChanged += TabControl_SelectedIndexChanged;
this.Controls.Add(tabControl);
// 创建 Word 页面
wordTabPage = new TabPage("Word 文档");
wordPanel = new Panel();
wordPanel.Dock = DockStyle.Fill;
wordTabPage.Controls.Add(wordPanel);
tabControl.TabPages.Add(wordTabPage);
// 创建 Excel 页面
excelTabPage = new TabPage("Excel 表格");
excelPanel = new Panel();
excelPanel.Dock = DockStyle.Fill;
excelTabPage.Controls.Add(excelPanel);
tabControl.TabPages.Add(excelTabPage);
// 状态栏
statusStrip = new StatusStrip();
statusLabel = new ToolStripStatusLabel();
statusLabel.Text = "就绪";
statusStrip.Items.Add(statusLabel);
this.Controls.Add(statusStrip);
}
#region Word 功能实现
private void EmbedWordDocument(string filePath = null)
{
try
{
// 清理现有实例
CleanupWord();
// 创建 Word 应用程序
_wordApp = new Application();
_wordApp.Visible = false;
_wordApp.WindowState = WdWindowState.wdWindowStateNormal;
_wordApp.DisplayAlerts = WdAlertLevel.wdAlertsNone;
// 创建或打开文档
if (string.IsNullOrEmpty(filePath))
{
_wordDoc = _wordApp.Documents.Add();
_currentFilePath = Path.Combine(Path.GetTempPath(), $"新建文档_{DateTime.Now:yyyyMMddHHmmss}.docx");
}
else
{
_wordDoc = _wordApp.Documents.Open(filePath);
_currentFilePath = filePath;
}
// 嵌入到 WinForms
var window = _wordApp.ActiveWindow;
var hwnd = new IntPtr(window.Hwnd);
var parent = wordPanel.Handle;
SetParent(hwnd, parent);
MoveWindow(hwnd, 0, 0, wordPanel.Width, wordPanel.Height, true);
_isWordEmbedded = true;
statusLabel.Text = $"Word 文档已加载: {Path.GetFileName(_currentFilePath)}";
}
catch (Exception ex)
{
MessageBox.Show($"嵌入 Word 文档时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
CleanupWord();
}
}
private void CleanupWord()
{
if (_wordDoc != null)
{
_wordDoc.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(_wordDoc);
_wordDoc = null;
}
if (_wordApp != null)
{
_wordApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(_wordApp);
_wordApp = null;
}
_isWordEmbedded = false;
}
private void NewWord_Click(object sender, EventArgs e)
{
tabControl.SelectedTab = wordTabPage;
EmbedWordDocument();
}
private void OpenWord_Click(object sender, EventArgs e)
{
using (var openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "Word 文档|*.doc;*.docx";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
tabControl.SelectedTab = wordTabPage;
EmbedWordDocument(openFileDialog.FileName);
}
}
}
private void SaveWord_Click(object sender, EventArgs e)
{
if (!_isWordEmbedded || _wordDoc == null)
{
MessageBox.Show("没有活动的 Word 文档", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
if (string.IsNullOrEmpty(_currentFilePath) || !File.Exists(_currentFilePath))
{
using (var saveFileDialog = new SaveFileDialog())
{
saveFileDialog.Filter = "Word 文档|*.docx";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
_currentFilePath = saveFileDialog.FileName;
}
else
{
return;
}
}
}
_wordDoc.SaveAs2(_currentFilePath);
statusLabel.Text = $"文档已保存: {Path.GetFileName(_currentFilePath)}";
}
catch (Exception ex)
{
MessageBox.Show($"保存文档时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
#region Excel 功能实现
private void EmbedExcelDocument(string filePath = null)
{
try
{
// 清理现有实例
CleanupExcel();
// 创建 Excel 应用程序
_excelApp = new ExcelApplication();
_excelApp.Visible = false;
_excelApp.DisplayAlerts = false;
_excelApp.ScreenUpdating = false;
// 创建或打开工作簿
if (string.IsNullOrEmpty(filePath))
{
_excelBook = _excelApp.Workbooks.Add();
_excelSheet = (Worksheet)_excelBook.Sheets[1];
_currentFilePath = Path.Combine(Path.GetTempPath(), $"新建工作簿_{DateTime.Now:yyyyMMddHHmmss}.xlsx");
}
else
{
_excelBook = _excelApp.Workbooks.Open(filePath);
_excelSheet = (Worksheet)_excelBook.Sheets[1];
_currentFilePath = filePath;
}
// 嵌入到 WinForms
var window = _excelApp.ActiveWindow;
var hwnd = new IntPtr(window.Hwnd);
var parent = excelPanel.Handle;
SetParent(hwnd, parent);
MoveWindow(hwnd, 0, 0, excelPanel.Width, excelPanel.Height, true);
_isExcelEmbedded = true;
statusLabel.Text = $"Excel 文档已加载: {Path.GetFileName(_currentFilePath)}";
}
catch (Exception ex)
{
MessageBox.Show($"嵌入 Excel 文档时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
CleanupExcel();
}
}
private void CleanupExcel()
{
if (_excelBook != null)
{
_excelBook.Close(false);
System.Runtime.InteropServices.Marshal.ReleaseComObject(_excelBook);
_excelBook = null;
}
if (_excelSheet != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(_excelSheet);
_excelSheet = null;
}
if (_excelApp != null)
{
_excelApp.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(_excelApp);
_excelApp = null;
}
_isExcelEmbedded = false;
}
private void NewExcel_Click(object sender, EventArgs e)
{
tabControl.SelectedTab = excelTabPage;
EmbedExcelDocument();
}
private void OpenExcel_Click(object sender, EventArgs e)
{
using (var openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "Excel 工作簿|*.xls;*.xlsx";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
tabControl.SelectedTab = excelTabPage;
EmbedExcelDocument(openFileDialog.FileName);
}
}
}
private void SaveExcel_Click(object sender, EventArgs e)
{
if (!_isExcelEmbedded || _excelBook == null)
{
MessageBox.Show("没有活动的 Excel 文档", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
if (string.IsNullOrEmpty(_currentFilePath) || !File.Exists(_currentFilePath))
{
using (var saveFileDialog = new SaveFileDialog())
{
saveFileDialog.Filter = "Excel 工作簿|*.xlsx";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
_currentFilePath = saveFileDialog.FileName;
}
else
{
return;
}
}
}
_excelBook.SaveAs(_currentFilePath);
statusLabel.Text = $"工作簿已保存: {Path.GetFileName(_currentFilePath)}";
}
catch (Exception ex)
{
MessageBox.Show($"保存工作簿时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
#region 通用功能
private void Print_Click(object sender, EventArgs e)
{
if (tabControl.SelectedTab == wordTabPage && _isWordEmbedded)
{
_wordDoc.PrintOut();
}
else if (tabControl.SelectedTab == excelTabPage && _isExcelEmbedded)
{
_excelApp.ActiveWorkbook.PrintOut();
}
else
{
MessageBox.Show("没有活动的文档", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
private void CloseDocument_Click(object sender, EventArgs e)
{
if (tabControl.SelectedTab == wordTabPage)
{
CleanupWord();
wordPanel.Controls.Clear();
statusLabel.Text = "Word 文档已关闭";
}
else if (tabControl.SelectedTab == excelTabPage)
{
CleanupExcel();
excelPanel.Controls.Clear();
statusLabel.Text = "Excel 文档已关闭";
}
}
private void TabControl_SelectedIndexChanged(object sender, EventArgs e)
{
// 切换选项卡时激活相应的文档
if (tabControl.SelectedTab == wordTabPage && _isWordEmbedded)
{
ActivateWindow(_wordApp.ActiveWindow.Hwnd);
}
else if (tabControl.SelectedTab == excelTabPage && _isExcelEmbedded)
{
ActivateWindow(_excelApp.ActiveWindow.Hwnd);
}
}
#endregion
#region P/Invoke 方法
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
private void ActivateWindow(IntPtr hWnd)
{
if (IsWindowVisible(hWnd))
{
SetForegroundWindow(hWnd);
}
}
#endregion
#region 窗体设计器代码
private TabControl tabControl;
private TabPage wordTabPage;
private TabPage excelTabPage;
private Panel wordPanel;
private Panel excelPanel;
private StatusStrip statusStrip;
private ToolStripStatusLabel statusLabel;
private void InitializeComponent()
{
this.SuspendLayout();
//
// MainForm
//
this.ClientSize = new System.Drawing.Size(984, 661);
this.Name = "MainForm";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
CleanupWord();
CleanupExcel();
}
}
}
2. 程序入口 (Program.cs)
csharp
using System;
using System.Windows.Forms;
namespace OfficeEmbedder
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
3. 项目文件 (OfficeEmbedder.csproj)
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<RootNamespace>OfficeEmbedder</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Office.Interop.Word, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C" />
<Reference Include="Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C" />
</ItemGroup>
</Project>
实现原理
1. 嵌入技术
- 使用
SetParentAPI 将 Office 应用程序窗口嵌入到 WinForm 面板中 - 通过
MoveWindow调整嵌入窗口的大小以适应容器 - 使用
ActivateWindow在切换选项卡时激活相应的文档
2. 功能实现
- Word 功能 :
- 创建/打开/保存 Word 文档
- 打印文档
- 关闭文档
- Excel 功能 :
- 创建/打开/保存 Excel 工作簿
- 打印工作簿
- 关闭工作簿
- 通用功能 :
- 选项卡切换
- 状态显示
- 错误处理
3. 资源管理
- 使用
CleanupWord和CleanupExcel方法释放 COM 对象 - 在窗体关闭时自动清理资源
- 使用
try-catch处理异常
使用说明
1. 系统要求
- Windows 操作系统
- Microsoft Office 2010 或更高版本
- .NET Framework 4.8
2. 安装步骤
- 创建新的 WinForms 项目
- 添加对以下 COM 库的引用:
- Microsoft Office 15.0 Object Library
- Microsoft Word 15.0 Object Library
- Microsoft Excel 15.0 Object Library
- 复制上述代码到项目中
- 编译并运行
3. 使用流程
-
Word 操作:
- 点击"新建 Word"创建新文档
- 点击"打开 Word"打开现有文档
- 点击"保存 Word"保存文档
- 点击"打印"打印文档
- 点击"关闭"关闭文档
-
Excel 操作:
- 点击"新建 Excel"创建工作簿
- 点击"打开 Excel"打开现有工作簿
- 点击"保存 Excel"保存工作簿
- 点击"打印"打印工作簿
- 点击"关闭"关闭工作簿
-
通用操作:
- 使用选项卡在 Word 和 Excel 之间切换
- 状态栏显示当前操作状态
参考代码 Winfrom 嵌入word、excel实现源码 www.youwenfan.com/contentcst/122421.html
功能扩展建议
1. 添加富文本编辑功能
csharp
// 在 Word 嵌入后添加文本
if (_isWordEmbedded)
{
_wordApp.Selection.TypeText("这是一段示例文本");
_wordApp.Selection.Font.Bold = 1;
_wordApp.Selection.Font.Size = 14;
}
2. 添加 Excel 数据操作
csharp
// 在 Excel 嵌入后添加数据
if (_isExcelEmbedded)
{
_excelSheet.Cells[1, 1] = "产品名称";
_excelSheet.Cells[1, 2] = "价格";
_excelSheet.Cells[2, 1] = "产品A";
_excelSheet.Cells[2, 2] = 100;
_excelSheet.Cells[3, 1] = "产品B";
_excelSheet.Cells[3, 2] = 200;
// 自动调整列宽
_excelSheet.Columns.AutoFit();
}
3. 添加文档比较功能
csharp
// 比较两个 Word 文档
private void CompareDocuments(string doc1Path, string doc2Path)
{
var app = new Application();
var doc1 = app.Documents.Open(doc1Path);
var doc2 = app.Documents.Open(doc2Path);
doc1.Compare(doc2Path, doc2.Name,
WdCompareDestination.wdCompareDestinationOriginal,
WdGranularity.wdGranularityWordLevel,
true, true, true, true, true);
app.Visible = true;
}
4. 添加数据导入导出功能
csharp
// 从 CSV 导入 Excel
private void ImportCsvToExcel(string csvPath)
{
if (_isExcelEmbedded)
{
var range = _excelSheet.Cells[1, 1];
range.QueryTable.Connection = $"TEXT;{csvPath}";
range.QueryTable.Refresh();
}
}
// 导出 Excel 到 CSV
private void ExportExcelToCsv(string csvPath)
{
if (_isExcelEmbedded)
{
_excelBook.SaveAs(csvPath, XlFileFormat.xlCSV);
}
}
常见问题解决
1. 嵌入后窗口显示异常
- 确保 Office 应用程序可见性设置为
false - 在调整窗口大小后调用
Application.DoEvents() - 使用
MoveWindow后添加短暂延迟
2. 内存泄漏问题
- 确保所有 COM 对象都正确释放
- 使用
System.Runtime.InteropServices.Marshal.ReleaseComObject() - 在窗体关闭时调用清理方法
3. 多文档处理
- 当前实现只支持单文档嵌入
- 如需多文档,可创建多个面板和对应的 Office 实例
- 使用文档管理类跟踪打开的文档
4. 64位与32位兼容性
- 确保项目平台目标与 Office 版本匹配
- 32位 Office 需要 x86 目标平台
- 64位 Office 需要 x64 目标平台
替代方案
1. 使用 WebBrowser 控件
csharp
// 使用 WebBrowser 显示 Office 文档
private void ShowInWebBrowser(string filePath)
{
webBrowser1.Navigate(filePath);
}
优点:简单,不需要 Office 安装
缺点:功能有限,仅支持查看
2. 使用第三方库
- Spire.Office:商业库,支持 Word/Excel 操作
- NPOI:开源库,支持 Office 文件格式
- EPPlus:开源库,专门处理 Excel 文件
3. 使用 Office Web Apps
- 通过 SharePoint 或 OneDrive 在线查看
- 使用 WebView2 控件嵌入 Web 内容
- 优点:跨平台,无需安装 Office
- 缺点:需要网络连接
总结
这个 WinForm 应用程序展示了如何将 Word 和 Excel 文档嵌入到 WinForm 界面中,实现基本的文档操作功能。通过使用 Windows API 和 Office Interop 技术,我们可以创建功能丰富的 Office 集成应用。