Monday, December 28, 2009

A new Word automation technique

Often in my Word automation code, I need to create a table in which to display data. To the best of my knowledge until now, there have been two ways to create and populate a table; in the first method, one declares a table (wrdTable:= WrdDoc.Tables.Add (wrdapp.selection.Range, 10, 5, 1, 2)), which is then populated by accessing specific cells in the table. The other method is to create a huge string in which the cells are separated by a specific character (for example, ^); after the huge string is created, the ConvertToTable method is called which converts the long string into a table.

Both methods have their problems: the first requires that the number of rows be known before the table is declared, whereas the second does not always work (maybe one day I should investigate this further, although I should point out that one time the code worked perfectly on my computer, but not on my client's computer, despite the fact that we have the same operating system and same version of Word).

Today I had to write Word code to populate a table, not knowing in advance how many rows would be in the table. Which horn of the dilemma should I choose? At first, I tried the ConvertToTable method, and as usual, it didn't work. So I used the first method. In the past, I have used a query inside the Delphi program to calculate how many rows would be returned from the real query, but this doesn't seem to be the optimal approach. Remembering that if one creates a table in Word, one can always add rows to it using the 'Add row' command. So I ran a quick macro check in Word, and discovered that there is a 'InsertRowsBelow' method belonging to the current selection.

So my code became (in pseudocode):
create a table of size one row and how ever many columns
insert the row headers into the first row
for every tuple returned from my query,
 add a row to the word table
 insert into this row the various fields of the tuple
until there are no more tuples
Voila! A perfectly sized table. Here is the Delphi code to do the above:      
         wrdTable:= WrdDoc.Tables.Add (wrdapp.selection.Range, 1, 5, 1, 2);
         wrdSel.paragraphformat.alignment:= wdAlignParagraphRight;
         wrdTable.cell (1, 1).range.text:= 'column 1';
         wrdTable.cell (1, 2).range.text:= '
column 2';
         wrdTable.cell (1, 3).range.text:= '
column 3';
         wrdTable.cell (1, 4).range.text:= '
column 4';
         wrdTable.cell (1, 5).range.text:= '
column 5
';
         row:= 1;
         while not eof do
          begin
           inc (row);
           wrdSel.InsertRowsBelow (1);  // add new row
           wrdTable.cell (row, 1).range.text:= qYomanTaarich.asstring;
           wrdTable.cell (row, 2).range.text:= qYomanForename.asstring;
           wrdTable.cell (row, 3).range.text:= qYomanSurname.asstring;
           wrdTable.cell (row, 4).range.text:= qYomanName.asstring;
           wrdTable.cell (row, 5).range.text:= qYomanTherPrice.AsString;
           next
          end;

         wrdTable.borders.insidelinestyle:= true;
         wrdTable.borders.outsidelinestyle:= true;
         wrdTable.AutoFitBehavior (wdAutoFitContent);
         wrdTable.cell (row, 1).select;
         wrdSel.MoveDown (wdLine, 3); // Get out of the table
 
I suppose that the next step is to go over all my 'legacy' code and update those tables in which I don't know in advance how many rows there will be. This type of coding could be referred to as 'overhead' or 'excise', and a case could be made that my client should not have to pay for this. Fortunately, I think that there only two or three cases, so it shouldn't take very long to correct them. I often discover a new technique and then retrofit it into existing programs. It makes my maintenance easier, and generally improves the programs' performance.

1 comment:

Helmut said...

Select the last cell in a Word table with a header and a few data rows and tab (i.e. use the TAB key) and you will see that new rows are created automatically. I use this for adding rows without knowing how many I can expect.