programmatically insert mail mergefield into hyperlink in a word document - c#

I'm trying to figure out how to programmatically insert mail mergefield into hyperlink in a word document.
In ms word application this is easily accomplished with the following code when in code-view(ALT+F9):
{HYPERLINK "http://example.com?id={MERGEFILED ID}"}
I consulted stackoverflow and google but came up empty-handed.
How could I accomplish something like above snippet via C# word interoperability library?
Right now this is what I have:
using mso = Microsoft.Office.Interop.Word;
public class Test
{
public void GenerateDynamicHyperlinkWithMergeField()
{
mso.Application app = new mso.Application();
object missing = System.Reflection.Missing.Value;
mso.Document doc = app.Documents.Add(ref missing, ref missing, ref missing, ref missing);
mso.Range range = app.Selection.Range;
// this is hyperlinked correctly
mso.Hyperlink hl = document.Hyperlinks.Add(range, "http://example.com?id=", ref missing, ref missing, "textToDisplay", ref missing);
// this mergfield is outside of hyperlink
mso.MailMerge merge = app.ActiveDocument.MailMerge;
mso.MailMergeField mf = merge.Fields.Add(range, "id");
// inserts mergefield code into hyperlink, but not as recognizable code by word application
mso.Hyperlink hl2 = document.Hyperlinks.Add(range, "http://example.com?id=" + mf.Code.Text, ref missing, ref missing, "textToDisplay", ref missing);
}
}
Any help would be much appreciated.
UPDATE:
To clarify what result is expected in word document;
I want this: {HYPERLINK "http://example.com?id={MERGEFILED ID}"}
But I get this with the above function: {HYPERLINK "http://example.com?id="}{MERGEFILED ID}

Try this:
string myLink = "http://example.com?id=" + mf.Code.Text;
mso.Hyperlink hl2 = document.Hyperlinks.Add(range, myString, ref missing, ref missing, "textToDisplay", ref missing);

This might be a red herring, but have you noticed it says MERGEFILED and not MERGEFIELD?
I only bring it up because it's in all instances you mention it.

Related

How to reverse paragraphs with Interop.Word

I am trying to reverse document paragraphs with the following code:
using Word = Microsoft.Office.Interop.Word;
object filePath = #"input.docx";
Word.Application app = new();
app.Visible = false;
object missing = System.Type.Missing;
object readOnly = false;
object isVisible = false;
Word.Document doc = app.Documents.Open(
ref filePath,
ref missing, ref readOnly, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref isVisible, ref missing,
ref missing, ref missing, ref missing);
try
{
Word.Range cachedPara2 = doc.Paragraphs[2].Range.Duplicate;
doc.Paragraphs[2].Range.FormattedText = doc.Paragraphs[1].Range.FormattedText;
doc.Paragraphs[1].Range.FormattedText = cachedPara2.FormattedText;
doc.SaveAs(#"output.docx");
}
finally
{
doc.Close();
app.Quit();
}
I expect this:
but the actual result is this:
How to get expectations?
UPDATE
With the answer below, I was able to get the expected result for my first case.
Now, in another case, I wanna do the following:
Unfortunately, I couldn't quite figure it out how .Collabse() method works. I am trying to do it with .InsertParagraphAfter():
doc.Paragraphs[2].Range.InsertParagraphAfter();
doc.Paragraphs[3].Range.FormattedText = doc.Paragraphs[5].Range.FormattedText;
doc.Paragraphs[5].Range.FormattedText = doc.Paragraphs[2].Range.FormattedText;
doc.Paragraphs[2].Range.Delete();
Where does this empty paragraph come from? How avoid it?
A range object does not have any content itself, it merely points to the location of the content, rather like a set of map co-ordinates.
What you need to do is add the content of the second paragraph before the first, which will create a new first paragraph. You can then delete what is now the third paragraph. For example:
Word.Range target = doc.Paragraphs[1].Range;
target.Collapse wdCollapseStart;
target.FormattedText = doc.Paragraphs[2].Range.FormattedText;
doc.Paragraphs[3].Range.Delete;

How to compare image (Shape) present in each page of word document through Microsoft.Interop.Word using C#.Net?

I am using following code to replace image (Shape in Microsoft.Interop.Office.Word) of the word document with new image but what the requirement from client is that I need to check the 1st Image of the 1st page of the word document and then compare this image with image of the rest of the document and if match it get replaced with new image else not so need help on how can we compare two shapes(Images)
public void ReplaceWordImage(string FilePath)
{
Word.Document d = new Word.Document();
Word.Application WordApp;
WordApp = new Microsoft.Office.Interop.Word.Application();
bool headerImage = false;
try
{
object missing = System.Reflection.Missing.Value;
object yes = true;
object no = false;
object filename = #"D:/ImageToReplace/5.docx";
d = WordApp.Documents.Open(ref filename, ref missing, ref no, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref yes, ref missing, ref missing, ref missing, ref missing);
List<Word.ShapeRange> ranges = new List<Microsoft.Office.Interop.Word.ShapeRange>();
List<Word.ShapeRange> headerRanges = new List<Microsoft.Office.Interop.Word.ShapeRange>();
foreach (Word.Shape shape in d.Shapes)
{
if (shape.Type == Microsoft.Office.Core.MsoShapeType.msoPicture)
{
shape.Delete();
foreach (Word.Range r in ranges)
`enter code here` {
r.InlineShapes.AddPicture(#"D:\Untitled.jpg", ref missing, ref missing);
break;
}
}
The Word object model doesn't provide anything to compare two images. The best what you could do is to save both on the disk and then try comparing the bytes representation of both. However, there is a better way to get the job done. The answer is the Open XML SDK which allows getting the bytes representation of images on the fly without saving them to a disk before. The Open XML SDK contains a class WordprocessingDocument that can manipulate a memory stream containing a WordDocument content. And MemoryStream can be converted using ToArray() to a byte[]. See Convert Word of interop object to byte [] without saving physically for more information.

Checkbox disabled for Word document created using Microsoft.Office.Interop.Word

I am trying to create a Word document with checkboxes in it using Microsoft.Office.Interop.Word. I have used the following references to do so:
Create a Word document in C#
Create and edit a CheckBox in Word with c#
I have successfully generated a Word document with a checkbox but unfortunately it is disabled. What I am trying to achieve is to have a checkbox that can be checked/unchecked.
In the screenshot below, you can see that I have 3 checkboxes. The 1st one is generated using Microsoft.Office.Interop.Word and the 2nd and 3rd ones were created manually in Word 2016. The first one cannot be marked as checked/unchecked while the 2nd and 3rd ones behave just like a normal checkboxes
And this is the code I used to generate the Word document.
private void btnCreateWordInterop_Click(object sender, EventArgs e)
{
Word._Application word_app = new Word.ApplicationClass();
word_app.Visible = true;
object missing = Type.Missing;
Word._Document word_doc = word_app.Documents.Add(ref missing, ref missing, ref missing, ref missing);
Word.Paragraph para = word_doc.Paragraphs.Add(ref missing);
para.Range.Text = "Chrysanthemum Curve";
object style_name = "Heading 1";
para.Range.set_Style(ref style_name);
para.Range.InsertParagraphAfter();
//Microsoft.Office.Interop.Word.Range range =
para.Range.Collapse(ref missing);
Word.FormField checkBox = word_doc.FormFields.Add(para.Range, Word.WdFieldType.wdFieldFormCheckBox);
para.Range.InsertAfter(" Checkbox generated by Microsoft.Office.Interop.Word");
// Save the document.
object filename = #"C:\Users\Username\Desktop\InteropWord.docx";
word_doc.SaveAs(ref filename, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing);
//Close.
object save_changes = false;
word_doc.Close(ref save_changes, ref missing, ref missing);
word_app.Quit(ref save_changes, ref missing, ref missing);
MessageBox.Show("Saved");
}
How can I make the generated checkbox enabled?
Instead of using FormFields I'd recommend using Content Controls for this. These are more 'User Friendly' and easier to work with in general.
Change this line:
Word.FormField checkBox = word_doc.FormFields.Add(para.Range, Word.WdFieldType.wdFieldFormCheckBox);
Using a Content Control it would be something like (from the top of my head)
Word.ContentControl checkbox = para.Range.ContentControls.Add(Word.WdContentControlType.wdContentControlCheckBox);

Read a Word Document Using c#

I need to start reading a word document from a specific point.
That key word is taken from a dropdown combo box.
The keyword is something like [blah blah, blah, 001]
So, I need to read only the content from that keyword to next heading ...
I used this to read heading numbers and line by line
but heading num notworking
string headNum = objparagraph.Range.ListFormat.ListString;
string sLine = objparagraph.Range.Text;
Word.Application word = new Word.Application();
Word.Document doc = new Word.Document();
object fileName = #"C:\wordFile.docx";
// Define an object to pass to the API for missing parameters
object missing = System.Type.Missing;
doc = word.Documents.Open(ref fileName,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing);
string ReadValue = string.Empty;
// Activate the document
doc.Activate();
foreach (Word.Range tmpRange in doc.StoryRanges)
{
ReadValue += tmpRange.Text;
}
If I understood correctly, you need to read the Word document starting from your keyword to next heading. In other words, something like the red text in the following document:
In that case, here is how you can accomplish that with GemBox.Document:
string keyword = " [blah blah, blah, 001]";
DocumentModel document = DocumentModel.Load("input.docx");
ContentPosition start = document.Content
.Find(keyword)
.First()
.End;
ContentPosition end = new ContentRange(start, document.Content.End)
.GetChildElements(ElementType.Paragraph)
.Cast<Paragraph>()
.First(p => p.ParagraphFormat.Style != null && p.ParagraphFormat.Style.Name.Contains("heading"))
.Content
.Start;
string text = new ContentRange(start, end).ToString();
The text variable's value will be:
Sample text content that we want to retrieve.
Another sample paragrap.
Also, here are additional Reading and Get Content examples, they contain some useful information.

Need suggestions on how to extract data from .docx/.doc file then into SQL Server

I'm suppose to develop an application for my project, it will load past-year examination / exercises paper (word file), detect the sections accordingly, extract the questions and images in that section, and then store the questions and images into the database. (Preview of the question paper is at the bottom of this post)
So I need some suggestions on how to extract data from a word file, then inserting them into a database. Currently I have a few methods to do so, however I have no idea how I could implement them when the file contains textboxes with background image. The question has to link with the image.
Method One (Make use of ms office interop)
Load the word file -> Extract image,
save into a folder -> Extract text,
save as .txt -> Extract text from .txt then store in db
Questions:
How do I detect the section and question?
How do I link the image to the question?
Extract text from word file (Working):
private object missing = Type.Missing;
private object sFilename = #"C:\temp\questionpaper.docx";
private object sFilename2 = #"C:\temp\temp.txt";
private object readOnly = true;
object fileFormat = Word.WdSaveFormat.wdFormatText;
private void button1_Click(object sender, EventArgs e)
{
Word.Application wWordApp = new Word.Application();
wWordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
Word.Document dFile = wWordApp.Documents.Open(ref sFilename,
ref missing, ref readOnly, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing);
dFile.SaveAs(ref sFilename2, ref fileFormat, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,ref missing,
ref missing,ref missing,ref missing,ref missing,ref missing,
ref missing,ref missing);
dFile.Close(ref missing, ref missing, ref missing);
}
Extract image from word file (doesn't work on image inside textbox):
private Word.Application wWordApp;
private int m_i;
private object missing = Type.Missing;
private object filename = #"C:\temp\questionpaper.docx";
private object readOnly = true;
private void CopyFromClipbordInlineShape(String imageIndex)
{
Word.InlineShape inlineShape = wWordApp.ActiveDocument.InlineShapes[m_i];
inlineShape.Select();
wWordApp.Selection.Copy();
Computer computer = new Computer();
if (computer.Clipboard.GetDataObject() != null)
{
System.Windows.Forms.IDataObject data = computer.Clipboard.GetDataObject();
if (data.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
{
Image image = (Image)data.GetData(System.Windows.Forms.DataFormats.Bitmap, true);
image.Save("C:\\temp\\DoCremoveImage" + imageIndex + ".png", System.Drawing.Imaging.ImageFormat.Png);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
wWordApp = new Word.Application();
wWordApp.Documents.Open(ref filename,
ref missing, ref readOnly, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing);
try
{
for (int i = 1; i <= wWordApp.ActiveDocument.InlineShapes.Count; i++)
{
m_i = i;
CopyFromClipbordInlineShape(Convert.ToString(i));
}
}
finally
{
object save = false;
wWordApp.Quit(ref save, ref missing, ref missing);
wWordApp = null;
}
}
Method Two
Unzip the word file (.docx) -> Copy the media(image) folder, store somewhere -> Parse the XML file -> Store the text in db
Any suggestion/help would be greatly appreciated :D
Preview of the word file:
(backup link: http://i.stack.imgur.com/YF1Ap.png)
The answer is choice #3 - the OpenXML SDK. First let me explain why you don't want the choices listed above.
Running Office on the server is a bad idea. Microsoft specifically says don't do it. It's slow and you will hit "issues" where it throws exceptions or just fails to find things.
Parsing the XML file will work but the XPath to find every possible case where the images, etc. are located adds up. You would probably have to iterate on sections, which come at the end of each section, then handle all cases of in a cell, in a textbox, positioned, inline, etc.
If you go with the OpenXML SDK you have a LINQ interface where you can then use the Descendents and get everything that is an image (or whatever you need). It also gives you sections by the SectPr node so you can easily iterate over sections.

Categories