使用 Word.Interop 创建多级项目符号列表

2024-01-06

我需要通过创建多级项目符号列表Microsoft.Office.Interop.Word我目前正在(再次)与它的(可怕的)API 作斗争。

我刚刚使用编程语言 C# 在 Microsoft Office Word 2010 的 VSTO 文档级项目中创建了以下示例(还不是动态的,仅用于演示目的):

Word.Paragraph paragraph = null;
Word.Range range = this.Content;
paragraph = range.Paragraphs.Add();
paragraph.Range.Text = "Item 1";
paragraph.Range.ListFormat.ApplyBulletDefault(Word.WdDefaultListBehavior.wdWord10ListBehavior);
// ATTENTION: We have to outdent the paragraph AFTER its list format has been set, otherwise this has no effect.
// Without this, the the indent of "Item 2" differs from the indent of "Item 1".
paragraph.Outdent();

paragraph.Range.InsertParagraphAfter();

paragraph = range.Paragraphs.Add();
paragraph.Range.Text = "Item 1.1";
// ATTENTION: We have to indent the paragraph AFTER its text has been set, otherwise this has no effect.
paragraph.Indent();
paragraph.Range.InsertParagraphAfter();

paragraph = range.Paragraphs.Add();
paragraph.Range.Text = "Item 1.2";
paragraph.Range.InsertParagraphAfter();

paragraph = range.Paragraphs.Add();
paragraph.Range.Text = "Item 2";
paragraph.Outdent();

该代码完全符合我的要求(经过多次尝试和错误!),但在我看来它很糟糕。该格式必须在非常具体的点应用,我必须手动缩进和减少缩进创建的段落。

所以我的问题是:是否存在更好的方法来创建多级项目符号列表Word.Interop,例如通过我还没有发现的速记方法?

我的目标是从 XML 数据创建一个多级列表(更具体地说是CustomXMLNode object)

Stack Overflow 上还存在另外两个与项目符号列表相关的问题,但这两个问题都对我没有帮助(上面的源代码是第二个问题的答案):

  • Word 中的项目符号与 C# Interop https://stackoverflow.com/questions/11508286/bullet-points-in-word-with-c-sharp-interop
  • https://stackoverflow.com/questions/3768414/ms-word-list-with-sub-lists https://stackoverflow.com/questions/3768414/ms-word-list-with-sub-lists

编辑(2013-08-08):

我刚刚将一些东西组合在一起,输出两个数组作为具有两个级别的项目符号列表(带有子项的数组用于每个根项,以保持简单)。通过引入递归,人们将能够创建一个具有无限级别的项目符号列表(理论上)。但问题依然存在,代码一团糟......

string[] rootItems = new string[]
{
    "Root Item A", "Root Item B", "Root Item C"
};

string[] subItems = new string[]
{
    "Subitem A", "Subitem B"
};

Word.Paragraph paragraph = null;
Word.Range range = this.Content;
bool appliedListFormat = false;
bool indented = false;

for (int i = 0; i < rootItems.Length; ++i)
{
    paragraph = range.Paragraphs.Add();
    paragraph.Range.Text = rootItems[i];

    if (!appliedListFormat)
    {
        paragraph.Range.ListFormat.ApplyBulletDefault(Word.WdDefaultListBehavior.wdWord10ListBehavior);
        appliedListFormat = true;
    }

    paragraph.Outdent();
    paragraph.Range.InsertParagraphAfter();

    for (int j = 0; j < subItems.Length; ++j)
    {
        paragraph = range.Paragraphs.Add();
        paragraph.Range.Text = subItems[j];

        if (!indented)
        {
            paragraph.Indent();
            indented = true;
        }

        paragraph.Range.InsertParagraphAfter();
    }

    indented = false;
}

// Delete the last paragraph, since otherwise the list ends with an empty sub-item.
paragraph.Range.Delete();

编辑(2013-08-12):

上周五,我以为我已经实现了我想要的目标,但今天早上我注意到,我的解决方案仅在插入点位于文档末尾时才有效。我创建了以下简单示例来演示(错误)行为。总结我的问题:我可以在文档末尾创建多级项目符号列表only。一旦我更改当前选择(例如更改为文档的开头),列表就会被销毁。据我所知,这与(自动或非自动)扩展有关Range对象。到目前为止我已经尝试了很多(我几乎要失去它了),但这对我来说都是货物崇拜。我唯一想做的就是一个接一个地插入元素(是否无法创建内容控件inside一个段落,以便该段落的文本后面跟着内容控件?)and到任何Range of a Document。我将用我的实际情况在 GitHub 上创建一个 GistCustomXMLPart今晚绑定课程。最终有人可以帮助我解决这个麻烦的问题。

private void buttonTestStatic_Click(object sender, RibbonControlEventArgs e)
{
    Word.Range range = Globals.ThisDocument.Application.Selection.Range;
    Word.ListGallery listGallery = Globals.ThisDocument.Application.ListGalleries[Word.WdListGalleryType.wdBulletGallery];
    Word.Paragraph paragraph = null;
    Word.ListFormat listFormat = null;

    // TODO At the end of the document, the ranges are automatically expanded and inbetween not?

    paragraph = range.Paragraphs.Add();
    listFormat = paragraph.Range.ListFormat;
    paragraph.Range.Text = "Root Item A";
    this.ApplyListTemplate(listGallery, listFormat, 1);
    paragraph.Range.InsertParagraphAfter();

    paragraph = paragraph.Range.Paragraphs.Add();
    listFormat = paragraph.Range.ListFormat;
    paragraph.Range.Text = "Child Item A.1";
    this.ApplyListTemplate(listGallery, listFormat, 2);
    paragraph.Range.InsertParagraphAfter();

    paragraph = paragraph.Range.Paragraphs.Add();
    listFormat = paragraph.Range.ListFormat;
    paragraph.Range.Text = "Child Item A.2";
    this.ApplyListTemplate(listGallery, listFormat, 2);
    paragraph.Range.InsertParagraphAfter();

    paragraph = paragraph.Range.Paragraphs.Add();
    listFormat = paragraph.Range.ListFormat;
    paragraph.Range.Text = "Root Item B";
    this.ApplyListTemplate(listGallery, listFormat, 1);
    paragraph.Range.InsertParagraphAfter();
}

private void ApplyListTemplate(Word.ListGallery listGallery, Word.ListFormat listFormat, int level = 1)
{
    listFormat.ApplyListTemplateWithLevel(
        listGallery.ListTemplates[level],
        ContinuePreviousList: true,
        ApplyTo: Word.WdListApplyTo.wdListApplyToSelection,
        DefaultListBehavior: Word.WdDefaultListBehavior.wdWord10ListBehavior,
        ApplyLevel: level);
}

编辑(2013-08-12):我已经设置了 GitHub 存储库here https://github.com/FlorianWolters/CSharp-WordInterop-Temp这说明了我的问题Word.Range对象。这OnClickButton文件中的方法Ribbon.cs调用我的自定义映射器类。那里的评论描述了这个问题。我know我的问题与论证有关Word.Range对象引用,但我尝试的所有其他解决方案(例如修改类内部的范围)都更加失败。到目前为止我所实现的最佳解决方案是指定Document.Content范围作为参数MapToCustomControlsIn方法。这会将格式良好的多级项目符号列表(具有绑定到内容控件的自定义 XML 部分)插入到end该文件的。我想要的是插入该列表在自定义位置进入文档(例如当前选择通过Word.Selection.Range).


Florian Wolters 的例子差不多就到了,但是当我尝试时,第一个子项目编号总是不正确。

有人建议使用宏和 VBA 脚本然后转换为 C# 给了我灵感。

下面是在我这边测试过的示例代码。希望能帮助到你。

using Microsoft.Office.Interop.Word;
using System.Reflection;

namespace OfficeUtility
{
    public class NumberListGenerate
    {
        public void GenerateList()
        {
            Application app = null;
            Document doc = null;
            string filePath = "c:\\output.docx";
            string pdfPath = "c:\\export.pdf";

            try
            {
                app = new Application();
                app.Visible = false;    // Open Microsoft Office in background
                doc = app.Documents.Open(filePath, Missing.Value, false);

                Range range = doc.Range();
                string search = "$list";
 
                // Find in document to generate list
                while (range.Find.Execute(search))
                {
                    ListGallery listGallery = 
                        app.ListGalleries[WdListGalleryType.wdNumberGallery];

                    // Select found location
                    range.Select();

                    // Apply multi level list
                    app.Selection.Range.ListFormat.ApplyListTemplateWithLevel(
                        listGallery.ListTemplates[1],
                        ContinuePreviousList: false,
                        ApplyTo: WdListApplyTo.wdListApplyToWholeList, 
                        DefaultListBehavior: WdDefaultListBehavior.wdWord10ListBehavior);

                    // First level
                    app.Selection.TypeText("Root Item A");  // Set text to key in
                    app.Selection.TypeParagraph();  // Simulate typing in MS Word

                    // Go to 2nd level
                    app.Selection.Range.ListFormat.ListIndent();
                    app.Selection.TypeText("Child Item A.1");
                    app.Selection.TypeParagraph();
                    app.Selection.TypeText("Child Item A.2");
                    app.Selection.TypeParagraph();

                    // Back to 1st level
                    app.Selection.Range.ListFormat.ListOutdent(); 
                    app.Selection.TypeText("Root Item B");
                    app.Selection.TypeParagraph();

                    // Go to 2nd level
                    app.Selection.Range.ListFormat.ListIndent();
                    app.Selection.TypeText("Child Item B.1");
                    app.Selection.TypeParagraph();
                    app.Selection.TypeText("Child Item B.2");
                    app.Selection.TypeParagraph();

                    // Delete empty item generated by app.Selection.TypeParagraph();
                    app.Selection.TypeBackspace();
                }

                // Save document
                doc.Save();

                // Export to pdf 
                doc.ExportAsFixedFormat(pdfPath, WdExportFormat.wdExportFormatPDF);                   
            }
            catch (System.Exception ex)
            {
                LogError(ex);
            }
            finally
            {
                if (doc != null)
                {
                    // Need to close the document to prevent deadlock
                    doc.Close(false);
                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(doc);
                }

                if (app != null)
                {
                    app.Quit();
                    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(app);
                }
            }
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Word.Interop 创建多级项目符号列表 的相关文章

随机推荐

  • Chart.js 在画布上单击时获取最近的点

    单击画布上的任意位置时有没有办法获得最近的点 也许以某种方式收获核心 最近 方法 谢谢 我想你会发现getElementsAtXAxis很有帮助 基本上 getElementsAtXAxis有一个非常相似的行为getElementsAtEv
  • html 表单 - 摆脱问号和方程式

    这是我的代码 if request path employees
  • C# xml 文档

    目的是什么 xml随组件一起提供的文档文件 dll files 我知道如何构建一个 例如这里 http msdn microsoft com en us library aa288481 28VS 9 0 29 aspx 但是它们有什么用呢
  • 如何确定 Node.js 中导致 UnhandledPromiseRejectionWarning 的原因?

    我已经围绕 async await 库构建了我的 Node js 应用程序 并且它在大多数情况下都运行良好 我遇到的唯一麻烦是 每当未履行承诺时 我都会收到以下错误的一些变体 node 83333 UnhandledPromiseRejec
  • C#.Net 面板控制和 MDI 子表单 - 问题

    您好 我被困在带有面板控制的 MDIform 中 我有一个面板控件停靠 填充 到父 MDI 窗体 当我尝试使用菜单单击事件打开新的子窗体时 子窗体不会显示在 MDI 容器中 经过几次调试 我将面板控件的visible属性设置为false 现
  • 将 RCurl 与 SFTP 结合使用

    我正在尝试使用ftpUpload第一次在 RCurl 包中 我尝试访问的站点使用 sftp 协议 我已确保安装包含建立安全连接功能的 libcurl 版本 SFTP 被列为 RCurl 可用的协议之一 curlVersion protoco
  • Sed - 用充满奇怪字符的变量替换字符串

    我正在使用 sed 将文件中的字符替换为变量 该变量基本上是读取文件或网页的内容 其中包含多个类似散列的字符串 如下所示 这些字符串是随机生成的 define AUTH KEY CVo BO Qt1B GE h2 yU7h 5 wRV gt
  • 为什么与简单的 Rcpp 实现相比,zoo::rollmean 慢?

    zoo rollmean是一个有用的函数 它返回时间序列的滚动平均值 对于矢量x长度n和窗口大小k它返回向量c mean x 1 k mean x 2 k 1 mean x n k 1 n 我注意到我正在开发的一些代码似乎运行缓慢 因此我使
  • 如何修复 Android BLE SCAN_FAILED_APPLICATION_REGISTRATION_FAILED 错误?

    大多数时候它工作得很好 但有时我在尝试发现 BLE 设备时会遇到此错误 02 12 18 00 41 952 16178 16339 com icrealtime allie W BleRpcConnectionFactory Starti
  • 应用agones舰队时Kubectl错误:确保首先安装CRD

    我正在使用 minikube docker 驱动程序 和 kubectl 来测试 agones 舰队部署 运行时kubectl apply f lobby fleet yml 当我尝试应用任何其他 agones yaml 文件时 我收到以下
  • 从 Powershell 调用带有很长的可变参数列表的程序?

    我目前正在尝试将一系列批处理文件转换为 powershell 脚本 我想递归地为目录中存在的源文件运行编译器 编译器需要一长串参数 问题是 我希望参数是可变的 这样我就可以根据需要更改它们 这是批处理文件中的典型调用 为了可读性和长度而进行
  • IFormFile 的 Asp.Net Core swagger 帮助页面

    我正在尝试设置 swagger 来测试具有 IFormFile 属性的模型 例如我有下一个 api 方法 HttpPost public ApiResult
  • 客户端密码哈希

    我和我的一个朋友正在讨论是否应该在将 Web 应用程序用户的密码发送到我们的服务器之前对其进行预哈希处理 我知道有多个问题已经解决了这个主题 但它们都是关于将其安全地传输到服务器 我们的想法不是关于传输安全性 我们使用 SSL 我们希望对客
  • 每个配置文件中的部分只能出现一次

    所以昨天这段代码工作得很好 今天当我们去运行该网站时 我们收到了这个配置错误 我浏览了提出的其他问题 尽管它们很有帮助 但我无法确定我在哪里重复了某个部分 Here is my error http img photobucket com
  • 如何调整 Bootstrap 3 内联表单宽度?

    我有一个依赖 Bootstrap 3 的表单 完整的工作示例 http jsfiddle net x7vk7 2 http jsfiddle net x7vk7 2 要点是我有两列内容 第一列是col lg 4 第二个是col lg 8 第
  • Beyond Compare - 自动合并工作副本中的“冲突”文件(如果可能)

    我最近在 Git 中合并了两个分支 它们都开发了太长时间 因此两个分支上都有很多文件被更改 因此 Git 将它们标记为 冲突 一旦我在 Beyond Compare 中打开它们 就 不再有冲突 因为它足够聪明 可以解决所谓的冲突 因此 只需
  • Jupyter 笔记本 CPU 使用率低

    我注意到有些单元需要很长时间才能在我的 Jupyter Notebook 上完成执行 查看任务管理器时 我发现 python 进程仅使用了大约 4 的 CPU 我正在使用Python 3 6 2 我使用的是 Windows 但使用 Bash
  • 使用 float 在 3 列上进行 div

    我试图将 6 个不同高度的 div 放置在 3 列上 我对左侧和右侧的 div 使用 float 属性 对中央 div 使用 margin 0 auto 使用clear属性 我将第二行div放置在第一行之下 但我希望每个div都位于具有相同
  • 尝试在 Oracle SQL 中选择特定行时返回多行

    我试图从 Oracle SQL 中特定行的字段 tid 返回一个值 但我得到了一个奇怪的行为 首先 我尝试了这个 select tid from select tid rownum as rn from select tid from Tr
  • 使用 Word.Interop 创建多级项目符号列表

    我需要通过创建多级项目符号列表Microsoft Office Interop Word我目前正在 再次 与它的 可怕的 API 作斗争 我刚刚使用编程语言 C 在 Microsoft Office Word 2010 的 VSTO 文档级