zoukankan      html  css  js  c++  java
  • OpenXml SDK学习笔记(2):新建段落和文本

    正好,我今天下午就打算写这个工具类库了。那开发过程就随便写写哈。我个人开发是习惯先想最后如何调用,再按调用编写过程。那么,最后使用,我希望是这样的风格:

     1 using (WordDocument doc = WordDocument.CreateDocument("newDocx.docx", true))
     2 {
     3     doc.AppendParagraph(para =>
     4         {
     5             para.Append("你好,世界")
     6                 .Append("  ")
     7                 .Append("你好,Word");
     8         })
     9         .AppendParagraph()
    10             .Append("Hello World")
    11             .Append("  ")
    12             .Append("Hello World");
    13     doc.Save();
    14 }

    大致可以看出两种风格。上面那种属于ASP.NET CORE里经常用到的Builder模式,下面一种则是经典的流式接口。那么,经过思考,由于这里之后还需要设置格式。所以,流式接口可能更适合一些。因为设置格式的时候,更需要Builder。而一个函数里写两个Builder就非常奇怪。所以,在这里,选择流式接口。所以,在刚才的项目里,新建两个类:WordParagraph和WordRun。这里需要注意,在WordprocessingML里,Run这个结构不一定是隶属于Paragraph的,Paragraph这个结构也不一定是直接隶属于Document的。比如在表格里,用来表示单元格的TableCell里,也会有Paragraph。所以呢,这里需要表示一下整个树型结构,否则以后要扩展起来就非常麻烦。那么,整个WordprocessingML基本所有元素都符合这个特性。而且在原本的代码里,Paragraph和Run都是OpenXmlCompositeElement的一个子类。

    所以,新建一个CompositeElementBase,大致代码如下:

     1 using System;
     2 using System.Collections.Generic;
     3 
     4 namespace Ricebird.Wordprocessing
     5 {
     6     public abstract class CompositeElementBase
     7     {
     8         #region ctor
     9         protected CompositeElementBase BaseElement
    10         {
    11             get;
    12             set;
    13         }
    14 
    15         protected WordDocument Document
    16         {
    17             get;
    18             set;
    19         }
    20 
    21         protected List<CompositeElementBase> Children
    22         {
    23             get; set;
    24         } = new List<CompositeElementBase>();
    25 
    26         public CompositeElementBase(CompositeElementBase @base)
    27         {
    28             if (@base == null)
    29             {
    30                 throw new NullReferenceException("@base 不能为 null");
    31             }
    32             BaseElement = @base;
    33             Document = @base.Document;
    34         }
    35 
    36         public CompositeElementBase(WordDocument doc)
    37         {
    38             Document = doc;
    39             BaseElement = null;
    40         }
    41         #endregion
    42 
    43         #region 流式接口
    44         public abstract CompositeElementBase AppendParagraph();
    45         public abstract CompositeElementBase Append(string text);
    46         #endregion
    47     }
    48 }

    这里注意,构造函数需要两个。因为一种情况是像顶级段落一样没有上级元素的。他们直接隶属于Document。接着,实现WordParagraph和WordRun。篇幅所限,就只放一个Run。

    using DocumentFormat.OpenXml.Wordprocessing;
    
    namespace Ricebird.Wordprocessing
    {
        public class WordRun : CompositeElementBase
        {
            private Run InternalRun
            {
                get;set;
            }
    
            public WordRun(Run r, CompositeElementBase @base) : base(@base)
            {
                InternalRun = r;
            }
    
            #region 读取文本
            public void SetText(string text)
            {
                Text t = InternalRun.GetFirstChild<Text>();
                if (t == null)
                {
                    t = new Text();
                    InternalRun.Append(t);
                }
                t.Text = text;
            }
    
            public string GetText()
            {
                Text t = InternalRun.GetFirstChild<Text>();
                return t?.Text ?? string.Empty;
            }
            #endregion
    
            #region 流式接口
            public override CompositeElementBase Append(string text)
            {
                return BaseElement.Append(text);
            }
    
            public override CompositeElementBase AppendParagraph()
            {
                return BaseElement.AppendParagraph();
            }
            #endregion
        }
    }

    这个都是非常简单的代码,然后运行这个程序。就会发现:

    对比一下我放在下面的代码图,是不是有哪里不对?比如说,空格呢?于是,再去翻阅文档。就可以从Text这个对象的属性里翻出一个叫Space的属性(https://docs.microsoft.com/zh-cn/dotnet/api/documentformat.openxml.wordprocessing.texttype.space)。他说了,不设定这个属性值,那么空格是不会显示的!所以,改动一下SetText函数:

     1 public void SetText(string text)
     2 {
     3     Text t = InternalRun.GetFirstChild<Text>();
     4     if (t == null)
     5     {
     6         t = new Text();
     7         InternalRun.Append(t);
     8     }
     9     t.Text = text;
    10 
    11     if (text.Contains(" "))
    12     {
    13         t.Space = new DocumentFormat.OpenXml.EnumValue<DocumentFormat.OpenXml.SpaceProcessingModeValues>(DocumentFormat.OpenXml.SpaceProcessingModeValues.Preserve);
    14     }
    15 }

    对,就是为了设置一个空格可以用,要写辣么辣么长的代码!再运行一下程序。就不截图了哈,整个代码就正常了起来。然后,考虑到在很多时候,文章的一段就是一个连续文本。所以,再添加一个AppendParagraph(string text)函数。代码就不贴了,非常简单,自己加一下就行。然后整理一下流式接口里的调用关系,能用Document的就不要用BaseElement。全部整理完,再看一眼自己的需求,把正式文本填进去:

     这图左上是代码的运行图,右上是最后的效果图,下面是代码。对比两边,不能说一模一样只能说完全不像。。。。。。为什么呢?原来是。。。少了格式。那么下一篇就讲如果给文字加上格式

  • 相关阅读:
    rqnoj71 拔河比赛
    NOI2002 洛谷 P1196 银河英雄传说
    sdibt 1244 烦人的幻灯片
    POJ 1273 Drainage Ditches -dinic
    NOIP2005提高组 过河
    OpenJudge 7627 鸡蛋的硬度
    Openjudge 8782 乘积最大
    OpenJudge 7624 山区建小学
    UVa 1328 Period
    UVa 11384 Help is needed for Dexter
  • 原文地址:https://www.cnblogs.com/Pray4U/p/15533993.html
Copyright © 2011-2022 走看看