Custom Data Source

  1. Add SautinSoft.Document from Nuget.
  2. Load a template document.
  3. Create a custom data source class that implements the IMailMergeDataSource
  4. Fill the custom data source object.
  5. Execute the mail merge process.
  6. Save the document in DOCX format.

This example shows how to Generate reports using a custom data source (collection of custom classes 'Actor' and 'Order').

Create a template in MS Word with Merge Fields or use a ready file «OrdersTemplate.docx».

After execution of mail merge method we'll get the «Orders.docx» as result.

Complete code

using System.Collections.Generic;
using SautinSoft.Document;
using SautinSoft.Document.MailMerging;

namespace Sample
{
    class Sample
    {
        static void Main(string[] args)
        {
            // Get your free 100-day key here:   
            // https://sautinsoft.com/start-for-free/

            CustomDataSource();
        }
        /// <summary>
        /// Generate reports using a custom data source (collection of custom classes Actor and Order).
        /// </summary>
        /// <remarks>
        /// Details: https://sautinsoft.com/products/document/help/net/developer-guide/mail-merge-custom-data-source-net-csharp-vb.php
        /// </remarks>
        static void CustomDataSource()
        {
            // Populate some data that we will use in the mail merge.
            List<Actor> actors = new List<Actor>();
            actors.Add(new Actor("Arnold Schwarzenegger", "12989 Chalon Road, Los Angeles, CA 90049"));
            actors.Add(new Actor("Sylvester Stallone", "30 Beverly Park Terrace, Beverly Hills, CA 90210"));

            // Populate some data for nesting in the mail merge.
            actors[0].Orders.Add(new Order("Bowflex SelectTech 1090 Adjustable Dumbbell", 2));
            actors[0].Orders.Add(new Order("Gold's Gym Kettlebell Kit, 5-15 Lbs.", 1));
            actors[1].Orders.Add(new Order("Weider Cast Iron Olympic Hammertone Weight Set, 300 Lb.", 1));

            // Load the template document.
            DocumentCore dc = DocumentCore.Load(@"..\..\..\OrdersTemplate.docx");

            // To be able to mail merge from your own data source, it must be wrapped into an object that implements the IMailMergeDataSource interface.
            CustomMailMergeDataSource customDataSource = new CustomMailMergeDataSource(actors);

            // Execute the mail merge.
            dc.MailMerge.Execute(customDataSource);

            string resultPath = "Orders.docx";

            // Save the output to file.
            dc.Save(resultPath);

            // Open the result for demonstration purposes.
            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(resultPath) { UseShellExecute = true });
        }

        /// <summary>
        /// An example of a class that contain actor's data.
        /// </summary>
        public class Actor
        {
            private string _fullName;
            private string _address;
            private List<Order> _orders;

            public string FullName
            {
                get { return _fullName; }
                set { _fullName = value; }
            }

            public string Address
            {
                get { return _address; }
                set { _address = value; }
            }

            public List<Order> Orders
            {
                get { return _orders; }
                set { _orders = value; }
            }

            public Actor(string fullName, string address)
            {
                _fullName = fullName;
                _address = address;
                _orders = new List<Order>();
            }

        }         

        /// <summary>
        /// An example of a class that contain order's data.
        /// </summary>
        public class Order
        {
            private string _name;
            private int _quantity;

            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }

            public int Quantity
            {
                get { return _quantity; }
                set { _quantity = value; }
            }

            public Order(string name, int quantity)
            {
                _name = name;
                _quantity = quantity;
            }
        }                     

        /// <summary>
        /// A custom mail merge data source that allows SautinSoft.Document to retrieve data from Actor objects.
        /// </summary>
        public class CustomMailMergeDataSource : IMailMergeDataSource
        {
            private readonly List<Actor> _actors;
            private int _recordIndex;

            /// <summary>
            /// The name of the data source. 
            /// </summary>
            public string Name
            {
                get { return "Actor"; }
            }

            /// <summary>
            /// SautinSoft.Document calls this method to get a value for every data field.
            /// </summary>
            public bool TryGetValue(string valueName, out object value)
            {
                switch (valueName)
                {
                    case "FullName":
                        value = _actors[_recordIndex].FullName;
                        return true;
                    case "Address":
                        value = _actors[_recordIndex].Address;
                        return true;
                    default:
                        // A field with this name was not found
                        value = null;
                        return false;
                }
            }

            /// <summary>
            /// A standard implementation for moving to a next record in a collection.
            /// </summary>
            public bool MoveNext()
            {
                return (++_recordIndex < _actors.Count);
            }

            public IMailMergeDataSource GetChildDataSource(string sourceName)
            {
                switch (sourceName)
                {
                    case "Order":
                        return new OrderMailMergeDataSource(_actors[_recordIndex].Orders);
                    default:
                        return null;
                }
            }

            public CustomMailMergeDataSource(List<Actor> actors)
            {
                _actors = actors;
                // When the data source is initialized, it must be positioned before the first record.
                _recordIndex = -1;
            }
        }

        public class OrderMailMergeDataSource : IMailMergeDataSource
        {
            private readonly List<Order> _orders;
            private int _recordIndex;

            /// <summary>
            /// The name of the data source. 
            /// </summary>
            public string Name
            {
                get { return "Order"; }
            }

            /// <summary>
            /// SautinSoft.Document calls this method to get a value for every data field.
            /// </summary>
            public bool TryGetValue(string valueName, out object value)
            {
                switch (valueName)
                {
                    case "Name":
                        value = _orders[_recordIndex].Name;
                        return true;
                    case "Quantity":
                        value = _orders[_recordIndex].Quantity;
                        return true;
                    default:
                        // A field with this name was not found
                        value = null;
                        return false;
                }
            }

            /// <summary>
            /// A standard implementation for moving to a next record in a collection.
            /// </summary>
            public bool MoveNext()
            {
                return (++_recordIndex < _orders.Count);
            }

            // Return null because Order haven't any child elements.
            public IMailMergeDataSource GetChildDataSource(string tableName)
            {
                return null;
            }

            public OrderMailMergeDataSource(List<Order> orders)
            {
                _orders = orders;
                // When the data source is initialized, it must be positioned before the first record.
                _recordIndex = -1;
            }
        }
    }
}

Download

Imports System.Collections.Generic
Imports SautinSoft.Document
Imports SautinSoft.Document.MailMerging

Namespace Sample
    Friend Class Sample
        Shared Sub Main(ByVal args() As String)
            CustomDataSource()
        End Sub
        ''' Get your free 100-day key here:   
        ''' https://sautinsoft.com/start-for-free/
        ''' <summary>
        ''' Generate reports using a custom data source (collection of custom classes Actor and Order).
        ''' </summary>
        ''' <remarks>
        ''' Details: https://sautinsoft.com/products/document/help/net/developer-guide/mail-merge-custom-data-source-net-csharp-vb.php
        ''' </remarks>
        Private Shared Sub CustomDataSource()
            ' Populate some data that we will use in the mail merge.
            Dim actors As New List(Of Actor)()
            actors.Add(New Actor("Arnold Schwarzenegger", "12989 Chalon Road, Los Angeles, CA 90049"))
            actors.Add(New Actor("Sylvester Stallone", "30 Beverly Park Terrace, Beverly Hills, CA 90210"))

            ' Populate some data for nesting in the mail merge.
            actors(0).Orders.Add(New Order("Bowflex SelectTech 1090 Adjustable Dumbbell", 2))
            actors(0).Orders.Add(New Order("Gold's Gym Kettlebell Kit, 5-15 Lbs.", 1))
            actors(1).Orders.Add(New Order("Weider Cast Iron Olympic Hammertone Weight Set, 300 Lb.", 1))

            ' Load the template document.
            Dim dc As DocumentCore = DocumentCore.Load("..\..\..\OrdersTemplate.docx")

            ' To be able to mail merge from your own data source, it must be wrapped into an object that implements the IMailMergeDataSource interface.
            'INSTANT VB NOTE: The variable customDataSource was renamed since Visual Basic does not handle local variables named the same as class members well:
            Dim customDataSource_Renamed As New CustomMailMergeDataSource(actors)

            ' Execute the mail merge.
            dc.MailMerge.Execute(customDataSource_Renamed)

            Dim resultPath As String = "Orders.docx"

            ' Save the output to file.
            dc.Save(resultPath)

            ' Open the result for demonstration purposes.
            System.Diagnostics.Process.Start(New System.Diagnostics.ProcessStartInfo(resultPath) With {.UseShellExecute = True})
        End Sub

        ''' <summary>
        ''' An example of a class that contain actor's data.
        ''' </summary>
        Public Class Actor
            Private _fullName As String
            Private _address As String
            Private _orders As List(Of Order)

            Public Property FullName() As String
                Get
                    Return _fullName
                End Get
                Set(ByVal value As String)
                    _fullName = value
                End Set
            End Property

            Public Property Address() As String
                Get
                    Return _address
                End Get
                Set(ByVal value As String)
                    _address = value
                End Set
            End Property

            Public Property Orders() As List(Of Order)
                Get
                    Return _orders
                End Get
                Set(ByVal value As List(Of Order))
                    _orders = value
                End Set
            End Property

            Public Sub New(ByVal fullName As String, ByVal address As String)
                _fullName = fullName
                _address = address
                _orders = New List(Of Order)()
            End Sub

        End Class

        ''' <summary>
        ''' An example of a class that contain order's data.
        ''' </summary>
        Public Class Order
            Private _name As String
            Private _quantity As Integer

            Public Property Name() As String
                Get
                    Return _name
                End Get
                Set(ByVal value As String)
                    _name = value
                End Set
            End Property

            Public Property Quantity() As Integer
                Get
                    Return _quantity
                End Get
                Set(ByVal value As Integer)
                    _quantity = value
                End Set
            End Property

            Public Sub New(ByVal name As String, ByVal quantity As Integer)
                _name = name
                _quantity = quantity
            End Sub
        End Class

        ''' <summary>
        ''' A custom mail merge data source that allows SautinSoft.Document to retrieve data from Actor objects.
        ''' </summary>
        Public Class CustomMailMergeDataSource
            Implements IMailMergeDataSource

            Private ReadOnly _actors As List(Of Actor)
            Private _recordIndex As Integer

            ''' <summary>
            ''' The name of the data source. 
            ''' </summary>
            Public ReadOnly Property Name() As String Implements IMailMergeDataSource.Name
                Get
                    Return "Actor"
                End Get
            End Property

            ''' <summary>
            ''' SautinSoft.Document calls this method to get a value for every data field.
            ''' </summary>
            Public Function TryGetValue(ByVal valueName As String, <System.Runtime.InteropServices.Out()> ByRef value As Object) As Boolean Implements IMailMergeDataSource.TryGetValue
                Select Case valueName
                    Case "FullName"
                        value = _actors(_recordIndex).FullName
                        Return True
                    Case "Address"
                        value = _actors(_recordIndex).Address
                        Return True
                    Case Else
                        ' A field with this name was not found
                        value = Nothing
                        Return False
                End Select
            End Function

            ''' <summary>
            ''' A standard implementation for moving to a next record in a collection.
            ''' </summary>
            Public Function MoveNext() As Boolean Implements IMailMergeDataSource.MoveNext
                _recordIndex += 1
                'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
                'ORIGINAL LINE: return (++_recordIndex < _actors.Count);
                Return (_recordIndex < _actors.Count)
            End Function

            Public Function GetChildDataSource(ByVal sourceName As String) As IMailMergeDataSource Implements IMailMergeDataSource.GetChildDataSource
                Select Case sourceName
                    Case "Order"
                        Return New OrderMailMergeDataSource(_actors(_recordIndex).Orders)
                    Case Else
                        Return Nothing
                End Select
            End Function

            Public Sub New(ByVal actors As List(Of Actor))
                _actors = actors
                ' When the data source is initialized, it must be positioned before the first record.
                _recordIndex = -1
            End Sub
        End Class

        Public Class OrderMailMergeDataSource
            Implements IMailMergeDataSource

            Private ReadOnly _orders As List(Of Order)
            Private _recordIndex As Integer

            ''' <summary>
            ''' The name of the data source. 
            ''' </summary>
            Public ReadOnly Property Name() As String Implements IMailMergeDataSource.Name
                Get
                    Return "Order"
                End Get
            End Property

            ''' <summary>
            ''' SautinSoft.Document calls this method to get a value for every data field.
            ''' </summary>
            Public Function TryGetValue(ByVal valueName As String, <System.Runtime.InteropServices.Out()> ByRef value As Object) As Boolean Implements IMailMergeDataSource.TryGetValue
                Select Case valueName
                    Case "Name"
                        value = _orders(_recordIndex).Name
                        Return True
                    Case "Quantity"
                        value = _orders(_recordIndex).Quantity
                        Return True
                    Case Else
                        ' A field with this name was not found
                        value = Nothing
                        Return False
                End Select
            End Function

            ''' <summary>
            ''' A standard implementation for moving to a next record in a collection.
            ''' </summary>
            Public Function MoveNext() As Boolean Implements IMailMergeDataSource.MoveNext
                _recordIndex += 1
                'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
                'ORIGINAL LINE: return (++_recordIndex < _orders.Count);
                Return (_recordIndex < _orders.Count)
            End Function

            ' Return null because Order haven't any child elements.
            Public Function GetChildDataSource(ByVal tableName As String) As IMailMergeDataSource Implements IMailMergeDataSource.GetChildDataSource
                Return Nothing
            End Function

            Public Sub New(ByVal orders As List(Of Order))
                _orders = orders
                ' When the data source is initialized, it must be positioned before the first record.
                _recordIndex = -1
            End Sub
        End Class
    End Class
End Namespace

Download


If you need a new code example or have a question: email us at support@sautinsoft.com or ask at Online Chat (right-bottom corner of this page) or use the Form below:



Questions and suggestions from you are always welcome!

We are developing .Net components since 2002. We know PDF, DOCX, RTF, HTML, XLSX and Images formats. If you need any assistance with creating, modifying or converting documents in various formats, we can help you. We will write any code example for you absolutely free.