WinForm 嵌入 WordExcel 实现方案

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. 嵌入技术

  • 使用 SetParent API 将 Office 应用程序窗口嵌入到 WinForm 面板中
  • 通过 MoveWindow 调整嵌入窗口的大小以适应容器
  • 使用 ActivateWindow 在切换选项卡时激活相应的文档

2. 功能实现

  • Word 功能
    • 创建/打开/保存 Word 文档
    • 打印文档
    • 关闭文档
  • Excel 功能
    • 创建/打开/保存 Excel 工作簿
    • 打印工作簿
    • 关闭工作簿
  • 通用功能
    • 选项卡切换
    • 状态显示
    • 错误处理

3. 资源管理

  • 使用 CleanupWordCleanupExcel 方法释放 COM 对象
  • 在窗体关闭时自动清理资源
  • 使用 try-catch 处理异常

使用说明

1. 系统要求

  • Windows 操作系统
  • Microsoft Office 2010 或更高版本
  • .NET Framework 4.8

2. 安装步骤

  1. 创建新的 WinForms 项目
  2. 添加对以下 COM 库的引用:
    • Microsoft Office 15.0 Object Library
    • Microsoft Word 15.0 Object Library
    • Microsoft Excel 15.0 Object Library
  3. 复制上述代码到项目中
  4. 编译并运行

3. 使用流程

  1. Word 操作

    • 点击"新建 Word"创建新文档
    • 点击"打开 Word"打开现有文档
    • 点击"保存 Word"保存文档
    • 点击"打印"打印文档
    • 点击"关闭"关闭文档
  2. Excel 操作

    • 点击"新建 Excel"创建工作簿
    • 点击"打开 Excel"打开现有工作簿
    • 点击"保存 Excel"保存工作簿
    • 点击"打印"打印工作簿
    • 点击"关闭"关闭工作簿
  3. 通用操作

    • 使用选项卡在 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 集成应用。

相关推荐
躺平的赶海人2 小时前
C# 客户端性能优化(一):CPU 利用率获取与监控
c#
沐知全栈开发2 小时前
SQLite 常用函数
开发语言
TE-茶叶蛋2 小时前
PHP入门指南
开发语言·php
lolo大魔王2 小时前
Go语言的循环语句、判断语句、通道选择语句
开发语言·算法·golang
551只玄猫4 小时前
【数学建模 matlab 实验报告12】聚类分析和判别分析
开发语言·数学建模·matlab·课程设计·聚类·实验报告
小陈工6 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
H Journey6 小时前
C++之 CMake、CMakeLists.txt、Makefile
开发语言·c++·makefile·cmake
rockey6279 小时前
AScript如何实现中文脚本引擎
c#·.net·script·eval·expression·function·动态脚本
lly20240610 小时前
C 标准库 - `<stdio.h>`
开发语言