65.9K
CodeProject 正在变化。 阅读更多。
Home

使用模板书签自动化 Microsoft Word 报表

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2018年5月31日

CPOL

7分钟阅读

viewsIcon

25482

downloadIcon

547

本文讨论了一种使用 Microsoft Word 模板中的书签自动生成报告的 C# 方法。

图 1 - 由模板生成的自动化报告示例

引言

本文讨论了一种使用 Microsoft Word 模板中的书签自动生成报告的 C# 方法。使用模板自动生成报告可以使您或客户包含自定义设计和信息。例如,可以将公司的标志设计放入模板页眉中。每个客户都可以拥有自己的模板报告样式。然而,某些信息可能需要放置在报告的特定位置,或者可能需要插入用户输入的信息,而这些信息仅在报告生成前才可知晓,这时就可以使用书签。

在上面的示例报告中,在模板中为测试日期、副标题、项目编号、测试方法、客户、工程师/技术员以及测试设置表中的信息设置了书签。可以在 Microsoft Word 中设计模板,并在所需位置插入书签。然后,报告制作应用程序可以读取模板中的书签,并将文本插入到书签位置。一些已知信息(如当前日期)可以自动插入,或者对于特定的用户信息,可以通过文本框对话框提示用户输入要为每个书签字段插入的文本。在 Word 中定义书签时,必须为其命名,这些名称可用作文本框标签以识别每个书签。

可以按照以下步骤将书签插入到 Microsoft Word 2013 模板中:

  • 将光标定位到文档中所需书签的位置
  • 选择“插入”选项卡
  • 从“链接”下拉菜单中选择“书签
  • 键入书签的名称(注意:名称中不允许有空格)
  • 单击 Add

(要显示书签:转到文件 > 选项 > 高级,然后在“显示文档内容”下选中“显示书签”)。

Using the Code

下面的代码从 Word 模板文档中读取书签,并以从上到下的顺序将它们返回到 List<string> 中。

using System.Linq; // For LINQ .OrderBy
using Microsoft.Office.Interop.Word; // For _Document, _Application, Bookmark

    //--------------------------------------------------------------------------
    // Returns ordered list of bookmarks found in doc.
    //--------------------------------------------------------------------------
    public static List<string> GetBookmarks(string fname) {
        _Application WDapp = new Microsoft.Office.Interop.Word.Application();
        _Document doc; // _ removes compiler ambiguity warning on Close()
        List<Bookmark> bmarks = new List<Bookmark>();

        try {
            doc = WDapp.Documents.Add(fname);

            // Get list as they come from Word (alphabetically)
            foreach (Bookmark b in doc.Bookmarks) {
                bmarks.Add(b);
            }
        }
        catch (Exception err) {
            MessageBox.Show("Error: " + err.Message, "GetBookmarks()", MessageBoxButtons.OK,
		MessageBoxIcon.Error);
            return null;
        }
     
        // Re-sort list in order of appearance
        bmarks = bmarks.OrderBy(b => b.Start).ToList(); // LINQ
      
        List<string> slBMarks = new List<string>();
        foreach (Bookmark b in bmarks) {
            slBMarks.Add(b.Name); // Accumulate bookmark names
        }

        try { // Crashes if already closed
            doc.Close(WdSaveOptions.wdDoNotSaveChanges);
            WDapp.Quit(WdSaveOptions.wdDoNotSaveChanges);
        }
        catch { }

        return slBMarks;
    }

模板文件名通过变量 fname 传入。从 Word 模板中读取书签并将其添加到 List<Bookmark> 对象中。由于 Word 返回的书签是按字母顺序排序的,因此 LINQ 语句会根据读取顺序(即在模板中找到的从上到下的顺序)重新排序书签。该方法以 List<string> 对象的形式返回排序后的书签名。一旦知道了书签名,就可以调用 InsertBookmarkText 类方法(如下所示)将文本插入到书签位置。

    //--------------------------------------------------------------------------
    // Inserts text at bookmark, returns true if didn't fail
    //--------------------------------------------------------------------------
    private bool InsertBookmarkText(string bookmarkName, string text) {
        try {
            if (_doc.Bookmarks.Exists(bookmarkName)) {
                _doc.Bookmarks.get_Item(bookmarkName).Range.Text = text;
                return true;
            }
        }
        catch (Exception ex) {
            #if DEBUG
            	MessageBox.Show(ex.Message, "InsertBookmarkText()", MessageBoxButtons.OK, 
				MessageBoxIcon.Error);
            #endif
        }

        return false; // Error return
    }

该方法以书签名和要在该书签位置插入的文本作为输入。该方法使用书签名在文档中定位书签。请注意,此处使用的 _doc 是一个 _Document 对象,与上面 GetBookmarks 中的对象相同,并且已在调用此方法之前由类构造函数初始化。这里的“catch”异常仅在 Debug 模式下显示消息,因为找不到书签通常不需要暂停应用程序来显示错误。例如,代码可以使用此方法搜索一些模板中存在但另一些模板中不存在的多个不同书签,并且仅为找到的书签插入文本。

对于必须提示输入的信息,可以生成一个对话框来提示用户输入要插入到报告中的每个书签的文本。

图 2 - 一个动态文本框对话框,可根据所选模板文件自动更新

上面的对话框设计了一个组合框,用于选择将用于报告的模板文件。由于每个模板可能包含不同的书签,因此对话框会为仅在其中找到的书签自动插入文本框。当从组合框中选择模板文件时,文本框会更新以仅显示该模板中找到的书签。它使用书签名作为文本框标签,以便用户可以识别其对应哪个书签项。利用“面板”控件作为所有文本框的容器,可以通过简单地使用面板控件的 Clear 方法轻松更新文本框。面板还可以锚定(属性:顶部、底部、左侧)到窗体,以便自动调整大小。

下面的 Dialog 方法(参见 CReportDlg 类)用于设置对话框。private 成员变量 _path_templatePath 会被保存供其他类方法使用。FilesInDir 方法(参见 CReportDlg 类)返回一个 List<string>,其中包含在指定文件夹中找到的匹配文件掩码的文件名,在本例中是模板文件,然后将这些文件名添加到组合框的下拉列表中。

    //--------------------------------------------------------------------------
    // Init. dialog
    //--------------------------------------------------------------------------
    public List<string> Dialog() {
        System.Windows.Forms.Cursor.Current = Cursors.WaitCursor;

        _path = System.Windows.Forms.Application.StartupPath + "\\";
        _templatePath = Path.Combine(_path, _templatePath); // Append def. filename
        string fname = Path.GetFileName(_templatePath);

        // Get template files for combo box
        List<string> tfiles = FilesInDir(_path, new string[] { "*.dotx" });
        if (tfiles != null) {
            foreach (string f in tfiles) {
                this.cbxTemplate.Items.Add(f);
            }
         
            if (fname != "")
                cbxTemplate.Text = fname; // Last one used
            else if (cbxTemplate.Items.Count > 0)
                cbxTemplate.Text = cbxTemplate.Items[0].ToString(); // Def. to 1st one
        }

        System.Windows.Forms.Cursor.Current = Cursors.Default;
        ShowDialog();

        return _lResults;
    }

下面的代码是组合框事件处理程序,用于响应用户更改为不同的模板文件。如上所述,使用面板作为文本框的容器(称为 pnlBookmarks),以便可以轻松释放所有先前的文本框(通过调用 Clear),并且在更改文本框时,面板可以固定到窗体以自动调整大小。从新模板中读取书签,并在 foreach 循环中为每个书签添加带有标签的文本框。调用 CMsgDlg.AddTextBox 将文本框添加到面板。面板控件传递给 CMsgDlg 构造函数,以便 AddTextBox 方法可以访问用于将文本框放置到其中的控件。有关更多信息,以及下载 CMsgDlg 类,请参阅 此 CodeProject 文章

标记为“检查之前的默认书签”的 foreach 循环会检查先前输入的文本框条目,以便为用户提供默认值以方便使用。通过查看用于保存书签名称的文本框标签来执行此检查。AddTextBox 方法存储传入的文本框标签(在本例中是书签名),并将其存储在文本框标签中以便识别。如果新的书签名与文本框标签匹配,则使用先前保存的文本框字符串作为默认输入。在 CReportDlg.btnOK_Click 函数中,书签名和用户输入的文本被合并到一个 string(用于返回给调用者),并用“|”字符分隔,以便稍后解析。这就是为什么执行 Split(‘|’) 来解析出书签名称的原因。检查文本框位置和最大宽度,以便对齐它们,最后调整对话框窗体的大小以正确适应文本框。

    //--------------------------------------------------------------------------
    // cbxTemplate change handler sets up textboxes for bookmarks.
    // A panel is used for bookmark controls for easier updating.
    //--------------------------------------------------------------------------
    private void cbxTemplate_SelectedIndexChanged(object sender, EventArgs e) {
        pnlBookmarks.Controls.Clear(); // Remove all prev.
        this.Size = new Size(_startformWidth, _startformHeight);

        // Add new bookmarks textboxes
        List<string> bms = CReport.GetBookmarks(Path.Combine(_path, this.cbxTemplate.Text));
        if (bms != null && bms.Count > 0) {
            string strPad = string.Format("{0:-30}\n ", " "); // Default empty string
            int maxlblRight = 0;
            foreach (string bookmarkName in bms) {
                string strDef = strPad;

                // Check for prev. bookmark defaults
                foreach (string bm_text in _lbmks) {
                    string[] str = bm_text.Split('|');
                    if (str[0] == bookmarkName) {
                        if (str[1] != "") strDef = str[1];
                        break;
                    }
                }

                Control tbx = new CMsgDlg(pnlBookmarks).AddTextBox(bookmarkName + ":", strDef);
                tbx.Text = tbx.Text.Trim(new char[] { ' ', '\n', '\r' }); // Trim \n's
                tbx.Name = bookmarkName; // Name for return ID
                Label lbl = (Label)tbx.Tag; // CMsgDlg stores tbx label object in Tag
                lbl.Left = 0; // Align labels
                if (maxlblRight < lbl.Right) maxlblRight = lbl.Right; // Save max.
                tbx.Left = lbl.Right;
                tbx.Width = this.Width - _margin * 2; // Make to form width
            }
            this.Width = pnlBookmarks.Width + pnlBookmarks.Margin.Right + _margin * 2;

            // Re-align all to max lbl.Right
            foreach (Control ctl in pnlBookmarks.Controls) {
                if (ctl is TextBox) ctl.Left = maxlblRight;
            }
        }

        this.Height = _startformHeight + pnlBookmarks.Height;
    }

在循环中调用 CMsgDlg.AddTextBox 方法,以添加相应数量的文本框及其标签,作为插入报告的书签信息的提示。其方法声明如下:

   public TextBox AddTextBox(string lblText, string tbxText) {...

第一个传入参数 lblText 将成为文本框的标签。此处使用书签名后跟一个冒号(“:”)。第二个参数 tbxText 传递给方法,用作文本框的默认文本。它也用于计算 textboxwidth 以及它应该有多少行(通过嵌入的“\n”数量)。在上面的用法中,默认的 textbox string strPad 被填充到 30 个字符,以提供更宽的 textbox 用于输入,并添加“\n”使 textbox 有两行。有关更多信息,以及下载 CMsgDlg 类,请参阅 此 CodeProject 文章

用户输入每个书签所需的文本并关闭对话框后,使用 InsertBookmarkText 方法(如上所述)插入文本 string。在循环中重复调用此方法,将每个文本 string 插入到其正确书签位置(有关更多详细信息,请参阅 CReport 方法)。

结论

可以看出,将书签与模板结合使用以自动化报告对于开发人员来说是一个有用的选项。本文讨论的方法有望为利用这些功能提供一个良好的起点。

© . All rights reserved.