This article is part of the "Building documents with code snippets" series. This article covers the manner in which a table is represented in WordProcessingML.

Tables are obviously more complex than the paragraphs we talked about in the previous article. A simple table in WordProcessingML takes up quite a few lines of xml which will need to be parsed when you read the table. Other operations, such as merging of table cells, gets fairly complicated as well. This article will help you get started on writing code to read and write WordprocessingML tables.

A really basic two-by-two table with borders is rendered in WordProcessingML as follows:

<w:tbl>
      <w:tblPr>
            <w:tblBorders>
                  <w:top w:val="single" w:sz="1" />
                  <w:left w:val="single" w:sz="1" />
                  <w:bottom w:val="single" w:sz="1" />
                  <w:right w:val="single" w:sz="1" />
                  <w:insideH w:val="single" w:sz="1" />
                  <w:insideV w:val="single" w:sz="1" />
            </w:tblBorders>
      </w:tblPr>
      <w:tblGrid>
            <w:gridCol w:w="1024" />
            <w:gridCol w:w="1024" />
      </w:tblGrid>
      <w:tr>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="1024" />
                  </w:tcPr>
                  <w:p />
            </w:tc>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="1024" />
                  </w:tcPr>
                  <w:p />
            </w:tc>
      </w:tr>
      <w:tr>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="1024" />
                  </w:tcPr>
                  <w:p />
            </w:tc>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="1024" />
                  </w:tcPr>
                  <w:p />
            </w:tc>
      </w:tr>     
</w:tbl>

Inside the w:tbl root node, the first point of interest is the node containing table properties, tblPr. Here you can specify things such as borders, margins and the preferred width of the table.

Next up the is the w:tblGrid node, which specifies the number of columns in the table. Each column includes a default width, which is specified in 20ths of a point. This value is respecified at the cell level to define the actual width of a cell.

That is the simple form of a table. But the structure gets more complex quite quickly because of all the things you can do with tables, such as merging cells or varying the widths of cells. These capabilities needs to be represented in WordProcessingML, which means more code to write if you want to handle each and every possible border-edge case..

Merging Cells

Now let me explain what merging cells does to the table structure. Like with the border settings you find in the sample above, this information is placed inside a node containing properties.

When merging a row of cells, a w:gridSpan node is used. Vertically merging a column of cells uses the w:vmerge element. WordProcessingML also defines the w:hmerge element which you can use instead of w:gridSpan, but that has been added for legacy purposes and should not be used normally.

Take a look at the following sample, which defines a row for the table in the first sample with one cell spanning two columns (a horizontal merge).

      <w:tr>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="2048" />
                        <w:gridSpan  w:val="2" />
                  </w:tcPr>
                  <w:p />
            </w:tc>
      </w:tr>

Notice that there is one fewer <w:tc> node defined compared to the unmerged table, and that the w:gridSpan node uses the w:val attribute to specify how many columns to span. The total number of cells in the row combined with these values (a row can contain more than one horizontally merged set of cells), should equal the number of columns defined in the table.

All of this basically means a bit more code to write when handling things like the removal of a column. You'll first need to calculate which cell is part of that column to remove it as well.

Merging Columns

Now the merging of a column. The following sample displays the same table as the first sample, this time with the first column merged together:

      <w:tr>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="1024" />
                        <w:vmerge w:val="restart" />
                  </w:tcPr>
                  <w:p />
            </w:tc>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="1024" />
                  </w:tcPr>
                  <w:p />
            </w:tc>
      </w:tr>
      <w:tr>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="1024" />
                        <w:vmerge />
                  </w:tcPr>
                  <w:p />
            </w:tc>
            <w:tc>
                  <w:tcPr>
                        <w:tcW w:w="1024" />
                  </w:tcPr>
                  <w:p />
            </w:tc>
      </w:tr>

Vertical merging is also defined inside the properties of cells, but for this we use the w:vmerge node instead of w:gridSpan. The w:val attribute is allowed to have two values: restart or continue. The first and top cell in the merged column should use restart and the rest continue. The default value is continue so you can drop the attribute all together for all but the top cell in the merged set.

That's it for the basic structure of tables. You can define much more information to take advantage of other Open XML features, but more on that in later samples. (If you think of anything you'd find of special interest let us know.)

The next article will go in to the details of the snippets working with tables. In the mean time, think about what merged cells will do to your code, when modifying rows or columns as shown above.