Hi,
I’m having an issue right now with document creation. I’ve created a Word docx document containing several MergeField elements. My document content’s is saved to my db, and later on I retrieve it somewhere else in my code using
MemoryStream myStream = new MemoryStream(byte[]);
Document doc = new Document(myStream);
When I try to access my form merge fields using doc.Range.FormFields, the list is empty. I’m wondering why because I can still see the formating of my MergeField elements when I do doc.Range.GetText();
Could you please help me figure this out. I’ve been struggling for some time. See below for the content of my document after I access its Range’s text. I’ve also attached a screenshot of my content where we can see my MERGE FIELD elements’ tags.
Thank you very much for you help.
Pat.
Raw Content:
PAGE * MERGEFORMAT 1
Evaluation Only. Created with Aspose.Words. Copyright 2003-2010 Aspose Pty Ltd.
2011 Marine Liability Quote
MERGEFIELD FieldAMapped \m * MERGEFORMAT «FieldAMapped» MERGEFIELD FieldB * MERGEFORMAT «FieldB» MERGEFIELD FieldC * MERGEFORMAT «FieldC» MERGEFIELD FieldDMapped \m * MERGEFORMAT «FieldDMapped»
Assured: [[InsuredCompanyName]]
[[InsuredAddress]]
[[InsuredCity]], [[InsuredState]], [[InsuredZip]]
[[InsuredContactFirstName]] [[InsuredContactLastName]]
Coverage(s): Commercial Marine Liability
Effective: 12 Months at [[PolicyEffectiveDate]] (MM/DD/YYYY)
Deductible: $ per occurrence
Commission: [[DistributorCommission]] %
Premium: $ [[TotalGrossPremiumAmount]]
Security: Starr Indemnity & Liability Company (A.M. Best A, FSC X)
Coverage Premiums:
Coverage TypeaGross Premiumaa[[OW_REPEAT_BEGIN]] [[Premium_PremiumType]]a$a[[Premium_Amount]]aa[[OW_REPEAT_END]] State Surchargea$a[[TotalTaxesAmount]]aaTotala$a[[TotalGrossPremiumAmount]] aa
Limits:
Coverage PartaCoverage TypeaLimitaa[[OW_REPEAT_BEGIN]][[Limit_PremiumType]]a[[Limit_Description]]a$a[[Limit_Amount]]aa[[OW_REPEAT_END]]aaaaa
Conditions:a[[InsuringConditions]]aa
Hi Patrick,
Thanks for your request. MergeFields and FormFields are completely different things. That is why MergeFields are not listed in the collection of FormFields. If you need to get a list of MergeField names, you can use Document.MailMerge.GetFieldNames() method. To fill these fields with data you should use Mail Merge feature as described here:
https://docs.aspose.com/words/net/types-of-mail-merge-operations/
Hope this helps. Please let me know if you need more information, we are always glad to help you.
Best regards,
Hi Alexey
Thank you very much for you very quick reply.
This is exactly what I was looking for. I can now get access to my fields.
I’ve another question then, if you permit: If I want to repeat data anywhere in my document base on a datasource’s records I’m using, do I necessarily need to put them into a table Object ?
I mean, do I need to draw a table in my word document, or can I just use TableStart and TableEnd anywhere and insert bullet points (an example) if I need or repeat rows if I need…
Thank you !
Patrick Goudjo-Ako
Hi
Thanks for your request. I think, in this case, you can use mail merge with regions to fill the repetitive part of your document. Please see the following link for more information:
https://docs.aspose.com/words/net/types-of-mail-merge-operations/
Your template should look like the following:
Column1 |
Column2 |
|
Column3 |
Column4 |
{ MERGEFIELD TableStart:myData }{ MERGEFIELD Column1 } |
{MERGEFIELD Column2 } |
|
{MERGEFIELD Column3 } |
{MERGEFIELD Column4 }{ MERGEFIELD TableEnd:myData } |
Hope this helps. Please let me know if you need more assistance, I will be glad to help you. |
|
|
|
|
Best regards, |
|
|
|
|
Hi Alexey,
Do you know what’s the best way to dynamically insert MergeFields from my .NET code ?
I’ve created .NET Ms Office component which I’m actually using to insert specific tags in any Word document. Later on, using another code, I parse those tags, looking for example for [[ and ]] as my delimiters and I replace those tags with some specific data.
Now that I want to use MergeFields to do that, this will work well if I manually create my fields directly inside my word document. But what if I want to create them from my C# code so that the end-user can still use my .NET Office tool ?
Thanks in advance for the great work you do at Aspose !
Patrick Goudjo Ako.
P.S.: My company just updated its Aspose components license to the latest one and I can see that you’ve fixed a lot of issues. Thanks for the good work !!!
Hi Andrey,
thanks for the quick reply and the link. This answer a good part of my question.
The other part is: can I use MailMerge with regions without drawing a table inside my Word document ?
Let’s say I’ve bullets points and I want to repeat data inside those bullet points, can I still use merge fields with region ?
The data I want to repeat can be of any kind.
Thanks !
Patrick Goudjo Ako
Hi
Thanks for your request. In your case, you can use code like the following to replace text placeholders with merge fields at run-time:
// Open document.
Document doc = new Document("C:\\Temp\\in.doc");
Regex regex = new Regex(@"\[(?\S+)\]", RegexOptions.IgnoreCase);
// Find and replace placeholders
doc.Range.Replace(regex, new ReplacePlaceholdersWithMergFields(), false);
// Save output document
doc.Save("C:\\Temp\\out.doc");
private class ReplacePlaceholdersWithMergFields : IReplacingCallback
{
public ReplaceAction Replacing(ReplacingArgs e)
{
// Get name of placeholder.
string name = e.Match.Groups["name"].Value;
// If name is empty string, then do nothing.
if (string.IsNullOrEmpty(name))
return ReplaceAction.Skip;
// This is a Run node that contains either the beginning or the complete match.
Node currentNode = e.MatchNode;
// The first (and may be the only) run can contain text before the match,
// in this case it is necessary to split the run.
if (e.MatchOffset > 0)
currentNode = SplitRun((Run)currentNode, e.MatchOffset);
// Create DocumentBuilder object, which will help us to insert filds.
DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
// Move builder cursor to the current node.
builder.MoveTo(currentNode);
// Insert mergefield.
builder.InsertField(string.Format("MERGEFIELD {0}", name), string.Format("«{0}»", name));
// This array is used to store all nodes of the match for further removing.
ArrayList runs = new ArrayList();
// Find all runs that contain parts of the match string.
int remainingLength = e.Match.Value.Length;
while (
(remainingLength > 0) &&
(currentNode != null) &&
(currentNode.GetText().Length <= remainingLength))
{
runs.Add(currentNode);
remainingLength = remainingLength - currentNode.GetText().Length;
// Select the next Run node.
// Have to loop because there could be other nodes such as BookmarkStart etc.
do
{
currentNode = currentNode.NextSibling;
}
while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
}
// Split the last run that contains the match if there is any text left.
if ((currentNode != null) && (remainingLength > 0))
{
SplitRun((Run)currentNode, remainingLength);
runs.Add(currentNode);
}
// Now highlight all runs in the sequence.
foreach (Run run in runs)
run.Remove();
// Signal to the replace engine to do nothing because we have already done all what we wanted.
return ReplaceAction.Skip;
}
}
///
/// Splits text of the specified run into two runs.
/// Inserts the new run just after the specified run.
///
private static Run SplitRun(Run run, int position)
{
Run afterRun = (Run)run.Clone(true);
afterRun.Text = run.Text.Substring(position);
run.Text = run.Text.Substring(0, position);
run.ParentNode.InsertAfter(afterRun, run);
return afterRun;
}
I use placeholders like the following [PlaceholderName]
Also, I attached my input and output documents.
Hope this helps.
Best regards,
And yes, you can use any construction between TableStart/TableEnd.
Best regards,
Hi Andrey,
thank you very much, this helps me a lot !!!
I’m going to put my brain together now and come up with a nice suitable solution for my needs.
I’ll keep you guys posted and let you know if I’ve questions.
Thanks !!!
P.
Hi Andrey,
I’ve another question.
When I get into ReplacePlaceholdersWithMergFields
from your previous code, e.MatchNode gives me a pointer to the run which starts my matching result.
Let say for any reason, I want to replace or duplicate all the runs containing in my match.
How can I programmatically access all the runs from the first to the last, but only those corresponding to my match ?
I will split run if necessary for my first run depending on the MatchOffset or if there is excedent data in my last run.
I just want to create a collection composed of runs which match my search.
Thanks !
P.
Hi Andrey,
I just want to add one more thing. I asked about what to do to access the collection of runs which match my search. I can see from your code above that ‘runs’ will contain them.
However, the issue is that this will work only if all my runs are contained inside the same paragraphs, because currentNode.NextSibling will return the next run inside the same paragraph. Therefore, it will return null if we are at the end of the paragraph, even we’ve not reached the end of my search match.
This can happen if my text is in more than 1 paragraph one inside a Table object or another structure.
In this case, How can I still obtain a list of all runs matching my search.
Thanks again for your time and quick responses.
Patrick Goudjo Ako.
Hi Patrick,
Thanks for your request. At the moment, Aspose.Words does not allow find/replace text that is spanned into few paragraphs. So matched text is always in a single paragraph. Please see remarks in the following article:
https://reference.aspose.com/words/net/aspose.words/range/replace/
Best regards,
Hi Alexey,
thanks for this info. I just didn’t realize it.
I knew it wasn’t working but didn’t think it was a limitation.
That’s why I’m using Shift+Enter to insert new lines into same paragraphs, instead of just Enter which will insert new paragraphs. It works this way.
My issue is I can’t force the end-users to always do that.
So I have to think of a way to make it work even with different paragraphs.
Here is what I’m planning to do:
After I have a reference to my first run with e.MatchNode, I will just go go (.ParentNode) until I find a node which contains all my matched text.
Then, I will clone that node and in the new node, I will replace new paragraphs special characters by new line special characters. I will then insert that node just after the parent node I cloned and remove that parent node.
After that, I will make my replacement.
What do you think about it ?
Thanks for all your support. You really help me !
Patrick.
Hi Patrick,
Thanks for your request. I think, you can use a similar approach suggested here to extract content between user defined strings:
https://forum.aspose.com/t/99344However, you should note that there were few changes in Aspose.Words API. So you should use IReplacingCallback instead of ReplaceEvaluator:
https://reference.aspose.com/words/net/aspose.words.replacing/ireplacingcallback/
Hope this helps. Please let me know if you need more assistance, I will be glad to help you.
Best regards,
Hi Alexey,
thanks for proposing me this alternative. However, what I can see from the code you pointed me to is that this will work perfectly only if all my text contained inside my delimitors are inside the same paragraphs.
Am I wrong ?
If this is the case then, my issue remain which is: how do I get all the nodes/runs containing text which is splitted into more than 1 paragraph ?
Here is a description of what I’m doing. Let say I’ve this text:
[[DELIMITER_START]][[VariableToEvaluate]]text text text
text text text
texttext
[[DELIMITER_END]]
DELIMITER_START and DELIMITER_END defines a portion of my document that I want to completely remove if VariableToEvalue is false (I have an internal mechanism to evaluate that) or display if VariableToEvaluate is true.
Actually It works fine with some house-made code if all my text between my delimiters are in the same paragraphs. But It won’t work if my text is splitted between more than 1 paragraph.
Considering the problem I’m trying to solve, do you think MailMerge or any other Aspose.Words functionality could help me ? I want to remove a portion of text from my document and that text can be spanned into more than 1 document.
Please let me know and I’ll provide more explanations if necessary.
If I keep my text, I will remove the delimiters and also VariableToEvaluate.
Yes, I’m refactoring the code for using CallBacks instead of ReplaceEvaluator as done in Aspose 10.
Thank you very much for your close support !
Patrick Goudjo Ako
Hello
Thanks for your request. I think in this case you can try using the same approach as suggested here:
https://docs.aspose.com/words/net/how-to-extract-selected-content-between-nodes-in-a-document/
Best regards,
Hi Patrick,
Thank you for additional information. If you need to conditionally remove/hide part of content in your document, then maybe you can consider using IF fields. For instance, please see the attached template. If you press Alt+F9 you will see field codes. If condition is true you will see the first text if false the second. Here is a simple code I used for testing:
Document doc = new Document(@"Test001\in.doc");
doc.MailMerge.Execute(new string[] {"test"}, new object[] {"true"});
doc.Save(@"Test001\out.doc");
Hope this approach could help you.
Best regards,
Hi Andrey,
thanks for pointing me to this code.
I’m taking a look at it.
Best regars,
Patrick.
Hi Alexey,
thanks for your hints.
I’ll give a deeper look to the IF field in Word.
I’ll let you know if I have any issue.
Best regards,
Patrick.
Hi Alexey,
quick question with IF fields: what happens if the text that I want to show or hide depending on my IF condition is already contained in my Word document ? Is it possible to have that portion of text automatically follow my IF condition inside my word, but not define it inside my MergeField ?
This is necessary if that text to show or hide contains paragraphs, multi lines or other elements.
What do you think about it ?
Thanks !
Patrick.