简体   繁体   English

如何在 ASP.net 中使用 Open XML 创建多级有序列表?

[英]How do you create multi-level ordered lists with Open XML in ASP.net?

I've spent countless hours trying to understand ordered lists in Open XML.我花了无数个小时试图理解 Open XML 中的有序列表。 Here's one of many references.这里是一个多引用。

I found this incredibly helpful example of a simple document creator here .我发现了一个简单的文档创建者的这种难以置信的帮助例子在这里

Also if I may gripe a little bit, I must say this is a painful learning curve.另外,如果我可能会抱怨一点,我必须说这是一个痛苦的学习曲线。 Creating numbering properties and referencing the correct abstractNumberId and the list goes on and on.创建编号属性并引用正确的abstractNumberId并且该列表不断重复。

Does anyone have a complete example of creating multi-level lists in code?有没有人有在代码中创建多级列表的完整示例? I can do so with no custom settings, as in setting the NumberFormatValues.Decimal .我可以在没有自定义设置的情况下这样做,就像设置NumberFormatValues.Decimal As soon as you want to start controlling the listType you have a lot of instant overhead.一旦您想开始控制listType您就会有很多即时开销。

Taking the above example that I started with, I added a level argument:以我开始的上述示例为例,我添加了一个level参数:

public void AddBulletList(List<Run> runList, int level)

I incorporate it on this line:我把它合并在这条线上:

var abstractLevel = new Level(new NumberingFormat() {
    Val = NumberFormatValues.Decimal}, new LevelText() {Val = "·"}) {LevelIndex = level};

Notice that I changed the format type to decimal... the method name says bullet but I'm just testing here.请注意,我将格式类型更改为十进制……方法名称显示为 bullet 但我只是在此处进行测试。

I also leverage it to handle the indentation:我还利用它来处理缩进:

var indentation = new Indentation() { Left = (720 * (level + 1)).ToString(), Hanging = "360" };  

So in my tests, I send a few sentences to the method passing in 0 for level.因此,在我的测试中,我向传入0表示级别的方法发送了几句话。 Then I send a few more passing in 1 for level.然后我再发送一些传入1的级别。

Two issues with my results:我的结果有两个问题:

1) I can't figure out how to reset the counter, so I get this: 1)我不知道如何重置计数器,所以我得到了这个:

1. Sentence 1
2. Sentence 2
3. Sentence 3
    4. Sentence 1
    5. Sentence 2
    6. Sentence 3

I attempted to use the levelRestart but that didn't work:我尝试使用levelRestart但这没有用:

abstractLevel.LevelRestart = new LevelRestart(){ Val = 0 } // tried 1 also

The only way I could get it to restart was to insert a blank paragraph before inserting the second list of sentences but this has styling issues (spacing).我可以让它重新启动的唯一方法是在插入第二个句子列表之前插入一个空白段落,但这有样式问题(间距)。

The second issue that I'm having is the numbering appears in WordDoc, but it shows as bullet points in Microsoft Word.我遇到的第二个问题是编号出现在 WordDoc 中,但它在 Microsoft Word 中显示为项目符号。 In addition, I get a compatibility mode warning in Microsoft Word.此外,我在 Microsoft Word 中收到兼容模式警告。

Someone will come along and say:有人会过来说:

Use Open XML Productivity Tools and create a doc and look at the generated code使用 Open XML Productivity Tools 并创建一个文档并查看生成的代码

Well my response to that is:好吧,我对此的回应是:

My eyes are bleeding after looking at the 5,000 lines of code a tiny little document to test with creates.在看了 5,000 行代码后,我的眼睛在流血,这是一个用来测试的小文档。

So I feel like I'm super close.所以我觉得我非常接近。 I have a lot of other customizations in my code which is why I continue to reference where I started.我的代码中有很多其他自定义项,这就是为什么我继续引用我开始的地方。 If someone could take that example or provide an existing one, I simply want to create multi-level lists and control the type of numbering (lowerRoman, decimal, etc.) is used.如果有人可以采用该示例或提供现有示例,我只想创建多级列表并控制使用的编号类型(lowerRoman、decimal 等)。

Update 1更新 1

I've really just had to bite the bullet and do some serious studying.我真的只需要硬着头皮做一些认真的学习。 With Thomas' help I've been able to press forward, but it seems I have one more pesky issue.在托马斯的帮助下,我已经能够继续前进,但似乎我还有一个棘手的问题。 My new ordered lists do not start at "1".我的新有序列表不是从“1”开始的。

Notice how in the image, if I click on the level for the first list, it highlights only that list.请注意在图像中,如果我单击第一个列表的级别,它只会突出显示该列表。 I expect them to be separate but apparently not.我希望它们是分开的,但显然不是。 The next list should start at 1.下一个列表应该从 1 开始。

新列表没有重新启动

For each new ordered list, I'm assigning it a new numberId so I don't know what's happening.对于每个新的有序列表,我都为它分配了一个新的numberId所以我不知道发生了什么。 Here's the generated markup:这是生成的标记:

<w:numbering xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid wp14">
      <w:abstractNum w:abstractNumId="1" w15:restartNumberingAfterBreak="1">
        <w:nsid w:val="191025D9" />
        <w:multiLevelType w:val="hybridMultilevel" />
        <w:tmpl w:val="48A2E570" />
        <w:lvl w:ilvl="0" w:tplc="0409000F">
          <w:start w:val="1" />
          <w:numFmt w:val="decimal" />
          <w:lvlText w:val="%1." />
          <w:lvlJc w:val="left" />
          <w:pPr>
            <w:ind w:start="720" w:hanging="360" />
          </w:pPr>
        </w:lvl>
        <w:lvl w:ilvl="1" w:tplc="04090019">
          <w:start w:val="1" />
          <w:numFmt w:val="lowerLetter" />
          <w:lvlText w:val="%2." />
          <w:lvlJc w:val="left" />
          <w:pPr>
            <w:ind w:start="1440" w:hanging="360" />
          </w:pPr>
        </w:lvl>        
      </w:abstractNum>
      <w:abstractNum w:abstractNumId="2" w15:restartNumberingAfterBreak="1">
        <w:nsid w:val="191025D9" />
        <w:multiLevelType w:val="hybridMultilevel" />
        <w:tmpl w:val="48A2E570" />
        <w:lvl w:ilvl="0" w:tplc="0409000F">
          <w:start w:val="1" />
          <w:numFmt w:val="decimal" />
          <w:lvlText w:val="%1." />
          <w:lvlJc w:val="left" />
          <w:pPr>
            <w:ind w:start="720" w:hanging="360" />
          </w:pPr>
        </w:lvl>
        <w:lvl w:ilvl="1" w:tplc="04090019">
          <w:start w:val="1" />
          <w:numFmt w:val="lowerLetter" />
          <w:lvlText w:val="%2." />
          <w:lvlJc w:val="left" />
          <w:pPr>
            <w:ind w:start="1440" w:hanging="360" />
          </w:pPr>
        </w:lvl>        
      </w:abstractNum>
      <w:num w:numId="1">
        <w:abstractNumId w:val="1" />
      </w:num>
      <w:num w:numId="2">
        <w:abstractNumId w:val="2" />
      </w:num>
    </w:numbering>

Here's the body:这是身体:

    <w:body xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:p>
    <w:pPr>
      <w:pStyle w:val="ListParagraph" />
      <w:numPr>
        <w:ilvl w:val="0" />
        <w:numId w:val="1" />
      </w:numPr>
    </w:pPr>
    <w:r>
      <w:t>List one item 1</w:t>
    </w:r>
  </w:p>
  <w:p>
    <w:pPr>
      <w:pStyle w:val="ListParagraph" />
      <w:numPr>
        <w:ilvl w:val="0" />
        <w:numId w:val="1" />
      </w:numPr>
    </w:pPr>
    <w:r>
      <w:t>List one item 2</w:t>
    </w:r>
  </w:p>
  <w:p>
    <w:pPr>
      <w:pStyle w:val="ListParagraph" />
      <w:numPr>
        <w:ilvl w:val="0" />
        <w:numId w:val="2" />
      </w:numPr>
    </w:pPr>
    <w:r>
      <w:t>List two item 1</w:t>
    </w:r>
  </w:p>
  <w:p>
    <w:pPr>
      <w:pStyle w:val="ListParagraph" />
      <w:numPr>
        <w:ilvl w:val="0" />
        <w:numId w:val="2" />
      </w:numPr>
    </w:pPr>
    <w:r>
      <w:t>List two item 2</w:t>
    </w:r>
  </w:p>
</w:body>

While I've never used the Open XML Productivity Tools, I regularly use and recommend the Open XML Package Editor for Modern Visual Studios .虽然我从未使用过 Open XML Productivity Tools,但我经常使用并推荐Open XML Package Editor for Modern Visual Studios With that package editor, you can look at the Open XML markup created by Microsoft Word to learn from that.使用该包编辑器,您可以查看 Microsoft Word 创建的 Open XML 标记以从中学习。

So, to answer your question, I've done just that, using a Word template that contains several multi-level lists that behave exactly like you want them to behave (I hope).因此,为了回答您的问题,我已经这样做了,使用包含多个多级列表的 Word 模板,这些列表的行为与您希望它们的行为完全相同(我希望)。

(a)  First paragraph, on outline level 0 (shown as 1 in Word)
(b)  Second paragraph, on outline level 0
     (1) Third paragraph, on outline level 1 (shown as 2 in Word)
     (2) Fourth paragraph, on outline level 1

Note that you would typically have different numbering formats (eg, lower letter, upper letter, lower roman, upper roman, decimal) on the different levels of your multi-level list.请注意,您的多级列表的不同级别通常会有不同的编号格式(例如,小写字母、大写字母、小写罗马字母、大写罗马字母、十进制)。

Next, this is the corresponding Open XML markup for the Main Document Part:接下来,这是主文档部分对应的 Open XML 标记:

  <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:body>
      <w:p>
        <w:pPr>
          <w:pStyle w:val="ListLowerLetter0"/>
        </w:pPr>
        <w:r>
          <w:t>First paragraph, on outline level 0 (shown as 1 in Word)</w:t>
        </w:r>
      </w:p>
      <w:p>
        <w:pPr>
          <w:pStyle w:val="ListLowerLetter0"/>
        </w:pPr>
        <w:r>
          <w:t>Second paragraph, on outline level 0</w:t>
        </w:r>
      </w:p>
      <w:p>
        <w:pPr>
          <w:pStyle w:val="ListLowerLetter0"/>
          <w:numPr>
            <w:ilvl w:val="1"/>    <!-- This overrides the numbering level -->
            <w:numId w:val="43"/>  <!-- This references the w:numbering/w:num -->
          </w:numPr>
        </w:pPr>
        <w:r>
          <w:t>Third paragraph, on outline level 1 (shown as 2 in Word)</w:t>
        </w:r>
      </w:p>
      <w:p>
        <w:pPr>
          <w:pStyle w:val="ListLowerLetter0"/>
          <w:numPr>
            <w:ilvl w:val="1"/>
            <w:numId w:val="43"/>
          </w:numPr>
        </w:pPr>
        <w:r>
          <w:t>Fourth paragraph, on outline level 1</w:t>
        </w:r>
      </w:p>
    </w:body>
  </w:document>

Note the w:pStyle and w:numPr elements, which specify the numbered paragraph style to be used and, where w:numPr is also used, override the numbering defaults specified in the style.请注意w:pStylew:numPr元素,它们指定要使用的编号段落样式,并且在也使用w:numPr情况下,会覆盖样式中指定的编号默认值。

Next, this is the Open XML markup for the Style Definitions Part:接下来,这是样式定义部分的 Open XML 标记:

  <w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">

    <!-- This is the paragraph style used in the main document part (w:document) -->
    <w:style w:type="paragraph" w:customStyle="1" w:styleId="ListLowerLetter0">
      <w:name w:val="List Lower Letter 0"/>
      <w:basedOn w:val="Normal"/>
      <w:pPr>
        <w:numPr>
          <w:numId w:val="43"/>   <!-- This references the w:numbering/w:num -->
        </w:numPr>
      </w:pPr>
    </w:style>

    <!-- This is the list style referenced in the numbering definitions part (w:numbering).
         This is optional but helps if you want to use the list in Word. -->
    <w:style w:type="numbering" w:customStyle="1" w:styleId="ListLowerLetter0List">
      <w:name w:val="List Lower Letter 0 List"/>
      <w:basedOn w:val="NoList"/>
      <w:pPr>
        <w:numPr>
          <w:numId w:val="43"/>   <!-- This references the w:numbering/w:num -->
        </w:numPr>
      </w:pPr>
    </w:style>


    <!-- I've included this for completeness because it is referenced by our
         paragraph style below -->
    <w:style w:type="paragraph" w:default="1" w:styleId="Normal">
      <w:name w:val="Normal"/>
      <w:rPr>
        <w:kern w:val="16"/>
      </w:rPr>
    </w:style>

    <!-- I've included this for completeness because it is referenced by our
         list style below -->
    <w:style w:type="numbering" w:default="1" w:styleId="NoList">
      <w:name w:val="No List"/>
      <w:semiHidden/>
      <w:unhideWhenUsed/>
    </w:style>
  </w:styles>

I would only really need the ListLowerLetter0 style.我只需要ListLowerLetter0样式。 ListLowerLetter0List is an optional list style. ListLowerLetter0List是可选的列表样式。 I always use those in my templates.我总是在我的模板中使用它们。 The other two styles are just there for completeness and consistency.其他两种风格只是为了完整性和一致性。 Obviously, a real-life Style Definitions Part contains many more styles and other elements.显然,现实生活中的样式定义部分包含更多样式和其他元素。

Finally, we have the Open XML markup of the Numbering Definitions Part (again with comments explaining what the relevant elements do):最后,我们有编号定义部分的 Open XML 标记(同样带有解释相关元素作用的注释):

  <w:numbering xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
               xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
               xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
               xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"
               mc:Ignorable="w14 w15">

    <!-- Here's an example multi-level list -->
    <w:abstractNum w:abstractNumId="67" w15:restartNumberingAfterBreak="0">
      <w:nsid w:val="3E434843" />
      <w:multiLevelType w:val="multilevel" />
      <w:tmpl w:val="1146F302" />

      <!-- The w:styleLink references our list style. This is optional (but I use
           it as a best practice in Word) -->
      <w:styleLink w:val="ListLowerLetter0List" />

      <!-- This defines the first outline level, i.e., 0 in Open XML lingo or 1
           when you look at it in Word -->
      <w:lvl w:ilvl="0">
        <!-- This starts the level at the ordinal number 1, i.e., "a" on this level -->
        <w:start w:val="1" />

        <!-- This defines the number format on this level -->
        <w:numFmt w:val="lowerLetter" />

        <!-- This references our paragraph style, which will be the same on each level -->
        <w:pStyle w:val="ListLowerLetter0" />

        <!-- This defines the level text, e.g., (a), (b), (c), ... -->
        <w:lvlText w:val="(%1)" />

        <!-- The next elements define alignment, indentation, and color -->
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="851" w:hanging="851" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>

      <!-- This and the following w:lvl elements define levels 1 to 8 (i.e., 2 to 9 in Word) -->
      <w:lvl w:ilvl="1">
        <w:start w:val="1" />
        <w:numFmt w:val="decimal" />
        <w:lvlText w:val="(%2)" />
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="1418" w:hanging="567" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>
      <w:lvl w:ilvl="2">
        <w:start w:val="1" />
        <w:numFmt w:val="upperLetter" />
        <w:lvlText w:val="(%3)" />
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="1985" w:hanging="567" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>
      <w:lvl w:ilvl="3">
        <w:start w:val="1" />
        <w:numFmt w:val="lowerRoman" />
        <w:lvlText w:val="(%4)" />
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="2552" w:hanging="567" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>
      <w:lvl w:ilvl="4">
        <w:start w:val="1" />
        <w:numFmt w:val="lowerLetter" />
        <w:lvlText w:val="%5." />
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="3119" w:hanging="567" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>
      <w:lvl w:ilvl="5">
        <w:start w:val="1" />
        <w:numFmt w:val="decimal" />
        <w:lvlText w:val="%6." />
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="3686" w:hanging="567" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>
      <w:lvl w:ilvl="6">
        <w:start w:val="1" />
        <w:numFmt w:val="lowerLetter" />
        <w:lvlText w:val="%7." />
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="4253" w:hanging="567" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>
      <w:lvl w:ilvl="7">
        <w:start w:val="1" />
        <w:numFmt w:val="lowerRoman" />
        <w:lvlText w:val="%8." />
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="4820" w:hanging="567" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>
      <w:lvl w:ilvl="8">
        <w:start w:val="1" />
        <w:numFmt w:val="lowerLetter" />
        <w:lvlText w:val="%9)" />
        <w:lvlJc w:val="left" />
        <w:pPr>
          <w:ind w:left="5387" w:hanging="567" />
        </w:pPr>
        <w:rPr>
          <w:color w:val="auto" />
        </w:rPr>
      </w:lvl>
    </w:abstractNum>

    <!-- This is the w:num referenced from our main document part (w:document) -->
    <w:num w:numId="43">
      <w:abstractNumId w:val="67" />
    </w:num>
  </w:numbering>

Now, based on the understanding of the markup required to create the desired effect, it is actually pretty easy to hand-write C# code that produces that markup, using either:现在,基于对创建所需效果所需的标记的理解,使用以下任一方法手写生成该标记的 C# 代码实际上非常容易:

Let me provide very short examples for both options, producing the following markup:让我为这两个选项提供非常简短的示例,生成以下标记:

    <w:num w:numId="43">
      <w:abstractNumId w:val="67" />
    </w:num>

Here's the code that uses the strongly typed classes of the Open XML SDK :下面是使用Open XML SDK的强类型类的代码:

var num = new NumberingInstance
{
    NumberID = 43,
    AbstractNumId = new AbstractNumId { Val = 67 }
};

And here's the code that uses the Open-XML-PowerTools :这是使用Open-XML-PowerTools的代码:

var num =
    new XElement(W.num, new XAttribute(W.numId, 43),
        new XElement(W.abstractNumId, new XAttribute(W.val, 67)));

In this case, the beauty of the Open-XML-PowerTools is that you can literally copy the tag names.在这种情况下,Open-XML-PowerTools 的美妙之处在于您可以逐字复制标签名称。 Further, the constructors of the Linq to XML classes (eg, XElement ) are much more flexible than those of the Open XML SDK and the C# code looks much like the Open XML markup.此外,Linq to XML 类的构造函数(例如XElement )比 Open XML SDK 的构造函数灵活得多,并且 C# 代码看起来很像 Open XML 标记。 Therefore, I have a slight preference for the Open-XML-PowerTools for use cases like this.因此,对于像这样的用例,我对 Open-XML-PowerTools 略有偏好。 However, I've very successfully used the Open XML SDK for such cases as well.但是,我也非常成功地将 Open XML SDK 用于此类情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM