Call to MailMerge.ExecuteWithRegions is not working

Hello there,

I understand that either it’s my document format or my code that is causing the issue, but I am not able to figure out the reason why MailMerge.ExecuteWithRegions(ds) is not replacing fields in the document

It’s not throwing any error even.

Please find attached document and code file attached.
Note: In AppointmentConfirmation.dotx(attached), I am replacing fields marked with <> to Merge fields at run time.

Thank you!

Hi

Thanks for your inquiry. To perform MailMerge your document should contain mergefields, but your document contains only plain text placeholders. Please follow the link to learn how to insert mergefields to a document
https://docs.aspose.com/words/net/mail-merge-template/
Best regards,

My document does contain Merge Fields before I am calling ExecuteWithRegions().

I am replacing fields marked with <<field>> as MergeFields as mentioned in the following post:
https://forum.aspose.com/t/83679

Attached is the intermediate document that I am getting after replacing fields with Merge Fields, but somehow merge fields are not replaced with actual data found in IMailMergeDataSource.

Please guide.

Hi

Thank you for additional information. Why do you execute mail merge with regions? To execute mail merge with regions there should be special merge fields with names TableStart:MyRegion and TableEnt:MyRegion, where MyRegion name of the region.
In your case you should use MailMerge.Execute method instead of MailMerge.ExecuteWithRegions.
Best regards,

Well the document that I sent to you is having a mix of simple Merge fields like
{MERGEFIELD streetNumber} as well as
TableStart and TableEnd fields like { MERGEFIELD TableStart:PropertyVendors } and { MERGEFIELD TableEnd:PropertyVendors }

though I am not sure if I am defining the TableStart and TableEnd the right way.

Hi

Thank you for additional information. I found the problem in your template. Please try rewriting your mergefield TableStart:PropertyVendors and TableEnd:PropertyVendors without the white space between TableStart: and table name. I attached refactored template here.
Best regards,

I just tried MailMerge.ExecuteWithRegions() after making the change in my source document, but still it’s not replacing any of the merge fields

Please guide.

Hi

Thank you for additional information. I cannot reproduce the problem on my side. I use the following code for testing:

// Open document.
Document tempDoc = new Document(@"Test086\temp.docx");
// Create dummy datasource.
DataTable data = new DataTable("PropertyVendors");
data.Columns.Add("VTITLE");
data.Columns.Add("VFORENAME");
// Add few rows.
for (int i = 0; i <9; i++)
    data.Rows.Add(new object[]
    {
        "VTITLE" + i.ToString(), "VFORENAME" + i.ToString()
    });
// Execute mail merge.
tempDoc.MailMerge.ExecuteWithRegions(data);
// Save output document.
tempDoc.Save(@"Test086\out.docx");

As temp.docx please use the document from my previous post.
Hope this helps.
Best regards,

I think this works fine becuase there is no parent child relation in the data source for mailmerge.

If I change above data source to following:

System.Data.DataSet ds = new System.Data.DataSet();

System.Data.DataTable propertyData = new System.Data.DataTable("Properties");
propertyData.Columns.Add("PropertyId");
propertyData.Columns.Add("streetNumber");
propertyData.Columns.Add("streetName");

System.Data.DataColumn[] pkCols = new System.Data.DataColumn[1];
pkCols[0] = propertyData.Columns[0];
propertyData.PrimaryKey = pkCols;

// Add row to parent table
propertyData.Rows.Add(new object[]
{
    1,
    "Street1",
    "Amity Street"
});
propertyData.Rows.Add(new object[]
{
    2,
    "Street2",
    "Lodge Street"
});
ds.Tables.Add(propertyData);

// Create child table
System.Data.DataTable data = new System.Data.DataTable("PropertyVendors");
data.Columns.Add("PropertyId");
data.Columns.Add("VTITLE");
data.Columns.Add("VFORENAME");

// Add few rows.
for (int i = 0; i <4; i++)
{
    if (i <= 1)
    {
        data.Rows.Add(new object[]
        {
            1,
            "VTITLE" + i.ToString(),
            "VFORENAME" + i.ToString()
        });
    }
    else
    {
        data.Rows.Add(new object[]
        {
            2,
            "VTITLE" + i.ToString(),
            "VFORENAME" + i.ToString()
        });
    }
}
ds.Tables.Add(data);

System.Data.DataRelation relation = new System.Data.DataRelation("p2c", ds.Tables["Properties"].Columns["PropertyId"], ds.Tables["PropertyVendors"].Columns["PropertyId"]);

ds.Tables["PropertyVendors"].ParentRelations.Add(relation);

// Execute mail merge.
tempDoc.MailMerge.ExecuteWithRegions(ds);

In this case, it just replaces merge fields with the child collection data. I wonder why it’s not merging fields from parent table.
Also, the output document is not in the way it should be - essentially, I should get two page document with same content on each page except

  • first page to be replaced by data for PropertyId = 1 with it’ s associated child rows
  • second page with data for PropertyId = 2 with it’ s associated child Vendor rows

Attached is the document I am getting as a result of Mail Merge.

Also,

No mail merge output is seen if I call Execute with following parent child collection data.
(not even for child collection in this case)

// Mail Merge code
List propertyList = new List();

// Execute mail merge with List<>
propertyList.Add(new Property("3 Bed House with Lawn", "150000", "15", "Amity Road", "Reading", "RG1"));
propertyList.Add(new Property("4 Bed House with Lawn", "250000", "35", "Empire Road", "Wembley", "HA9"));
propertyList.Add(new Property("Office Complex", "350000", "25", "Lodge Road", "Hendon", "NW4"));

propertyList[0].PropertyVendors.Add(new Vendor("Mr", "VendorA"));
propertyList[0].PropertyVendors.Add(new Vendor("Mrs", "VendorAA"));
propertyList[1].PropertyVendors.Add(new Vendor("Mr", "VendorB"));
propertyList[1].PropertyVendors.Add(new Vendor("Mrs", "VendorBB"));
propertyList[2].PropertyVendors.Add(new Vendor("Mr", "VendorC"));
propertyList[2].PropertyVendors.Add(new Vendor("Mrs", "VendorCC"));

PropertyTestMailMergeDataSource mmDataSource = new PropertyTestMailMergeDataSource(propertyList);
tempDoc.MailMerge.ExecuteWithRegions(mmDataSource);
// Test Entities
public class Property
{
    private string propertyTitle;
    private string price;
    private string contactName;
    private string streetNumber;
    private string streetName;
    private PropertyAddress propertyAddress;
    private List propertyBuyers = new List();
    private List propertyVendors = new List();

    public List PropertyBuyers
    {
        get
        {
            return propertyBuyers;
        }
        set
        {
            propertyBuyers = value;
        }
    }

    public List PropertyVendors
    {
        get
        {
            return propertyVendors;
        }
        set
        {
            propertyVendors = value;
        }
    }

    public string PropertyAccommodation
    {
        get
        {
            return propertyTitle + " " + price + " at " + streetNumber + ", " + streetName;
        }
    }

    public string PropertyTitle
    {
        get
        {
            return propertyTitle;
        }
        set
        {
            propertyTitle = value;
        }
    }

    public string Price
    {
        get
        {
            return price;
        }
        set
        {
            price = value;
        }
    }

    public string ContactName
    {
        get
        {
            return contactName;
        }
        set
        {
            contactName = value;
        }
    }

    public string StreetNumber
    {
        get
        {
            return streetNumber;
        }
        set
        {
            streetNumber = value;
        }
    }

    public string StreetName
    {
        get
        {
            return streetName;
        }
        set
        {
            streetName = value;
        }
    }

public PropertyAddress PropertyAddress
{
    get
    {
        return propertyAddress;
    }
    set
    {
        propertyAddress = value;
    }
}

public Property(string propertyTitle, string price, string streetNumber, string streetName, string city, string postCode)
{
    this.propertyTitle = propertyTitle;
    this.price = price;
    this.streetNumber = streetNumber;
    this.streetName = streetName;
    this.propertyAddress = new PropertyAddress(city, postCode);
}
}

public class PropertyAddress
{
    private string city;
    private string postCode;

    public string City
    {
        get
        {
            return city;
        }
        set
        {
            city = value;
        }
    }

    public string PostCode
    {
        get
        {
            return postCode;
        }
        set
        {
            postCode = value;
        }
    }

    public PropertyAddress(string city, string postCode)
    {
        this.city = city;
        this.postCode = postCode;
    }
}

public class Vendor
{
    private string vtitle;
    private string vfName;

    public string VTitle
    {
        get
        {
            return vtitle;
        }
        set
        {
            vtitle = value;
        }
    }

    public string VFName
    {
        get
        {
            return vfName;
        }
        set
        {
            vfName = value;
        }
    }

    public Vendor(string title, string fName)
    {
        this.vtitle = title;
        this.vfName = fName;
    }
}

#region Mail Merge Data Source

public class PropertyTestMailMergeDataSource: IMailMergeDataSource
{
    private readonly List propertyList;
    private int recordIndex;

    private bool IsEof
    {
        get
        {
            return (recordIndex>= propertyList.Count);
        }
    }

    public PropertyTestMailMergeDataSource(List properties)
    {
        this.propertyList = properties;
        recordIndex = -1;
    }

    #region IMailMergeDataSource Members

    bool IMailMergeDataSource.GetValue(string fieldName, out object fieldValue)
    {
        switch (fieldName.ToUpper())
        {
            // Todo: Replace all cases with actual fieldName
            case "PROPERTYTITLE":
                fieldValue = propertyList[recordIndex].PropertyTitle;
                return true;
            case "PRICE":
                fieldValue = propertyList[recordIndex].Price;
                return true;
            case "CONTACTNAME":
                fieldValue = propertyList[recordIndex].ContactName;
                return true;
            case "STREETNUMBER":
                fieldValue = propertyList[recordIndex].PropertyAddress.City;
                return true;
            case "STREETNAME":
                fieldValue = propertyList[recordIndex].PropertyTitle;
                return true;
            case "CITY":
            case "TOWN":
                fieldValue = propertyList[recordIndex].PropertyAddress.City;
                return true;
            case "POSTCODE":
                fieldValue = propertyList[recordIndex].PropertyAddress.PostCode;
                return true;
            case "PROPERTYACCOMMODATION":
                fieldValue = propertyList[recordIndex].PropertyAccommodation;
                return true;
            case "PROPERTYFULLDETAILS":
                fieldValue = propertyList[recordIndex].PropertyAddress;
                return true;
            case "IMAGEPPHOTOMAIN":
            case "IMAGEPPHIPEER":
                // todo: Check which Image Index is the correct one
                fieldValue = null;
                return true;
            default:
                fieldValue = null;
                return false;
        }
    }

    bool IMailMergeDataSource.MoveNext()
    {
        if (!IsEof)
        {
            recordIndex++;
        }
        return (!IsEof);
    }

    string IMailMergeDataSource.TableName
    {
        get
        {
            return "PropertyList";
        }
    }

    public IMailMergeDataSource GetChildDataSource(string tableName)
    {
        switch (tableName)
        {
            case "PropertyBuyers":
                return new BuyersDataSource(propertyList[recordIndex].PropertyBuyers);
            case "PropertyVendors":
                return new VendorsDataSource(propertyList[recordIndex].PropertyVendors);
            default:
                return null;
        }
    }

    #endregion
}

public class VendorsDataSource: IMailMergeDataSource
{
    private List mVendors;
    private int mRecordIndex;

    public string TableName
    {
        get
        {
            return "PropertyVendors";
        }
    }

    private bool IsEof
    {
        get
        {
            return (mRecordIndex>= mVendors.Count);
        }
    }

    public VendorsDataSource(List vendors)
    {
        mVendors = vendors;
        mRecordIndex = -1;
    }

    public bool MoveNext()
    {
        if (!IsEof)
        {
            mRecordIndex++;
        }
        return (!IsEof);
    }

    public bool GetValue(string fieldName, out object fieldValue)
    {
        switch (fieldName.ToUpper())
        {
            // Todo: Replace all cases with actual fieldName
            case "VTITLE":
                fieldValue = mVendors[mRecordIndex].VTitle;
                return true;
            case "VFORENAME":
                fieldValue = mVendors[mRecordIndex].VFName;
                return true;

            default:
                fieldValue = null;
                return false;
        }
    }

    public IMailMergeDataSource GetChildDataSource(string tableName)
    {
        return null;
    }

}
#endregion

Hi

Thank you for additional information. In your dataset, there are two tables, but your template contains only one region. So, when you execute mail merge with regions only one region is filled with data.
Here you can find template and code examples, which show how to fill document with data with parent-child relationships.
https://forum.aspose.com/t/81826
Hope this helps.
Best regards,

Thanks for your help.

I was creating my data source in line with the way mentioned in above thread only.

Now, I understand what I was missing in my source document was: { MERGEFIELD TableStart:Properties } at begining of source document and {MERGEFIELD TableEnd:Properties} at end of source document.

Thank you!