Dynamically insert code to an existing word table

Hi,
I got help from AndreyN the other day with opening an existing word template (.dot) and then saving this. A couple of my template have word tables in them, and I have figured out a way of removing a table from the document if it is not required in certain scenario’s.
My current problem is that the way I had set up my application I was populating one table with data on a number of beneficiaries. The number of beneficiaries could vary and so I created a sub that would accept all of the data in one string delimited by ‘#’, but there would only ever be three columns per row (see code below). On my word template there is a form field in row 2, cell 1 of the table, this is the case for all the tables that call my sub.
Now that I’ve moved to Aspose I am unsure how I can implement this code, or even if this is the correct approach. I’ve read some other forum posts and I think the solution would be to do something similar in terms of inserting the entries for one row, but would then potentially have to clone the row?
I don’t really know, could you shed some light on this for me please, thank you.
Regards,
Dan

Private Sub MoveDataToColumns(ByVal objTable As Word.Table)
    'This sub splits the data in the text form field (which is always in row 2, cell 1)
    'into the relevant cloumns. The string is delimited by "#"s for new columns
    'declarations
    Dim allString As String
    Dim inputString As String
    Dim outputString As String
    Dim stringLen As Integer
    Dim startPos As Integer
    Dim cellIdx As Integer
    Dim rowIdx As Integer

    cellIdx = 1
    rowIdx = 2
    startPos = 1
    allString = objTable.Rows(2).Cells(1).Range.Text
    inputString = allString
    Do While (startPos < (Len(allString) - 1))
        stringLen = (InStr(startPos, inputString, "#") - startPos)
        outputString = Mid(inputString, startPos, stringLen)
        objTable.Rows(rowIdx).Cells(cellIdx).Range.Text = outputString 'move the startpos past the "#"
        startPos = stringLen + startPos + 1
        stringLen = 0
        outputString = String.Empty

        'increment the indexes (only 3 cells in a row)
        If cellIdx = 3 Then
            If startPos < (Len(allString) - 1) Then
                objTable.Rows.Add()

                rowIdx = rowIdx + 1
                cellIdx = 1
            End If
        Else
            cellIdx = cellIdx + 1
        End If
    Loop
End Sub

Hi

Thanks for your request. I think in your case, you should use MailMerge with regions to fill your document with data. Please see the following link to learn more:
https://docs.aspose.com/words/java/types-of-mail-merge-operations/
Please let me know if such approach is acceptable for you.
Best regards,

Hi,
I’ve tried this suggestion of using mail merge with regions this morning and run into a couple of problems. I inserted three merge fields within table start and end tags within my word table like this:

Beneficiary Date of Birth Share
«TableStart:cBenTab»«FullName» «DateOfBirth» «Split»«TableEnd:cBenTab»

I then created my classes for the individual beneficiary (FullName, DateOfBirth an Split), the array list of then (BenList) and the source (BenDataSource), and after adding new items to the list I executed the following code:

Dim contBenDataSource As New BenDataSource(contBens)
polDoc.MailMerge.ExecuteWithRegions(contBenDataSource)

This didn’t put either of my two list entries into the document. I even tried placing the merge fields outside of my word table but this also did not work.
This is the first issue, the second is that my document template has three word tables in it so I would need to insert different data into all three, but if I try and add a second parameter with the table name (which I think I probably will to, as I will have three separate tables to populate) my first parameter must be of type “System.Data.IDataReader” and not my user defined data source!
Please can you help, thank you

Hello

Thanks for your request. I cannot reproduce the problem on my side using the latest version of Aspose.Words. Please try using the following code:

// Create a list of objects.
List <Customer> customers = new List <Customer> ();
// Add few items to the list.
customers.Add(new Customer("Thomas Hardy", "10/10/86", "Split1"));
customers.Add(new Customer("Paolo Accorti", "11/11/84", "Split2"));
// etc...
// Open document
Document doc = new Document("in.docx");
// To be able to mail merge from your own data source, it must be wrapped
// into an object that implements the IMailMergeDataSource interface.
MailMergeDataSource customersDataSource = new MailMergeDataSource(customers, "cBenTab");
// Now you can pass your data source into Aspose.Words.
doc.MailMerge.ExecuteWithRegions(customersDataSource);
// Save output document
doc.Save("out.doc");
////
/// An example of a "data entity" class in your application.
///
public class Customer
{
    public Customer(string aFullName, string aDateOfBirth, string aSplit)
    {
        mFullName = aFullName;
        mDateOfBirth = aDateOfBirth;
        mSplit = aSplit;
    }
    public string FullName
    {
        get
        {
            return mFullName;
        }
        set
        {
            mFullName = value;
        }
    }
    public string DateOfBirth
    {
        get
        {
            return mDateOfBirth;
        }
        set
        {
            mDateOfBirth = value;
        }
    }
    public string Split
    {
        get
        {
            return mSplit;
        }
        set
        {
            mSplit = value;
        }
    }
    private string mFullName;
    private string mDateOfBirth;
    private string mSplit;
}
///
/// A custom mail merge data source that you implement to allow Aspose.Words
/// to mail merge data from LINQ query results into Microsoft Word documents.
///
public class MailMergeDataSource: IMailMergeDataSource
{
    ///
    /// Creates new instance of a custom mail merge data source
    ///
    /// Data returned from a LINQ query
    public MailMergeDataSource(IEnumerable data)
    {
        mEnumerator = data.GetEnumerator();
    }
    ///
    /// Creates new instance of a custom mail merge data source
    ///
    /// Data returned from a LINQ query
    /// Name of the data source is only used when you perform mail merge with regions.
    /// If you would like to use simple mail merge then use constructor with one parameter.
    public MailMergeDataSource(IEnumerable data, string tableName)
    {
        mEnumerator = data.GetEnumerator();
        // Name of the data source is needed when you perform mail merge with regions
        mTableName = tableName;
    }
    ///
    /// Aspose.Words call this to get a value for every data field.
    ///
    public bool GetValue(string fieldName, out object fieldValue)
    {
        // Get type of current record
        Type curentRecordType = mCurrentObject.GetType();
        // Use reflection to get property by name and its value
        PropertyInfo property = curentRecordType.GetProperty(fieldName);
        if (property != null)
        {
            fieldValue = property.GetValue(mCurrentObject, null).ToString();
            return true;
        }
        else
        {
            // A field with this name was not found,
            // return false to the Aspose.Words mail merge engine.
            fieldValue = null;
            return false;
        }
    }
    ///
    /// Moves to the next record in the collection.
    ///
    public bool MoveNext()
    {
        // Move enumerator to next record
        bool hasNexeRecord = mEnumerator.MoveNext();
        if (hasNexeRecord)
        {
            mCurrentObject = mEnumerator.Current;
        }
        return hasNexeRecord;
    }
    ///
    /// The name of the data source. Used by Aspose.Words only when executing mail merge with repeatable regions.
    ///
    public string TableName
    {
        get
        {
            return mTableName;
        }
    }
    private IEnumerator mEnumerator;
    private object mCurrentObject;
    private string mTableName = string.Empty;
    #region IMailMergeDataSource Members
    public IMailMergeDataSource GetChildDataSource(string tableName)
    {
        return null;
    }
    #endregion
}

The template and the output documents are attached.
Regarding the second question, you can use this IMailMergeDataSource to create any other Data Sources for your application.
Best regards,

Hi,
Thanks for the information and code. I tried to use the code but had to convert to VB on-line and am getting an error message in the “GetValue” function, it says “Type ‘PropertyInfo’ is not defined”.

''' 
''' Aspose.Words call this to get a value for every data field.
''' 
Public Function GetValue(ByVal fieldName As String, ByRef fieldValue As Object) As Boolean Implements Aspose.Words.Reporting.IMailMergeDataSource.GetValue
    ' Get type of current record
    Dim curentRecordType As Type = mCurrentObject.[GetType]()

    ' Use reflection to get property by name and its value
    Dim [property] As PropertyInfo = curentRecordType.GetProperty(fieldName)

    If [property] IsNot Nothing Then
        fieldValue = [property].GetValue(mCurrentObject, Nothing).ToString()

        Return True
        'Else
        ' A field with this name was not found, 
        ' return false to the Aspose.Words mail merge engine.
        fieldValue = Nothing
        Return False
    End If
End Function

I also experienced other minor problems with the functions and had to put the ‘implements’ clause on all the functions within the class (as you can see above).
In an effort to get around this I tried to just alter the code I had set up previously (which I copied from this website) to introduce the #region tags on my existing ‘GetChildDataSource’ function in the existing data source. This didn’t work for me either.
As far as I can see, my template (which is a .dot) is set up correctly, please see attached document, I have only put in the merge fields to the first table for now until I get that one working. Could you tell me what I’m missing in this code, as I’m sure if I could get that to build it would probably work.
Regards,
Dan

Hello

Thanks for your request. Please just add this line of code:
Imports System.Reflection
Also your template is correct.
Best regards,

Hello,
I just wanted to thank you for your help earlier, this worked and now my application is displaying information on all three word tables in my template as required.
Thanks again, it’s much appreciated.
Regards,
Dan

Hello Dan,

It is perfect, that you already resolved the problem.
If you need more assistance, I will be glad to help you.
Best regards,