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.
Hi Patrick,
Thanks for your inquiry. But unfortunately, it is not quite clear for me what you mean. You can do not use mergefields in IF fields at all. I put a mergefield just to demonstrate how IF fields work.
Please follow the link to learn more about IF fields:
https://support.microsoft.com/en-us/office/field-codes-if-field-9f79e82f-e53b-4ff5-9d2c-ae3b22b7eb5e
Best regards,
Hi Alexey,
thanks for the link. I actually read a couple of documents about IF, but from my understanding, you need to provide the TrueText and FalseText inside your IF statement, which makes sense.
However, Im wondering how can someone decide that a text which already exist inside his word document will be displayed or not. Let say I have a paragraph of text in my Word document. This paragraph may contain text, images, tables or anything I want. Then I want to display or show this entire block of text (paragraph) or hide it, depending of something I evaluate. Here is an example for normal situations:
{IF "«test»" = "true" "This content will be displayed if contion is true" "Display is false"}
What if, instead of “This content will be displayed if contion is true”, I want to have a block of text containing paragraphs, tables, or anything else ?
Then, I can’t put all that text into by IF field, but it will already exist in my document.
Does this help to better understand my scenario ?
Thanks again !
Patrick Goudjo Ako
Hi Patrick,
Thanks for your inquiry.
If you look carefully you will see that the template provided by Alexey already demonstrates how to insert block level content such as paragraphs into an IF field. With the document open, please toggle field codes by pressing ALT+F9 and you will see that the true text contains two separate paragraphs.
To insert all types of content into the IF field just type normally into the field as if you were normally typing a document.
If you have any further queries, please feel free to ask.
Thanks,