Mail merging to multiple text boxes Word - c#

I have a template Word document which contains multiple text boxes on top of shapes (to give it a better border outline than what can be achieved my the outline of a text box). These text boxes contain mail merge fields that I wish to merge to. I have the following code in an attempt to do this
foreach (Microsoft.Office.Interop.Word.Range range in document.StoryRanges)
{
foreach (Microsoft.Office.Interop.Word.Field field in range.Fields)
{
if (field.Code.Text.Contains("Test Field"))
{
field.Select();
application.Selection.TypeText("test");
}
}
The problem is this only changes the fields within the first text box, I have searched both on here and MSDN for a solution, however I am still having trouble actually finding a solution. I have also added the following lines in an attempt to figure out something
Console.WriteLine(document.StoryRanges.Count);
And within the foreach loop I also have
Console.WriteLine(range.Fields.Count);
The first call to WriteLine indicates there are two StoryRanges, one being the main document, the other being the range that all the text boxes are on, I presume. However, the second WriteLine indicates the first range has 0 fields, whereas the second range only has 1 field, even though the template document I am using contains over 10 fields.

Are the StoryRanges nested ? Are they of different type?
If so, you should consider using StoryRange.NextStoryRange as proposed here:
http://word.mvps.org/faqs/customization/ReplaceAnywhere.htm
'Iterate through all story types in the current document
For Each rngStory In ActiveDocument.StoryRanges
'Iterate through all linked stories
Do
With rngStory.Find
.Text = "find text"
.Replacement.Text = "I'm found"
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
End With
'Get next linked story (if any)
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next

Related

InlineShape not added to Range after calling AddPicture in Microsoft Word

I'm creating a Microsoft Word 365 Add-in where I need to be able to add and remove inline shapes. I currently have the following test code:
bookmark.Range.InlineShapes.AddPicture("c:\\temp\\test.png");
And although the InlineShape gets added to the page, and seemingly on the correct position, i.e. within the provided bookmark, the bookmark.Range.InlineShapes collection stays empty:
Assert.IsTrue(bookmark.Range.InlineShapes.Count > 0); // This fails
As far as I can see, the shape is actually not added to the range, but directly after it.
This behavior is odd and causes problems in my situation, where I need to be able to iterate the inline shapes of the bookmark later on, especially to be able to remove (toggle) the image again. But without the inline shape as part of the bookmark
What am I doing wrong and how can I fix this, in such way that the inline shape is becoming part of the bookmark again?
That's because your code doesn't insert the shape within the bookmark. This is a quite common misunderstanding. You need to first point a Range object to the bookmark range. Then, add the InlineShape to your Range object's range, then extend the range by one character. Finally, re-apply the bookmark to the Range object's range.
Following is a VBA example:
Sub Demo()
Dim wdRng As Word.Range, BkMkNm As String
BkMkNm = "MyBookmark"
With ActiveDocument
'Confirm that the bookmark exists
If .Bookmarks.Exists(BkMkNm) Then
Set wdRng = .Bookmarks(BkMkNm).Range
'Delete existing contents
wdRng.Text = vbNullString
'Insert Pic
.Range.InlineShapes.AddPicture FileName:="PictureFullName", Range:=wdRng
'Extend Range
wdRng.End = wdRng.End + 1
'Re-apply bookmark
.Bookmarks.Add BkMkNm, wdRng
End If
End With
End Sub

How to find the line number using Word API?

I want to know the line number in the Word document from Word.Paragraph or Word.Range, but there are no suitable fields and methods.I'm using C#.
Getting the line number involves using an old part of the Word object model that comes from the Word Basic days: the Information property. Since C# doesn't "like" properties with arguments, it's the get_Information method for C#
int lineNumberSelection = WordApp.Selection.get_Information(Word.WdInformation.wdFirstCharacterLineNumber);
int lineNumberRange = myParagraph.Range.get_Information(Word.WdInformation.wdFirstCharacterLineNumber);
This returns the numbering as set in the document - it's "What you see is what you get". So if the numbering is set to restart on each page or for each section or each page - that's the result returned.
If a different result is required, the numbering rule for the document must be changed (and can be changed back again, after). For example:
wdDocument.PageSetup.LineNumbering.RestartMode = Word.Wdnumberingrule.wdRestartContinuous;

How to get words from current visible page of ms word document?

I'm developing C# addin for MS Word. I can grab all words of current document - it's something like that:
app = (Word._Application )Application; // Application object comes on addin's connection
foreach(Word.Word word in app.Application.Words)
{
doSmth(word);
}
My question, is how to grab all words not from entire document but from current active(visible for user) page?
In other words, I need to define active page/paragraph of app.Application.ActiveDocument and do something with "active" words.
Interesting question. [See update at end]
Word's object model doesn't really have a "page" object, because the pagination of the document is constantly changing as you add and remove content (or change the font size, the paper size, etc.). So, there is no "ActiveDocument.Pages(1)" sort of thing.
What's more, there's no easy way to tell what page is currently displayed. In part, that's because the user doesn't necessarily see only one page at a time. He may be viewing the end of one page and the start of the next, or several pages may be displayed - depending on his view settings.
If I can make the question slightly easier, then perhaps I can answer it in a way that helps you. Let me re-define "current active (visible for user) page" as the page where the selection is. (Actually, since the selection can span several pages, let's define it as "the page where the active end of the selection is").
I'll also answer using VBA because it's easier to play around with it in the VBA immediate window, and it's trivial to convert to C# when you need to (it's the same object model, after all).
Word's Selection object has the properties of a Range, and if you simply wanted all the selected words, then this would be trivial (Selection.Words!). However, if we want all the words on that page, then we need to work a little harder.
First, let's find out what page the (start of the) selection is on. For this, we can use the Information method:
pageNumber = Selection.Information(wdActiveEndPageNumber)
So now we know what page we're interested in. We now need to get a Range object that includes all the text on that page. We need to do this in two steps - by finding first the start and then the end of that range.
To find the start of the range, we can use the Goto function, which returns a Range object representing the start of a specified item:
startOfRange = ActiveDocument.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, pageNumber).Start
The end of the range is either the start of the next page (minus one character, but let's not quibble), or the end of the document (if we're on the last page):
If pageNumber = ActiveDocument.Content.Information(wdNumberOfPagesInDocument) Then
endOfRange = ActiveDocument.Content.End
Else
endOfRange = ActiveDocument.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, pageNumber + 1).Start
End If
Now we can construct a Range object encompassing all the text on the page:
Set pageRange = ActiveDocument.Range(startOfRange, endOfRange)
... and from there we can get the words:
Set words = pageRange.Words
Here is a short VBA macro that uses the above technique to report the number of words on the active page:
Sub Test()
Dim pageNumber As Integer
Dim startOfRange As Integer
Dim endOfRange As Integer
Dim pageRange As Range
pageNumber = Selection.Information(wdActiveEndPageNumber)
startOfRange = ActiveDocument.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, pageNumber).Start
If pageNumber = ActiveDocument.Content.Information(wdNumberOfPagesInDocument) Then
endOfRange = ActiveDocument.Content.End
Else
endOfRange = ActiveDocument.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, pageNumber + 1).Start
End If
Set pageRange = ActiveDocument.Range(startOfRange, endOfRange)
MsgBox pageRange.Words.Count
End Sub
UPDATE
OK, it turns out that there's a much easier way to do this. Word has a "special bookmark" that points to the text on the current page, so this will do the same as all that code above:
words = ActiveDocument.Bookmarks("\page").Range.Words

How do I text wrap to next acrofield in iTextSharp?

how do I get text to wrap from one acrofield to the next? I have an adobe pdf doc our client gave us. It has acro fields one atop another (all with the same name). They want the text to wrap from one to another when it reaches the end of the line. All the other examples I see out there do not deal with filling in acro fields that wrap. Please help!
// loop through disabilities and display them
foreach (var disability in formNature.Disabilities)
{
fields.SetField("EVALUATION", disability.PrimaryDisabilityName + "; ");
}
in theory this should loop through all the disabilities they had entered on the web form and display them one after another while text-wrapping when it reaches the end of each line. But instead it only displays one item one the field.
This isn't a complete answer unfortunately.
First, when you call SetField() you are erasing the current contents of the field and replacing it with your new value. When done in a loop only the last value will ever be stored then. What you need to do is loop through each value and concatenate them into one big string.
string buf = '';
foreach (var disability in formNature.Disabilities)
{
buf += disability.PrimaryDisabilityName + "; ";
}
buf = buf.Trim();
Second, the PDF standard to the best of my knowledge does not support chaining of fields for overflow which is what you are looking for. The only way that I know of to accomplish what you are trying is to actually measure the strings and compare them to the widths of the fields and truncate them as needed. To do this you will need to find the font used for the given field, create a BaseFont from it and use that to Measure the string. Then compare that with the field's rectangle and use only the characters that "fit" into that field. Repeat as needed.
That all said, I would really really recommend that you just edit the PDF and replace the multiple fields with one large field that supports multiple lines. Your life will be much, much easier.

PowerPoint Notes in C#

I want to read the notes of a PowerPoint Slide in C#.
Following Snippet works for me.
slide.NotesPage.Shapes[2].TextFrame.TextRange.Text
However, this doesn't work for some presentations.
Then it throws an "Out of range" exception.
What ist the meaning of index 2? Are there any alternative to do this?
You can't assume that the notes text placeholder will be at any specific index or even that it'll have a specific name. Here's a simple function in VBA that returns the notes text placeholder for a slide:
Function NotesTextPlaceholder(oSl As Slide) As Shape
Dim osh As Shape
For Each osh In oSl.NotesPage.Shapes
If osh.Type = msoPlaceholder Then
If osh.PlaceholderFormat.Type = ppPlaceholderBody Then
' we found it
Set NotesTextPlaceholder = osh
Exit Function
End If
End If
Next
End Function
It means that you are trying to access the third element of the slide.NotesPage.Shapes collection. If the collection has 2 elements or less, the exception is thrown because the element at the specified index 2 could not be accessed since it doesn't exist — you simply cannot retrieve a collection's third element if it doesn't have one.
(The index is zero-based, meaning that the first element is given the index 0, the second one is given the index 1 and so on. Thus, the greatest possible index of a collection with N elements is N-1.)
It's dangerous to try and access an index object without checking if it exists first, since this might throw exceptions. You can check if the slide has notes with the HasNotesPage property of the slide object:
if(slide.HasNotesPage == Microsoft.Office.Core.MsoTriState.msoTrue)
{
}
If you want to get all the notes at once, you might want to use NotesPage property to retrieve a range with all notes.

Categories

Resources