Skip to content

Commit 8ae8a0e

Browse files
Merge pull request #514 from SyncfusionExamples/1003564-Apply-custom-formatting-mailmerge-fields
1003564 - Add MailMerge sample with custom field formatting.
2 parents 68da84f + 77c7e92 commit 8ae8a0e

5 files changed

Lines changed: 196 additions & 0 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36518.9 d17.14
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apply-custom-formatting-mailmerge-fields", "Apply-custom-formatting-mailmerge-fields\Apply-custom-formatting-mailmerge-fields.csproj", "{F0C26935-893F-44DC-99CE-8AFEF31BF30D}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{F0C26935-893F-44DC-99CE-8AFEF31BF30D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{F0C26935-893F-44DC-99CE-8AFEF31BF30D}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{F0C26935-893F-44DC-99CE-8AFEF31BF30D}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{F0C26935-893F-44DC-99CE-8AFEF31BF30D}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {04BDD3BB-2942-4C48-8E80-492D6AE1B900}
24+
EndGlobalSection
25+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<RootNamespace>Apply_custom_formatting_mailmerge_fields</RootNamespace>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Syncfusion.DocIO.Net.Core" Version="*" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<None Update="Data\Template.docx">
17+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
18+
</None>
19+
<None Update="Output\.gitkeep">
20+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
21+
</None>
22+
</ItemGroup>
23+
24+
</Project>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using Syncfusion.DocIO.DLS;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
5+
namespace Apply_custom_formatting_mailmerge_fields
6+
{
7+
class Program
8+
{
9+
public static Dictionary<WParagraph, List<KeyValuePair<int, string>>> paratoModifyNumberFormat
10+
= new Dictionary<WParagraph, List<KeyValuePair<int, string>>>();
11+
public static Dictionary<WParagraph, List<KeyValuePair<int, string>>> paratoModifyDateFormat
12+
= new Dictionary<WParagraph, List<KeyValuePair<int, string>>>();
13+
public static void Main(string[] args)
14+
{
15+
// Load the existing word document
16+
WordDocument document = new WordDocument(Path.GetFullPath(@"Data\Template.docx"));
17+
// Enable separate page for each invoice
18+
document.MailMerge.StartAtNewPage = true;
19+
document.MailMerge.MergeField += new MergeFieldEventHandler(MergeFieldEvent);
20+
// Perform mail merge
21+
document.MailMerge.ExecuteGroup(GetInvoiceData());
22+
// Update the merge field results with formatted values.
23+
UpdateMergeFieldResult(true);
24+
UpdateMergeFieldResult(false);
25+
// Save the Word document.
26+
document.Save(Path.GetFullPath(@"../../../Output/Output.docx"));
27+
// Close the document
28+
document.Close();
29+
30+
}
31+
/// <summary>
32+
/// Event handler triggered during mail merge for each merge field.
33+
/// </summary>
34+
/// <param name="sender">The source of the event (MailMerge engine)</param>
35+
/// <param name="args">Provides information about the current merge field.</param>
36+
private static void MergeFieldEvent(object sender, MergeFieldEventArgs args)
37+
{
38+
// Get the mergefield's Owner paragraph
39+
WParagraph mergeFieldOwnerParagraph = args.CurrentMergeField.OwnerParagraph;
40+
// Find the index of the current merge field within the paragraph.
41+
int index = mergeFieldOwnerParagraph.ChildEntities.IndexOf(args.CurrentMergeField);
42+
if (args.FieldName == "Amount")
43+
{
44+
// Check if this paragraph already has an entry in the dictionary.
45+
// If not, create a new list to store field index and field value.
46+
if (!paratoModifyNumberFormat.TryGetValue(mergeFieldOwnerParagraph, out var fields))
47+
{
48+
fields = new List<KeyValuePair<int, string>>();
49+
paratoModifyNumberFormat[mergeFieldOwnerParagraph] = fields;
50+
}
51+
// Add the current merge field's index and field name
52+
fields.Add(new KeyValuePair<int, string>(index, args.FieldValue.ToString()));
53+
}
54+
else if (args.FieldName == "InvoiceDate")
55+
{
56+
// Check if this paragraph already has an entry in the dictionary.
57+
// If not, create a new list to store field index and field value.
58+
if (!paratoModifyDateFormat.TryGetValue(mergeFieldOwnerParagraph, out var fields))
59+
{
60+
fields = new List<KeyValuePair<int, string>>();
61+
paratoModifyDateFormat[mergeFieldOwnerParagraph] = fields;
62+
}
63+
// Add the current merge field's index and field name
64+
fields.Add(new KeyValuePair<int, string>(index, args.FieldValue.ToString()));
65+
}
66+
}
67+
/// <summary>
68+
/// Updates the merge fields result after mail merge by applying number and date formatting using IF fields.
69+
/// </summary>
70+
/// <param name="numberType">The boolean denotes current changes as Number format</param>
71+
public static void UpdateMergeFieldResult(bool numberType)
72+
{
73+
Dictionary<WParagraph, List<KeyValuePair<int, string>>> mergeFieldResults;
74+
if (numberType)
75+
mergeFieldResults = paratoModifyNumberFormat;
76+
else
77+
mergeFieldResults = paratoModifyDateFormat;
78+
// Iterate the outer dictionary entries
79+
foreach (KeyValuePair<WParagraph, List<KeyValuePair<int, string>>> dictionaryItem in mergeFieldResults)
80+
{
81+
// Get the merge field result paragraph
82+
WParagraph mergeFieldParagraph = dictionaryItem.Key;
83+
// The list of (index, fieldValues) pairs for this paragraph.
84+
List<KeyValuePair<int, string>> fieldList = dictionaryItem.Value;
85+
for (int i = 0; i <= fieldList.Count - 1; i++)
86+
{
87+
// Get the index and Field values ("Number" or "Date")
88+
int index = fieldList[i].Key;
89+
string fieldValue = fieldList[i].Value;
90+
// Get the existing merge field result text at the specified index.
91+
WTextRange mergeFieldText = (WTextRange)mergeFieldParagraph.ChildEntities[index];
92+
if (mergeFieldText != null)
93+
{
94+
// Create the temporary document and insert the IF field.
95+
WordDocument tempDocument = new WordDocument();
96+
WSection section = (WSection)tempDocument.AddSection();
97+
WParagraph ifFieldParagraph = (WParagraph)section.AddParagraph();
98+
WIfField field = (WIfField)ifFieldParagraph.AppendField("IfField", Syncfusion.DocIO.FieldType.FieldIf);
99+
// Check if the Number field value
100+
if (numberType)
101+
{
102+
// Format number: 1,234.56
103+
field.FieldCode = $"IF 1 = 1 \"{fieldValue}\" \" \" \\# \"#,##0.00";
104+
}
105+
// Update the Date field value
106+
else
107+
{
108+
// Format date: dd/MMM/yyyy
109+
field.FieldCode = $"IF 1 = 1 \"{fieldValue}\" \" \" \\@ \"dd/MMM/yyyy\" ";
110+
}
111+
// Update the field and unlink
112+
tempDocument.UpdateDocumentFields();
113+
field.Unlink();
114+
// Update the Merge field result
115+
WTextRange modifiedText = (WTextRange)ifFieldParagraph.ChildEntities[0];
116+
mergeFieldText.Text = modifiedText.Text;
117+
// close the temp document
118+
tempDocument.Close();
119+
}
120+
}
121+
}
122+
if (numberType)
123+
paratoModifyNumberFormat.Clear();
124+
else
125+
paratoModifyDateFormat.Clear();
126+
}
127+
private static DataTable GetInvoiceData()
128+
{
129+
DataTable table = new DataTable("Invoice");
130+
131+
table.Columns.Add("InvoiceNumber");
132+
table.Columns.Add("InvoiceDate");
133+
table.Columns.Add("CustomerName");
134+
table.Columns.Add("ItemDescription");
135+
table.Columns.Add("Amount");
136+
// First Invoice
137+
table.Rows.Add("INV001", "2024-05-01", "Andy Bernard", "Consulting Services", "3,000.578");
138+
// Second Invoice
139+
table.Rows.Add("INV002", "2024-05-05", "Stanley Hudson", "Software Development", "4,500.052");
140+
// Third Invoice
141+
table.Rows.Add("INV003", "2024-05-10", "Margaret Peacock", "UI Design Services", "2,000.600");
142+
143+
return table;
144+
}
145+
}
146+
}

0 commit comments

Comments
 (0)