In the last post about interop I gave you some basic code and techniques to do some really basic word document interaction.  In this one, I'm going to go beyond that and get into some more advanced document interaction.  I learned most of this because someone who no longer works for me locked us into Word as our "reporting platform", but at the 11th hour I realized the reports weren't really all that...implemented....and I had to basically start from scratch.  I was displeased.  Fair warning - if you find yourself needing this information, I beg you to find another way to do this besides Word interop!

Okay, so the first thing to remember when working with text and other objects in a Word Document - Everything is a Range!  Furthermore, each distinct range gets its own set of formatting options.  This means if you require a granular level of control over the text and how it appears, you will be required to take granular control over one or many ranges.  Let's look at an example:

This is some bold header text.

  Underneath the text, and indented two spaces, we have a description.

  Underneath that, we talk about whatever we are talking about, but based on some variable data, this text here is red. 

Ok, let's look at what is going on here.  To make this happen, you will need 6 Range objects total, and an understanding of how to manipulate them.  Looking at it, you may have guessed we would need 5 ranges, and, that could also work.  However, I found that it was much easier to manage the document by working within a master Range, that I call the insert range.

To get the insert range, you would find the beginning in some way.  This could be by setting a Bookmark in your template, or setting up the document in tables (I'll cover this later), or some other landmark.  I find that the usage of Bookmarks, when possible, is the most reliable method.  So we'll assume that we started that block on our template with the bookmark InsertRange.  Let's look at some code.  First, a little more scaffolding code to add to our WordDocument class:

protected Object GetEndOfRange(Range dataRange)
{
Object start = dataRange.End - 1;
Object end = dataRange.End - 1;
Range rng = wordDocument.Range(ref start, ref end);
Object range = rng;
return range;
}

This code is used to find the end of a Range, which becomes very important when we are creating ranges to manipulate on the fly, and the following code works in tandem with this to create dynamic bookmarks, which we can use to find our way around a document as we meddle with it:

protected void CreateBookmark(string bookmarkName, object bookmarkRange)
{
wordDocument.Bookmarks.Add(bookmarkName, ref bookmarkRange);
}

Okay, with that out of the way, let's generate what we came up with in the example above.

 

   1:  Object bookmark = "InsertRange"; //The name of our landmark bookmark
   2:  //Get our primary insert Range
   3:  Range insertRange = wordDocument.Bookmarks.get_Item(ref bookmark).Range;
   4:  //Get end of our range
   5:  Object endInsertRange = GetEndOfRange(insertRange);
   6:   
   7:  //Ok, now we start the real work.  We're creating
   8:  //new bookmarks on the fly to insert the various text data
   9:   
  10:   //this puts our bookmark at the end of the main insert range.
  11:  bookmark = "HeaderText"; //make an object of the name of our new range
  12:  CreateBookmark("HeaderText", endInsertRange);
  13:  //Now we get the range of the bookmark we just created...
  14:  Range bookmarkRange = wordDocument.Bookmarks.get_Item(ref bookmark).Range;
  15:  //Now you can interact directly with the range
  16:  bookmarkRange.Text = "This is the header text, which is bold";
  17:  bookmarkRange.Font.Bold = 1; //word interop bools...1=true, 0=false
  18:  bookmarkRange.Font.Size = 14; //If you want to change the font size.
  19:   
  20:  //ok this part gets confusing.  We are going to make a paragraph mark
  21:  //at the end of the primary insert range, not the one we just created.
  22:  //the primary insert range now contains the range we just made, and has adjusted its size accordingly
  23:   
  24:  insertRange.InsertParagraphAfter();
  25:   
  26:  endInsertRange = GetEndOfRange(insertRange); //find the new end of the main range.  
  27:  bookmark = "SummaryText";
  28:  CreateBookmark("SummaryText", endInsertRange); //lather rinse repeat as needed
  29:  bookmarkRange = wordDocument.Bookmarks.get_Item(ref bookmark).Range;
  30:  bookmarkRange.Text = "This is the italicized discription text";
  31:  //Now the main thing to remember is that you are currently inheriting any property you
  32:  //explicitly set in the previous range here.
  33:  bookmarkRange.Font.Bold = 0; //reset bold because we don't want that
  34:  bookmarkRange.Font.Size = 12; //reset the size too
  35:  bookmarkRange.Font.Italic = 1;
  36:  bookmarkRange.ParagraphFormat.IndentCharWidth(1); //This does an indent to this paragraph style
  37:   
  38:  //and here we go again
  39:  insertRange.InsertParagraphAfter();
  40:  endInsertRange = GetEndOfRange(insertRange);
  41:   
  42:  bookmark = "OtherTextPartOne";
  43:  CreateBookmark("OtherTextPartOne", endInsertRange);
  44:  bookmarkRange = wordDocument.Bookmarks.get_Item(ref bookmark).Range;
  45:  bookmarkRange.Italics = 0; //again reset any property you dont' want for this range
  46:  bookmarkRange.Text = "This is the body text, before the dynamic part";

 

And so on...

As you can see, this starts to be a real pain in the ass when you have a lot of need for control over the text.  Next we would have made a new bookmark at the end of the insert range, got its range, set its color to red, put in the text, got the new insert range end, create a new bookmark, get its range, set its color back to black, and finished the paragraph.  This type of thing gets real old real quick when you are doing a lot of variable text, so again, I beg you, find a way other than word interop.

You'll notice that the code repeats itself a lot, and it can be really easy to get lost in terms of where you are in the document.  You will see some wicked formatting issues come up.  Just remember, everything is a range, and you have to be the master of all of the properties of the current range, as well as the properties of the one prior.  Getting everything exactly right will require a lot of trial and error.   A lot of this could be encapsulated into function calls, but the first few times you will be glad to have it written out the long way as you really get a feel for the range breakdown in your document.

In the next part, I'll talk about working with Tables, finding your way around the table hierarchy, creating and manipulating tables on the fly, and so on.  Happy fun times.


Tags: