Mail Merge - ExecuteWithRegions

Hello there,

I am having a doubt regarding what will be the best way to implement Mail Merge - ExecuteWithRegions in my scenerio -

I have a Main collection object called Properties with two child collections: Applicants, Vendors.

In first scenerio:

  1. Source template is having Merge Fields for Property + Vendor and region defined one child collection say Applicants as
    {MERGEFIELD TableStart:Applicants}

    {MERGEFIELD TableStart:Applicants}
    and for second child collection Vendors, mail merge needs to be repeated for each vendor under a property object as:
<Property[0]>
    <Vendors[0.1]>
        mail merge executed with region Property[0].Applicants
        <Vendors[0.2]> mail merge executed with region Property[0].Applicants
</Property[0]>
<Property[1]>
    <Vendors[1.1]>
        mail merge executed with region Property[1].Applicants
        <Vendors[1.2]>
            mail merge executed with region Property[1].Applicants
            <Vendors[1.3]> mail merge executed with region Property[1].Applicants
</Property[1]>

So, In above scenerio, my output report will have 5 pages considering the source template is of one page.

Similary in second scenerio, region is defined for Vendors and mail Merge is repeated for Property Applicants:

  1. In second type of template, I have Merge Fields for Property+Applicant and have region defined for one child collection say Vendors as

{MERGEFIELD TableStart:Vendors}

{MERGEFIELD TableStart:Vendors}

and for second child collection Applicants mail merge needs to be repeated for each Applicant under a property object as:

<Property[0]>
    <Applicant[0.1]>
        mail merge executed with region Property[0].Vendors
        <Applicant[0.2]>
            mail merge executed with region Property[0].Vendors
            <Applicant[0.3]> mail merge executed with region Property[0].Vendors
</Property[0]>
<Property[1]>
    <Applicant[1.1]>
        mail merge executed with region Property[1].Vendors<br>
        <Applicant[1.2]>
            mail merge executed with region Property[1].Vendors<br>
            <Applicant[1.3]>  mail merge executed with region Property[1].Vendors<br>
</Property[1]>

So, In above scenerio, my output report will have 6 pages considering the source template is of one page.

I am thinking to implement it by creating run time collection as mentioned in this thread:

https://forum.aspose.com/t/81934

Do you think of any other way to achieve above?
P.S. attached are the sample docs for above scenerios

Just to share with you,

I was also thinking to implement IMailMergeDataSource for my collection object Properties
as mentioned in following thread:
https://forum.aspose.com/t/mailmerge-executewithregions-error-value-cannot-be-null-parameter-name-startparent/81384/4

But I am not able to think of a way to run through my child collection(one which is not a part of TableStart-TableEnd region).

So, if I take first object in Properties collection, say Property[0],

  • MoveNext() should be called for Property[0], set recordIndex ++
  • MoveNext() should be called for Property[0].Vendors[0]
  • MoveNext() should be called for Property[0].Vendors[Property[0].Vendors.count-1]
  • MoveNext() should be called for Property[1], set recordIndex ++

and so on.

I tried to achieve above, but it seems whenever I return true from IMailMergeDataSource.MoveNext(), it just moves the counter of parent collection i.e. Properties.

I wonder if there is any way I can iterate over child collection while being in the loop for parent collection.

Thank you!

Hi

Thanks for your inquiry. Maybe you should consider an option of creating wrapper object, which will contain all properties, from parent objects (Properties) and from child objects (Vendors).

As I know, you use .NET 3.0 or better, so you can use anonymous type as a container of all needed properties. In this case, you do not need to create one more class.

Best regards.

Thanks for your reply.

Can you please help me with a quick code snippet to show - you can use anonymous type as a container of all needed properties
and then use that as a mail merge data source for my scenerio.

Thanks a lot!

Hi

Thanks for your request. Sure, here is a simple example, how you can use this approach.

Let’s suppose you have Property class, which have two properties PropertyId and PropertyName, for example. Each property can contain any number of vendors. Let’s suppose Vendor class has properties VendorFirstName and VendorLastName. Code of these classes is provided below.

In your case you needed properties of Property and Vendor classes during mail merge. In this case, you can easily achieve this using LINQ and anonymous types (highlighted is anonymous type):

// Get dummy data.
Property[] properties = GetDummyData();
// Create LINQ query to get all Property and all Vendor properties into one Anonymous object.
var q = from p in properties
        from v in p.Vendors
        select new { p.PropertyId, p.PropertyName, v.VendorFirstName, v.VendorLastName };
// Create MailMergeObjectDatasource. I used IMailMergeDatasource provided by Fredrik in the following thread:
// https://forum.aspose.com/t/81384
MailMergeObjectDatasource ds = new MailMergeObjectDatasource(q, "Property");
// Open template and execute mail merge.
Document doc = new Document(@"C:\Temp\in.doc");
doc.MailMerge.ExecuteWithRegions(ds);
doc.Save(@"C:\Temp\out.doc");

/// 
/// Method generates dummy data, which will be used for testing.
/// 
private static Property[] GetDummyData()
{
    Property[] properties = new Property[2];
    for (int i = 0; i < properties.Length; i++)
    {
        properties[i] = new Property(i, string.Format("Property_{0}", i));
        // Add few vendors.
        properties[i].Vendors.Add(new Vendor("Alexey", "Noskov"));
        properties[i].Vendors.Add(new Vendor("Den", "Martin"));
        properties[i].Vendors.Add(new Vendor("Sam", "Smith"));
    }
    return properties;
}

And here are code of Property and Vendor classes:

class Property
{
    public Property(int id, string name)
    {
        mPropertyId = id;
        mPropertyName = name;
    }

    public int PropertyId
    {
        get { return mPropertyId; }
    }

    public string PropertyName
    {
        get { return mPropertyName; }
    }

    public List<Vendor> Vendors
    {
        get { return mVendors; }
    }

    private readonly int mPropertyId;
    private readonly string mPropertyName;
    private readonly List<Vendor> mVendors = new List<Vendor>();
}

class Vendor
{
    public Vendor(string firstName, string lastName)
    {
        mVendorFirstName = firstName;
        mVendorLastName = lastName;
    }

    public string VendorFirstName
    {
        get { return mVendorFirstName; }
    }

    public string VendorLastName
    {
        get { return mVendorLastName; }
    }

    private readonly string mVendorFirstName;
    private readonly string mVendorLastName;
}

Hope this helps.

Best regards.