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
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
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,
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