5

我需要通过创建一个多级项目符号列表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,例如通过我尚未发现的速记方法?

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

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

编辑(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):

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

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 存储库,它展示了我对Word.Range对象的问题。文件中的OnClickButton方法Ribbon.cs调用我的自定义映射器类。那里的评论描述了这个问题。我知道我的问题与参数Word.Range对象引用有关,但我尝试的所有其他解决方案(例如修改类内部的范围)都失败了。到目前为止,我实现的最佳解决方案是将Document.Content范围指定为MapToCustomControlsIn方法的参数。这会将格式良好的多级项目符号列表(自定义 XML 部分绑定到内容控件)插入到文档的末尾。我想要的是将该列表插入到自定义位置到文档中(例如,当前选择通过Word.Selection.Range)。

4

2 回答 2

1

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);
                }
            }
        }
    }
}

于 2019-04-21T02:55:56.430 回答
0

需要做的事情的总结。

1.) 选择范围

2.) 配置范围的 ListFormat 属性。必须将 DefaultListBehavior 设置为 WdDefaultListBehavior.wdWord10ListBehavior

3.)添加您的一级文本。

4.)添加一个段落

5.)添加您的二级文本。

6.) 将 ListLevelTier 设置为 2 级

      public void GenerateMultiLevelList()
    {
        //A Range to write Text
        Word.Range writRange = ActiveDoc.Range(0); 
        writRange.Text = "Tier 1 Bullet Text";

        //Moving the Range to the End of Previously Entered Text
        writRange = ActiveDoc.Range(writRange.End - 1);

        //Formating the Range as a Bullet Point List
        writRange.ListFormat.ApplyListTemplate(BulletListTemplate, true, Word.WdListApplyTo.wdListApplyToWholeList, Word.WdDefaultListBehavior.wdWord10ListBehavior);

        //Adding a Paragraph
        writRange.Paragraphs.Add();
        writRange = ActiveDoc.Range(writRange.End - 1);
        writRange.Text = "Tier 2 Bullet Text";

        //Setting the List Level to 2 
        writRange.SetListLevel(2);
    }

在此处输入图像描述

于 2021-01-08T01:38:47.263 回答