How to add text into Word Documents at a specific position? - c#

How do I write to a specific position in a Word document, for example, line 5, character 50? I have searched for a couple of hours, but couldn't find a solution.
I am using Microsoft.Office.Interop.Word

If you are content with the more simple sentence, rather than lines:
ActiveDocument.Sentences(1).Characters(5).Select
Selection.Collapse
Selection.InsertBefore "added "
Five paragraphs and 50 spaces in VBA
Selection.Text = String(5, vbCrLf)
Selection.Collapse wdCollapseEnd
Selection.Text = String(50, " ")
However, for a particular position, I would prefer a textbox:
Set sh = doc.Shapes.AddTextbox(1, 10, 344, 575, 80)
sh.Name = "Course1"
With some properties:
sh.Fill.Visible = False
sh.Line.Visible = False
sh.TextFrame.MarginLeft = 0#
sh.TextFrame.MarginRight = 0#
sh.TextFrame.MarginTop = 0#
sh.TextFrame.MarginBottom = 0#

If you will be inserting text at the same location every time a simple way to do this is to create a .dotx template file with a bookmark at the location. Make sure the template is included in the build
Doc = Word.Documents.Add("Directory\Filename")
Doc.Bookmarks.Item("BookmarkName").Range.Text = "Text to be inserted"

Finding a position in a word document to insert a table
You may find some useful information in the above location.

Related

Gembox document removes table of contents

I'm using Gembox document to replace some text in a docx document and it works great. However, I have a table of contents field that disappears after saving the document.
I tried doing the following but the field still disappears leaving only the placeholder text:
var toc = (TableOfEntries)document.GetChildElements(true, ElementType.TableOfEntries).First();
toc.Update();
document.GetPaginator(new PaginatorOptions() { UpdateFields = true });
UPDATE (2021-01-15):
Please try again with the latest version from the BugFixes page or from NuGet.
The latest version will work on the machine that uses culture with ';' character as a list separator.
Or you can specify that culture like this:
var toc = (TableOfEntries)document.GetChildElements(true, ElementType.TableOfEntries).First();
CultureInfo.CurrentCulture = new CultureInfo("fr");
toc.Update();
document.GetPaginator(new PaginatorOptions() { UpdateFields = true });
Also, the issue with the missing tab stop should be resolved now as well.
ORIGINAL:
When I tried to update your TOC from MS Word, I got the following:
No table of contents entries found.
After investigating the field's code of your TOC element, I figured out what the problem is.
This is the instruction text that you have:
{ TOC \h \z \t "TitreChapitre;1;SousTitreChapitre;2" }
These semicolon character separators (;) are culture-dependent. In other words, updating this TOC element will work on a machine that has a French region and settings, but it won't work when you have an English region and settings.
I'm currently on vacation, so I can't do anything about this. When I come back I will fix this problem for you.
For now, can you use the following as a workaround (I also noticed an issue with missing TabStop, this workaround will cover that as well):
var toc = (TableOfEntries)document.GetChildElements(true, ElementType.TableOfEntries).First();
var section = toc.Parent as Section;
var tocWidth = section.PageSetup.PageWidth - section.PageSetup.PageMargins.Left - section.PageSetup.PageMargins.Right;
var toc1Style = document.Styles["toc 1"] as ParagraphStyle;
var toc1TabStop = new TabStop(tocWidth - toc1Style.ParagraphFormat.RightIndentation, TabStopAlignment.Right, TabStopLeader.Dot);
toc1Style.ParagraphFormat.Tabs.Add(toc1TabStop);
var toc2Style = document.Styles["toc 2"] as ParagraphStyle;
var toc2TabStop = new TabStop(tocWidth - toc2Style.ParagraphFormat.RightIndentation, TabStopAlignment.Right, TabStopLeader.Dot);
toc2Style.ParagraphFormat.Tabs.Add(toc2TabStop);
toc.InstructionText = toc.InstructionText.Replace(';', ',');
toc.Update();
document.GetPaginator(new PaginatorOptions() { UpdateFields = true });
I hope this works for you.

How to remove paragraph spacing in output using the Microsoft.Office.Interop.Word Library

In my C# Desktop app i am using the Microsoft.Office.Interop.Word library to write to a word file. But whenever its done exporting there's huge spaces between paragraphs. Using the paragraph1.Range.ParagraphFormat.SpaceAfter = 0; parameter doesn't affect the result as well.
My desired result is the "No Spacing" preset in Word but after some research i found out it is not an option this library.
Is there any way to remove or set the spacing between paragraphs.
This is my code:
Microsoft.Office.Interop.Word.Application word = new Application();
word.Visible = false;
var doc = word.Documents.Add();
var paragraph1 = doc.Content.Paragraphs.Add();
paragraph1.Range.Font.Name = "Calibri";
paragraph1.Range.Font.Size = 11;
paragraph1.Range.ParagraphFormat.SpaceBefore = 0;
paragraph1.Range.ParagraphFormat.SpaceAfter = 0;
paragraph1.Range.Text = story;
paragraph1.Range.InsertParagraphAfter();
doc.SaveAs2(#"F:\Documents\Visual Studio Projects\LITPC\LITPC\bin\Debug\"+title+".docx");
word.Quit();
First of all the SpaceBefore and SpaceAfter properties take floats so you need to suffix your values with an f.
Try: paragraph1.SpaceBefore = 0.0f;, that worked for me.
Also, be careful to reset the spacing when you insert a new paragraph because subsequent paragraphs will inherit the new spacing.
Or avoid Interop Word altogether if you can, it's a deep, dark, rabbit hole I've been down for several days.
I solved this problem by changing the Word.Paragraph.SpaceAfter parameter on previous paragraph. For example.
// First Paragraph
Word.Paragraph mainTitle = document.Paragraphs.Add();
mainTitle.Range.Text = "para1";
mainTitle.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
mainTitle.Range.InsertParagraphAfter();
mainTitle.SpaceAfter = 0.0f;
// Second Paragraph
Word.Paragraph semiTitle = document.Paragraphs.Add();
semiTitle.Range.Text = "para2";
semiTitle.Alignment = Word.WdParagraphAlignment.wdAlignParagraphCenter;
semiTitle.Range.InsertParagraphAfter();
semiTitle.SpaceAfter = 10.0f;
So, the SpaceAfter of semiTitle will be 0. Similarly, the SpaceAfter of the next paragraph after semiTitle will be 10.

Insert List<string> into word document

I'm currently trying to find a way to read in, and insert data into a word document. So far this is what I have gotten:
class Program
{
static void Main(string[] args)
{
var FileName = #"C:\temp\test.DOC";
List<string> data = new List<string>();
Application app = new Application();
Document doc = app.Documents.Open(#"C:\temp\test.DOC");
foreach (Paragraph objParagraph in doc.Paragraphs)
{
data.Add(objParagraph.Range.Text.Trim());
}
//data.Insert
data.Insert(16, "Test 1");
data.Insert(16, "\tTest 2\tName\tAmount");
data.Insert(16, "Test 3");
data.Insert(16, "Test 4");
data.Insert(16, "Test 5");
data.Insert(16, "Test 6");
data.Insert(16, "Test 7");
data.Insert(16, "Test 8");
data.Insert(16, "Test 9");
data.Insert(16, "Test 10");
var x = doc.Paragraphs.Add();
x.Range.Text.Insert(0,"\tTest 2\tName\tAmount");
doc.SaveAs2(#"C:\temp\test3.DOC");
((_Document)doc).Close();
((_Application)app).Quit();
}
}
Now, this successfully populates the List data - but I'm trying to append each new test element at the [16]th index, and save it into the word document. Is there a simple way to accomplish this, or am I just over-thinking this issue?
I realize the string list is separate from the Document object which represents the word document.
I have a few other places in the document where I am using bookmarks to add data, but I don't think it is possible to use bookmarks for placing the data in this instance - or If I don't have to use bookmarks I'd like to stray away from that.
EDIT: I am trying to insert X amount of elements at the [16]th position within the data[].
EDIT 2:
Essentially I am sourcing the data dynamically, and I'm not sure how many records/rows I'll need to add to the document, so it could be as follows:
[15]
[16]\tName\tID\tAMOUNT
[17]\tName\tID\tAMOUNT
[18]\tName\tID\tAMOUNT
Since the headers will already be there (NAME,ID,AMOUNT), and each time I run the program I'm not sure how many elements I'll be inserting into the document - so as long as each element is placed under one another, and on the 16th line in the document template I have setup that should accomplish what I am trying to do.
Image 1 - Image into string array
Image 2 - Image after adding content into the string - this is what the resulting document. (this is to be saved)
I'm attempting to put each element ie: Test1 Test2 Test3 in their each own column each (see above)
Again I am totally confused as to why you want to read the word file into a string list array. This simply adds the text you show after line 15 into the word document. You do not specify WHERE Test 1, Test 2, Test3... are coming from.
Edit: Added a try-catch just in case the document does not have at least 16 paragraphs.
static void Main(string[] args)
{
List<string> data = new List<string>();
Application app = new Application();
Document doc = app.Documents.Open(#"C:\temp\test.DOC");
string testRows = "Test 1\n\tTest 2\tName\tAmount\nTest 3\nTest 4\nTest 5\nTest 6\nTest 7\nTest 8\nTest 9\nTest 10\n";
try
{
var x = doc.Paragraphs[16];
x = doc.Paragraphs.Add(x.Range);
x.Range.Text = testRows;
doc.SaveAs2(#"C:\temp\test3.DOC");
}
catch (System.Runtime.InteropServices.COMException e)
{
Console.WriteLine("COMException: " + e.StackTrace.ToString());
Console.ReadKey();
}
((_Document)doc).Close();
((_Application)app).Quit();
}
So what I figured out (for my purposes) is that is is easiest to insert a list of strings into makeshift columns separated by tabs by inserting at specific paragraphs.
Since I am using bookmarks to place text as well - I found it useful to work from a copy of a document instead of worrying about removing/creating bookmarks each time.
When populating the list that you are going to be placing at a specific paragraph mark it is useful to append tab characters as well as newline charters on the fly. Later on this will make it easier to loop through the list and place them nicely on the document.
Depending on the way you are going to go about placing columns some logic will have to be determined to space everything correctly. I did this by creating maximum lengths for columns and trimming, and accommodating for smaller/larger lengths by adding specific amounts of tab characters.
So, my columns I am populating would look like:
myList.Add("\t12345678912345\tJohn Doe\t\t\t\t123456\r\n");
myList.Add("\987654321654987\tJohn Smith\t\t\t\98765\r\n");
These lines would be inserted at paragraph 17 and placed neatly under headers.
Lastly, I decided to use bookmarks to place single lines of text like the date,title, and signature values since those values don't need to be correctly spaced or anything.
At the end I delete the copy of the word document I'm working on, and delete the pdf (since in my case I'm sending it via email)
Thank you for the help #JohnG - I hope this answer might help others who come across it. I removed the try-catch since I'm working from the template as well.
File.Copy(sCurrentPath + "\\" + "testTemplate.DOC", sCurrentPath + "\\" + "test.DOC");
Application app = new Application();
Document doc = app.Documents.Add(sCurrentPath + "\\" + "test.DOC");
foreach (string sValue in myList)
{
var List = doc.Paragraphs[17];
myList = doc.Paragraphs.Add(myList.Range);
myList.Range.Text = sValue;
}
if (doc.Bookmarks.Exists("Date"))
{
object oBookMark = "Date";
doc.Bookmarks.get_Item(ref oBookMark).Range.Text = DateTime.Now.ToString("MM/dd/yyyy");
}
if (doc.Bookmarks.Exists("Signature"))
{
object oBookMark = "Signature";
doc.Bookmarks.get_Item(ref oBookMark).Range.Text = "My Name";
}
if (doc.Bookmarks.Exists("Title"))
{
object oBookMark = "Title";
doc.Bookmarks.get_Item(ref oBookMark).Range.Text = "Title Here";
}
doc.ExportAsFixedFormat(sCurrentPath + "\\" + "test.pdf", WdExportFormat.wdExportFormatPDF);
File.Delete(sCurrentPath + "\\" + "testCopy.DOC");
File.Delete(sCurrentPath + "\\" + "test.pdf");
((_Document)doc).Close();
((_Application)app).Quit();

Range.Find.Execute - Forward only misbehave

I'm using the .Net libraries for Microsoft Word 2010 interoping
Word.Document doc = wb.Documents[1];
Word.Range range = doc.Range();
range.Find.Forward = true;
range.Find.Execute("HELLO");
MessageBox.Show("Start " + range.Start.ToString());
MessageBox.Show("End " + range.End.ToString());
//make sure it won't even look back
range.Start = range.End;
range.Find.Execute("HELLO");
MessageBox.Show("Start " + range.Start.ToString());
MessageBox.Show("End " + range.End.ToString());
if the document has more than one "HELLO" in it, the range that is returned just ignores it, it keeps finding the same string, I keep getting the same range.Start and range.End values over and over again. what am i missing here ?
I isolated the problem and it seem to happen only when there's special characters in the word document, particularity if text is contained inside a custom table. on normal plain documents it works fine.
I ended up sampling the range.Start value after each search, if it was the same as the prior one, I increase it's start range by + 1 and then re-search.
This is done again and again and although it adds a few more searches it's quite stable.
while (range.text.Contains("HELLO"))
{
range.Find.Execute("HELLO");
while ((range.Start < intLastFoundRangeStartValue) && (range.Find.Found))
{
range.Start += intAddToRangeStartValue;
intAddToRangeStartValue++;
range.Find.Execute("HELLO");
}
}
...
In addition as a by-product it relives me from the need to add a counter to the loop.

Cannot change hyperlink style with Word interop without changing the style of the next paragraph

I have a document with a format similar to
Section Heading 1
Paragraph 1
...
Paragraph N
Sub Heading 1
Paragraph 1
...
Paragraph N
What I am trying to do is add a hyperlink from a heading to a reference document. I can add the hyperlink and apply a style to the link but the style gets applied to the section's Paragraph 1 as well as the hyperlink.
Note: WordApp is a singleton wrapper around Microsoft.Office.Interop.Word.Application. The HyperlinkDestionation class just holds the bookmark name and the path for the file that contains the bookmark.
private void LinkHeadings(string file)
{
Document doc = WordApp.Open(file);
for (int i = 1; i <= proposal.Paragraphs.Count; i++)
{
HyperlinkDestination dest = null;
Paragraph paragraph = proposal.Paragraphs[i];
paragraph.Range.Select();
Style style = (Style)paragraph.get_Style();
string styleString = ((Style)paragraph.get_Style()).NameLocal;
string headingText = paragraph.Range.Text.Split(' ')[0];
if (styleString.Contains("Heading"))
{
dest = _hyperlinkDestinations.Find(x => x.HyperlinkText == headingText);
}
if (dest != null)
{
Hyperlink link = WordApp.ActiveWindow.Document.Hyperlinks.Add(WordApp.Selection.Range, Address: dest.FilePath, SubAddress: dest.bookmarkName, TextToDisplay: WordApp.Selection.Text);
link.Range.set_Style(style);
}
}
WordApp.Close(true);
}
My guess is that it has something to do with with the hyperlink anchor. I've also tried deleting the heading first then inserting the hyperlink but it also has the same result.
The basic problem is that you are including the paragraph mark in the Hyperlink field that Word inserts. That pargraph mark will then be hidden when the hyperlink field result is displayed, i.e. the Section Heading 1 para. will actually become part of Paragraph 1. When you apply the style to the selection, the entire paragraph will be affected.
I'm not going to attempt to provide C# here, but here are some suggestions
a. as a rule it is better to work with Range objects in Word than the Selection where possible, and you should be able to do so here.
b. If you apply the Hyperlink to the paragraph without the paragraph marker, the paragraph style will be unchanged, so you should not need to re-apply it
c. So instead of the code starting with "paragraph.Range.Select();" you should be able to use something like this (I leave you to get the C# syntax right - perhaps you can edit this message)
Range r = Paragraph.Range();
string headingText = r.Text.Split(' ')[0];
if (styleString.Contains("Heading"))
// you shoul probably also tst for an empty paragraph here before inserting anything (I leave it to you)
{
dest = _hyperlinkDestinations.Find(x => x.HyperlinkText == headingText);
}
if (dest != null)
{
// Move the end of the range one character towards the beginning
r.MoveEnd(Word.WdUnits.WdCharacter,-1)
Hyperlink link = WordApp.ActiveWindow.Document.Hyperlinks.Add(r, Address: dest.FilePath, SubAddress: dest.bookmarkName, TextToDisplay: r.Text);
}
If your code needs to run internationally and you only need to check paragraphs with the built-in style types Heading 1..Heading 9, then it would also be better to compare the Style.Type to see if it is one of those 9 style types. If you have other style types called "Heading something" that need to be included, then you probably need to check both the Style.Type and the name.

Categories

Resources