dotnet OpenXML 解析 PPT 圖表 面積圖入門

来源:https://www.cnblogs.com/lindexi/archive/2022/08/05/16552973.html
-Advertisement-
Play Games

1、await和.result/ .getwaiter() .getresult()的區別 await:Task.Run裡面的邏輯是新開的線程去執行的,await Task.Run後面邏輯都在新開的線程去執行。 private async void MainWindow_Loaded(object ...


本文告訴大家如何使用 OpenXML 解析 PPT 的圖表,以面積圖為入門例子告訴大家 OpenXML 的存儲

在 PPT 裡面,有強大的圖表功能,可以聯動 Excel 展示數據。在 PPT 裡面的圖表和 Excel 的圖表稍微有一些差別,本文只聊 PPT 的圖表

如下圖是本文將作為例子的圖表

對應的數據如圖

如上圖可以看到在 PPT 裡面的圖表是可以使用 Excel 的數據,將 Excel 文件內嵌到 PPT 裡面。但這不代表要解析圖表的數據就一定需要先瞭解 Excel 的內容,本文將繞過對 Excel 的任何讀取,通過 PPT 裡面的內容拿到圖表的數據

圖表的組成

開始之前,還請先讓我告訴大家一個圖表元素包含的基礎組件部分,也就是圖表元素由哪些部分組成

橫坐標軸 類別坐標軸數據

對於面積圖來說,預設的面積圖的橫坐標就是類別的坐標軸數據,對應的 Excel 表格的第一列的內容,也就是 A B C D E 這些數據

在 OpenXML SDK 裡面,採用 DocumentFormat.OpenXml.Drawing.Charts.CategoryAxisData 存放

本文以下將會告訴大家獲取方法,這裡只是寫上類型,方便大家瞭解

縱坐標軸

對於預設面積圖來說,縱坐標屬於一個運行時屬性,不會存放在 OpenXML 文檔裡面,需要根據每個系列的數值的最大值和最小值以及配置,計算出來縱坐標的內容,本文不會涉及具體的坐標軸計算方法

數據系列

在圖表裡面有數據系列的概念,每個系列的數據組成一個個的數據系列。對於大部分圖表來說,數據層都是由一個個數據系列組成的

每個數據系列可以有自己的系列名稱

系列名稱大部分時候都放在圖例裡面,也就是圖例裡面的內容就是由系列名稱提供的

在 OpenXML SDK 裡面,採用 DocumentFormat.OpenXml.Drawing.Charts.SeriesText 存放

在圖表裡面,核心就是對數據的處理,系列的數據內容就是核心的

如圖,面積圖有兩個數據系列,通過上面的 Excel 內容可以瞭解到兩個系列的數據分別如下

系列 1:32,32,28,12,15
系列 2:12,12,12,21,28

本文將重點告訴大家如何解析圖表的數據

效果

以下是本文的解析效果,可以解析出來圖表的類別坐標軸數據,和各個系列的系列名稱和系列數據

下麵將告訴大家如何根據 OpenXML SDK 提供的方法讀取到圖表的內容

讀取圖表

在開始之前,還請大家先瞭解 OpenXml 讀取 PPT 的基礎。本文將在 C# dotnet 使用 OpenXml 解析 PPT 文件 的基礎上進行開發

先讀取 PPT 文檔

            var file = new FileInfo("Test.pptx");

            using var presentationDocument = PresentationDocument.Open(file.FullName, false);

本文的測試文件和所有代碼都可以在本文最後獲取

在這份 Test.pptx 的圖表是放在第一個頁面,先獲取頁面,通過頁面的元素獲取到圖表

            var slide = presentationDocument.PresentationPart!.SlideParts.First().Slide;

在 OpenXML 裡面的頁面存放的圖表的代碼如下

 <p:cSld>
   <p:spTree>
     <p:graphicFrame>
       ...
     </p:graphicFrame>
   </p:spTree>
 </p:cSld>

圖表也是一個元素,放在 SharpTree (p:spTree) 裡面,作為 GraphicFrame (p:graphicFrame) 存放。但不能說 GraphicFrame 就是圖表元素,在 OpenXML 的 GraphicFrame 是一個很通用的元素,如 OLE 元素或公式都會用到此元素

讀取 GraphicFrame 的內容,如果能讀取到 ChartReference (c:chart) 那就證明這個元素是圖表元素

            // 獲取圖表元素,在這份課件里,有一個面積圖。以下使用 First 忽略細節,獲取圖表
            var graphicFrame = slide.Descendants<GraphicFrame>().First();
            // 獲取到對應的圖表信息,圖表是引用的,內容不是放在 Slide 頁面裡面,而是放在獨立的圖表 xml 文件里
            var graphic = graphicFrame.Graphic;
            var graphicData = graphic?.GraphicData;
            var chartReference = graphicData?.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.ChartReference>();

在 OpenXML 里,圖表是引用的,內容不是放在 Slide 頁面裡面,而是放在獨立的圖表 xml 文件里。頁面的代碼如下

   <p:graphicFrame>
     <a:graphic>
       <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart">
         <c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId2" />
       </a:graphicData>
     </a:graphic>
   </p:graphicFrame>

根據 dotnet OpenXML 為什麼資源使用 Relationship 引用 可以瞭解到,這裡的圖表引用,需要到 rels 文件裡面獲取關聯的內容。在 OpenXml SDK 里,封裝好了獲取方法,獲取時需要有兩個參數,一個是 id 另一個是去哪裡獲取的 Part 內容。獲取 id 的方法如下

            // 獲取到 id 也就是 `r:id="rId2"` 根據 Relationship 的描述,可以知道去 rels 文件裡面獲取關聯的內容。在 OpenXml SDK 里,封裝好了獲取方法,獲取時需要有兩個參數,一個是 id 另一個是去哪裡獲取的 Part 內容
            var id = chartReference?.Id?.Value;

在這份課件是圖表元素放在頁面上,可以通過頁面去獲取到圖表元素的存儲。在實際項目里,需要判斷圖表元素所在的是頁面還是頁面模版等,不能和以下代碼寫固定從頁面獲取

            // 如果是放在模版裡面,記得要用模版的 Part 去獲取
            var currentPart = slide.SlidePart!;

            if (!currentPart.TryGetPartById(id!, out var openXmlPart))
            {
                // 在這份課件里,一定不會進入此分支
                // 一定能從頁面找到對應的資源內容也就是圖表
                return;
            }

這裡拿到的 openXmlPart 是 ChartPart 對象,這裡面就存放了圖表的信息

            if (openXmlPart is not ChartPart chartPart)
            {
                // 這裡拿到的一定是 ChartPart 對象,一定不會進入此分支。但是在實際項目的代碼,還是要做這個判斷
                return;
            }

這裡的 ChartPart 對應的就是 charts\chartN.xml 文件。這裡的 chartN.xml 表示的是 chart1.xml 或 chart2.xml 等文件

這個文件的存儲內容大概如下

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <c:chartSpace>
   <c:chart>
     ...
     <c:plotArea>
       ...
     </c:plotArea>
   </c:chart>
 </c:chartSpace>

讀取圖表首先需要獲取 ChartSpace 對象,再獲取到 Chart 對象。在 OpenXML SDK 裡面,定義了很多個 Chart 類型,放在不同的命名空間,在獲取時,推薦寫全命名空間

using Chart = DocumentFormat.OpenXml.Drawing.Charts.Chart;

            // 這裡的 ChartPart 對應的就是 charts\chartN.xml 文件。這裡的 chartN.xml 表示的是 chart1.xml 或 chart2.xml 等文件
            var chartSpace = chartPart.ChartSpace;

            // 這裡的 Chart 是 DocumentFormat.OpenXml.Drawing.Charts.Chart 類型,在 OpenXmlSDK 裡面,有多個同名的 Chart 類型,還請看具體的命名空間
            /*
            <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
            <c:chartSpace>
              <c:chart>
                ...
                <c:plotArea>
                  ...
                </c:plotArea>
              </c:chart>
            </c:chartSpace>
             */
            var chart = chartSpace.GetFirstChild<Chart>();

接著獲取 PlotArea 對象,這裡面就存放了圖表的內容

using PlotArea = DocumentFormat.OpenXml.Drawing.Charts.PlotArea;

            var chart = chartSpace.GetFirstChild<Chart>();
            var plotArea = chart?.GetFirstChild<PlotArea>();

如本文的面積圖就放在 PlotArea 元素里

 <c:plotArea>
   <c:areaChart>
     ...
   </c:areaChart>
 </c:plotArea>

在 Chart 里,有不同的圖表類型,例如 BarChart Bar3DChart LineChart PieChart Pie3DChart OfPieChart 不水字數了,就是很多不同的圖表。本文這裡只獲取面積圖

            var areaChart = plotArea?.GetFirstChild<AreaChart>();

            if (areaChart == null)
            {
                // 在這份課件里,一定存在面積圖,一定不會進入此分支
                return;
            }

獲取到面積圖,接下來就是讀取面積圖的數據系列

數據系列的存儲代碼如下

  <c:plotArea>
    <c:areaChart>
      <c:ser>
        ...
      </c:ser>
      <c:ser>
        ...
      </c:ser>
    </c:areaChart>
  </c:plotArea>

每個 DocumentFormat.OpenXml.Drawing.Charts.AreaChartSeries (c:ser) 就是一個系列的內容。一個圖表裡面可以有多個系列,每個系列包含下麵數據

  • 系列名
  • 系列數據
  • 類別軸上的數據
  • 樣式信息

樣式信息裡面包含了填充的畫刷,如純色填充。類別軸上的數據是面積圖橫坐標軸顯示內容,每個系列都有,這是重覆的數據,在 PPT 里,只取第一個系列的數據

數據系列里的橫坐標軸的類別坐標軸數據,在 OpenXML 裡面,是 DocumentFormat.OpenXml.Drawing.Charts.CategoryAxisData 類型,對應 c:cat 的內容

讀取類別軸上的數據方法如下

            foreach (var areaChartChildElement in areaChart.ChildElements)
            {
                // 獲取系列
                /*
                    <c:plotArea>
                      <c:areaChart>
                        <c:ser>
                          ...
                        </c:ser>
                        <c:ser>
                          ...
                        </c:ser>
                      </c:areaChart>
                    </c:plotArea>
                 */
                if (areaChartChildElement is DocumentFormat.OpenXml.Drawing.Charts.AreaChartSeries areaChartSeries)
                {
                    // 類別軸上的數據 橫坐標軸上的數據
                    var categoryAxisData = areaChartSeries.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.CategoryAxisData>()!;
                }
            }

在 OpenXML SDK 的存儲如下

 <c:plotArea>
   <c:areaChart>
     <c:ser>
       <c:cat>
       </c:cat>         
     </c:ser>
   </c:areaChart>
 </c:plotArea>

在類別軸上的數據存放的是數據引用,數據引用在 OpenXML 裡面有多個不同的存儲類型。例如 NumberReference 類型表示的是數值引用,例如 StringReference 表示字元串引用類型,在這份課件裡面存放的是 StringReference 類型,以下代碼只演示採用 StringReference 類型的讀取方式,還請在具體項目,自行判斷

var categoryAxisData = areaChartSeries.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.CategoryAxisData>()!;
var categoryAxisDataStringReference = categoryAxisData.GetFirstChild<StringReference>();

在 StringReference 裡面,大部分都有兩個部分,一個是公式,表示如何引用 Excel 的數據。通過公式讀取 Excel 可以獲取到正確的數據,但缺點是比較複雜。可以通過第二部分,也就是緩存數據部分讀取,雖然讀取緩存也許不對,不過優點在於簡單

存儲的代碼如下

  <c:cat>
   <c:strRef>
     <c:f>Sheet1!$A$2:$A$6</c:f>
     <c:strCache>
       <c:ptCount val="5" />
       <c:pt idx="0">
         <c:v>A</c:v>
       </c:pt>
       <c:pt idx="1">
         <c:v>B</c:v>
       </c:pt>
       <c:pt idx="2">
         <c:v>C</c:v>
       </c:pt>
       <c:pt idx="3">
         <c:v>D</c:v>
       </c:pt>
       <c:pt idx="4">
         <c:v>E</c:v>
       </c:pt>
     </c:strCache>
   </c:strRef>
 </c:cat>

獲取公式的代碼如下

 var categoryAxisDataStringReference = categoryAxisData.GetFirstChild<StringReference>();
 if (categoryAxisDataStringReference != null)
 {
     // 這個公式表示是從 Excel 哪個數據獲取的,獲取的方式比較複雜。這裡還是先從緩存獲取
     var categoryAxisDataFormula = categoryAxisDataStringReference.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.Formula>();
 }

讀取緩存的方法如下

  // 讀取緩存
  var categoryAxisDataStringCache = categoryAxisDataStringReference.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.StringCache>()!;

讀取類別軸上的數據

 var list = new List<string>();
 foreach (var stringPoint in categoryAxisDataStringCache.Elements<DocumentFormat.OpenXml.Drawing.Charts.StringPoint>())
 {
     // 以下的 類別軸上的數據 橫坐標軸上的數據,各個列項的名稱
     // 對於面積圖來說,多個系列的列項都是相同的。儘管在 OpenXml 存儲裡面存放了兩份,但以第零個系列的為準
     var text = stringPoint.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.NumericValue>()!.Text;

     list.Add(text);
 }

上面代碼的 list 就存放了讀取類別軸上的數據,也就是 A B C D E 字元串

繼續讀取第二部分內容,系列的系列名稱,也就是系列標題

系列標題在 OpenXML 里,使用 DocumentFormat.OpenXml.Drawing.Charts.SeriesText 表示,對應 c:tx 類型。在圖表裡面的數據大部分都採用引用的方式,引用裡面基本都有兩個部分,如 類別軸上的數據 有引用 Excel 的公式,和緩存

這裡讀取系列標題也是通過緩存讀取,不會去解析 Excel 內容

  // 獲取系列標題,放心,可以不讀取 Excel 的內容,通過緩存內容即可。但是緩存內容也許和 Excel 內容不對應
  /*
      <c:plotArea>
        <c:areaChart>
          <c:ser>
            <c:tx>
              <c:strRef>
                <c:f>Sheet1!$B$1</c:f>
                <c:strCache>
                  <c:ptCount val="1" />
                  <c:pt idx="0">
                    <c:v>系列 1</c:v>
                  </c:pt>
                </c:strCache>
              </c:strRef>
            </c:tx>
            ...
          </c:ser>
        </c:areaChart>
      </c:plotArea>
   */
  var seriesText = areaChartSeries.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.SeriesText>()!;
  var seriesTextStringReference = seriesText.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.StringReference>()!;
  // 這個公式表示是從 Excel 哪個數據獲取的,獲取的方式比較複雜。這裡還是先從緩存獲取
  var seriesTextFormula = seriesTextStringReference.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.Formula>();

使用緩存獲取系列名稱

 // 有緩存的話,從緩存獲取就可以,緩存內容也許和 Excel 內容不對應
 /*
     <c:strCache>
       <c:ptCount val="1" />
       <c:pt idx="0">
         <c:v>系列 1</c:v>
       </c:pt>
     </c:strCache>
  */
 var seriesTextStringCache = seriesTextStringReference.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.StringCache>();
 if (seriesTextStringCache != null)
 {
     var seriesTextStringPoint = seriesTextStringCache.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.StringPoint>();

     var numericValue = seriesTextStringPoint!.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.NumericValue>();
     // 系列1 標題
     var title = numericValue!.Text;

 }

上面的 title 就是系列的標題,如上面圖表,拿到的就是 系列1系列2 字元串

完成獲取系列的標題獲取,下麵開始獲取系列的樣式。系列的樣式如系列的填充畫刷,畫刷是一個比較大的話題,本文使用的例子只用到純色畫刷

圖表的系列樣式存儲採用的是 DocumentFormat.OpenXml.Drawing.Charts.ChartShapeProperties 類型,圖表的形狀屬性的內容和 形狀屬性 的內容是差不多的

  <c:plotArea>
    <c:areaChart>
      <c:ser>
        <c:tx>
          ...
        </c:tx>
        <c:spPr>
          <a:solidFill>
            <a:srgbClr val="FF0000" />
          </a:solidFill>
        </c:spPr>
      </c:ser>
    </c:areaChart>
  </c:plotArea>

獲取系列的填充顏色

  // 圖表的形狀屬性的內容和 形狀屬性 的內容是差不多的
  /*
      <c:plotArea>
        <c:areaChart>
          <c:ser>
            <c:tx>
              ...
            </c:tx>
            <c:spPr>
              <a:solidFill>
                <a:srgbClr val="FF0000" />
              </a:solidFill>
            </c:spPr>
          </c:ser>
        </c:areaChart>
      </c:plotArea>
   */
  var chartShapeProperties = areaChartSeries.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.ChartShapeProperties>()!;
  // 獲取畫刷,畫刷有好多不同的類型,這個課件只用了純色
  var solidFill = chartShapeProperties.GetFirstChild<SolidFill>()!;
  // 畫刷純色顏色有很多個顏色表示方法,這個課件只用了 RGB 的純色
  var rgbColorModelHex = solidFill.GetFirstChild<DocumentFormat.OpenXml.Drawing.RgbColorModelHex>()!;
  // 這就是這個系列的顏色
  var colorValue = rgbColorModelHex.Val!.Value;

以上的 colorValue 就是這個系列的填充。不同的系列可以有不同的填充

接下來獲取圖表最核心的內容,系列的數據

在 PPT 裡面,是允許數據為空的,如果是空,行為就是不繪製系列內容。本文使用的例子是存在數據,就沒有判斷數據為空

 // 獲取系列的值
 /*
     <c:plotArea>
       <c:areaChart>
         <c:ser>
           <c:tx>
             ...
           </c:tx>
           <c:cat>
             ...
           </c:cat>
           <c:val>
             <c:numRef>
               <c:f>Sheet1!$B$2:$B$6</c:f>
               <c:numCache>
                 <c:formatCode>General</c:formatCode>
                 <c:ptCount val="5" />
                 <c:pt idx="0">
                   <c:v>32</c:v>
                 </c:pt>
                 <c:pt idx="1">
                   <c:v>32</c:v>
                 </c:pt>
                 <c:pt idx="2">
                   <c:v>28</c:v>
                 </c:pt>
                 <c:pt idx="3">
                   <c:v>12</c:v>
                 </c:pt>
                 <c:pt idx="4">
                   <c:v>15</c:v>
                 </c:pt>
               </c:numCache>
             </c:numRef>
           </c:val>
         </c:ser>
         <c:ser>
           ...
         </c:ser>
       </c:areaChart>
     </c:plotArea>
  */
 // 這就是系列裡面最重要的數據。然而在 PPT 裡面,是允許為空的,如果是空,行為就是不繪製系列內容
 var valueList = new List<string>();
 var values = areaChartSeries.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.Values>();

在面積圖,數據理論上是數值類型。對應的是 NumberReference 引用,同樣可以使用公式引用 Excel 數據,也可以採用緩存獲取

  var valuesNumberReference = values?.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.NumberReference>();
  if (valuesNumberReference != null)
  {
      /*
           <c:val>
             <c:numRef>
               <c:f>Sheet1!$B$2:$B$6</c:f>
               <c:numCache>
                 <c:formatCode>General</c:formatCode>
                 <c:ptCount val="5" />
                 <c:pt idx="0">
                   <c:v>32</c:v>
                 </c:pt>
                 <c:pt idx="1">
                   <c:v>32</c:v>
                 </c:pt>
                 <c:pt idx="2">
                   <c:v>28</c:v>
                 </c:pt>
                 <c:pt idx="3">
                   <c:v>12</c:v>
                 </c:pt>
                 <c:pt idx="4">
                   <c:v>15</c:v>
                 </c:pt>
               </c:numCache>
             </c:numRef>
           </c:val>
       */
      // 這份課件一定存在 values 內容
      // 和其他的一樣,存在引用 Excel 的內容。這裡同樣也是採用緩存
      var valuesFormula = valuesNumberReference.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.Formula>();

本文只採用讀取緩存的方式。在緩存也有一個數據,表示數據如何格式化顯示,例如通過格式化字元串告訴 PPT 如何格式化日期內容等。本文使用的例子寫的是 General 表示不需要格式化

  var valuesNumberingCache = valuesNumberReference.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.NumberingCache>()!;

  // 通過 FormatCode 決定界面效果。這份課件是 General 表示不用格式化
  var formatCode = valuesNumberingCache.FormatCode;
  Debug.Assert(formatCode?.Text == "General");

接下來繼續獲取數據

 var valueList = new List<string>();
 foreach (var numericPoint in valuesNumberingCache.Elements<DocumentFormat.OpenXml.Drawing.Charts.NumericPoint>())
 {
     var numericValue = numericPoint.GetFirstChild<DocumentFormat.OpenXml.Drawing.Charts.NumericValue>()!;
     var numericValueText = numericValue.Text;

     valueList.Add(numericValueText);
 }

通過上面例子,無論數據引用是數值引用還是字元串引用,具體的內容都是 DocumentFormat.OpenXml.Drawing.Charts.NumericValue 類型。如果不需要準確判斷內容,可以採用獲取此類型,簡化邏輯

上面代碼的 valueList 存放了系列數據內容

這就完成了讀取圖表的大部分數據內容

數據存儲

本文期望大家瞭解 OpenXML 里對圖表的存儲方式。在 OpenXML 裡面,圖表是放在頁面的一個元素,但是數據不放在頁面上,頁面上放的是引用。通過引用獲取到圖表的內容,對應的數據存儲如下

    <c:plotArea>
      <c:areaChart>
        <c:ser>
          <!-- 系列的數據 -->
        </c:ser>
        <c:ser>
          <c:tx>
            <!-- 系列標題 -->
          </c:tx>
          <c:spPr>
            <!-- 系列樣式 -->
          </c:spPr>
          <c:cat>
            <!-- 類別軸上的數據 -->
          </c:cat>
          <c:val>
            <!-- 系列數據 -->
          </c:val>
        </c:ser>
      </c:areaChart>
    </c:plotArea>

以上是面積圖的存儲,面積圖裡面由多個系列組成。對於圖表來說,最重要的數據就是每個系列的內容。系列裡面包含了系列標題,系列樣式,和類別軸上的數據和系列數據。其中類別軸上的數據只有第零個系列的有用,但是在 OpenXML 里每個系列都重覆存放一份

在圖表裡存放的數據使用的是引用,可以用公式讀取 Excel 的數據,也可以使用緩存。如果想要數據正確,是需要通過公式讀取 Excel 的數據,如果想要讀取 Excel 的數據,前置的是讀取 PPT 裡面內嵌的 Excel 內容,請看 dotnet OpenXML 讀取 PPT 內嵌 xlsx 格式 Excel 表格的信息

圖表還有其他的內容,如圖表標題和樣式等。以及圖表的數據格式化展示邏輯,日期計算方法等,這些都沒有放在本文告訴大家。將在後續博客告訴大家這些內容和行為,請看 Office 使用 OpenXML SDK 解析文檔博客目錄

代碼

本文以上的測試文件和代碼放在githubgitee 歡迎訪問

可以通過如下方式獲取本文的源代碼,先創建一個空文件夾,接著使用命令行 cd 命令進入此空文件夾,在命令行裡面輸入以下代碼,即可獲取到本文的代碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 2f266d20916f784662d84a98d60b7e1bd097d11d

以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

獲取代碼之後,進入 MainWindow.xaml.cs 文件,在這個文件里就是本文的例子代碼

更多

更多請看 Office 使用 OpenXML SDK 解析文檔博客目錄

博客園博客只做備份,博客發佈就不再更新,如果想看最新博客,請到 https://blog.lindexi.com/

知識共用許可協議
本作品採用知識共用署名-非商業性使用-相同方式共用 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發佈,但務必保留文章署名[林德熙](http://blog.csdn.net/lindexi_gd)(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我[聯繫](mailto:[email protected])。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在上一遍文章中已經介紹了PixelShaderEffect 用hlsl(著色器) 可以實現各種自定義濾鏡效果了,本文將用 "ThresholdEffect" 來講解如何編寫,編譯hlsl,然後使用PixelShaderEffect製作自定義濾鏡。 效果圖: 一.hlsl幫助程式介紹 在寫hlsl 代 ...
  • 簡介 FTP是FileTransferProtocol(文件傳輸協議)的英文簡稱,而中文簡稱為“文傳協議”。用於Internet上的控制文件的雙向傳輸。同時,它也是一個應用程式(Application)。基於不同的操作系統有不同的FTP應用程式,而所有這些應用程式都遵守同一種協議以傳輸文件。 FTP ...
  • 在繼承中,派生類可以拿到基類的方法,若是派生類很多,且有時某部分派生類的部分實現邏輯是一樣的,但其他的派生類又用不到,這個時候這些邏輯若是全部寫到派生類中,就會導致產生很多的重覆邏輯,但是若是寫到基類中就會導致其他用不到當前邏輯的派生類也能調用,這樣就會導致代碼維護出現了問題。由此產生了介面。 在C ...
  • WPF 截圖控制項之移除控制項(九)「仿微信」 WPF 截圖控制項之移除控制項(九)「仿微信」 作者:WPFDevelopersOrg 原文鏈接: https://github.com/WPFDevelopersOrg/WPFDevelopers 框架使用大於等於.NET40; Visual Studio ...
  • 之前寫的DBHelper,名稱確實太Low,就改了個名,叫LiteSql,本來想叫SqlShuttle(SQL一把梭),奈何單詞太長。 有兩個版本,一個是LiteSql,一個是Dapper.LiteSql,LiteSql底層用的是ADO.NET,Dapper.LiteSql底層用的是Dapper,提 ...
  • 從零開始搭建基於 ABP Framework 分層架構解決方案,快速集成框架內置應用模塊。探索基於 ABP Framework 極速開發的最佳路徑,構建一個模塊完備、可開發、可調試、可發佈和部署的分層架構解決方案。 ...
  • 一、前言 之前分享過一期關於DrawingVisual來繪製高性能曲線的博客,今天再分享一篇通過另一種方式來繪製高性能曲線的方法,也就是通過WriteableBitmap的方式;具體的一些細節這裡就不啰嗦了,同樣是局部繪製的思想,滾動條拖動到哪裡,就只繪製那一部分的曲線,直接貼代碼;(該程式在英特爾 ...
  • 規則,點擊投籃目標點,就會有一個球沿著相關拋物線,然後,判斷是否進入籃子里,其實就是一個矩形,直接是按照碰撞檢測來的,碰到就算進去了,對其增加了一個分數統計等功能。 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...