# MailMerge: Using Smart Business Objects to Calculate Batch Totals and Subtotals

> The TX Text Control MailMerge class accepts IEnumerable business objects as data sources for nested merge blocks. Using ObservableCollection and INotifyPropertyChanged, computed fields like subtotals and totals update automatically. MergeObject then produces the final merged output.

- **Author:** Bjoern Meyer
- **Published:** 2017-04-21
- **Modified:** 2026-03-05
- **Description:** The TX Text Control MailMerge class accepts IEnumerable business objects as data sources for nested merge blocks. Using ObservableCollection and INotifyPropertyChanged, computed fields like subtotals and totals update automatically. MergeObject then produces the final merged output.
- **3 min read** (583 words)
- **Tags:**
  - Business Objects
  - Merge Blocks
  - Reporting
- **Web URL:** https://www.textcontrol.com/blog/2017/04/21/mailmerge-using-smart-business-objects-to-calculate-batch-totals-and-subtotals/
- **LLMs URL:** https://www.textcontrol.com/blog/2017/04/21/mailmerge-using-smart-business-objects-to-calculate-batch-totals-and-subtotals/llms.txt
- **LLMs-Full URL:** https://www.textcontrol.com/blog/2017/04/21/mailmerge-using-smart-business-objects-to-calculate-batch-totals-and-subtotals/llms-full.txt

---

Text Control's [MailMerge](https://docs.textcontrol.com/textcontrol/windows-forms/ref.txtextcontrol.documentserver.mailmerge.class.htm) class supports *IEnumerable* objects, *DataSets*, *DataTables* and *Json* as data sources for mail merge processes. A smart advantage of business objects is that a specific logic can be implemented into these classes. Consider the following business object:

![MailMerge: Using smart business objects to calculate batch totals and subtotals](https://s1-www.textcontrol.com/assets/dist/blog/2017/04/21/a/assets/mergedata.webp "MailMerge: Using smart business objects to calculate batch totals and subtotals")

*Order* is the main object or *Master Table* for the mail merge process. It consists of *Customer* information, a *Total* and a collection of *ProductBlocks*. The *ProductBlock* class contains the sum of prices of all contained *Products*.

```
public class ProductBlock
{
    private List<Product> _products;

    public List<Product> Products
    {
        get { return _products; }
        set
        {
            _products = value;

            foreach (Product product in _products)
            {
                this.BlockSum += product.Price;
            }
        }
    }

    public int BlockSum { get; internal set; }
}
```

```
public class Product
{
    public string Name { get; set; }
    public int Price { get; set; }
}
```

If a new list of products is added to the *ProductBlock* object, the *BlockSum* field is calculated automatically. Therefore, the total sum of a *ProductBlock* object is always accurate and must not be set manually.

The main object *Order* implements the *INotifyPropertyChanged* interface which is used to update the *ObservableCollection* of *ProductBlocks*. Each time, a new *ProductBlock* is added, the *Total* is calculated.

```
public class Order : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    // *** M E M B E R S ***
    private ObservableCollection<ProductBlock> _productBlocks;

    // *** P R O P E R T I E S ***
    public int Total { get; set; }
    public Customer Customer { get; set; }

    public ObservableCollection<ProductBlock> ProductBlocks
    {
        get { return _productBlocks; }
        set { _productBlocks = value; }
    }

    // *** C O N S T R U C T O R ***
    public Order()
    {
        _productBlocks = new ObservableCollection<ProductBlock>();
        _productBlocks.CollectionChanged += _productBlocks_CollectionChanged;
    }

    // Update the Total, if new blocks are added
    private void _productBlocks_CollectionChanged(object sender,
      System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        Total = 0;

        foreach (ProductBlock block in _productBlocks)
        {
            Total += block.BlockSum;
        }
    }
}
```

```
public class Customer
{
    public string Name { get; set; }
}
```

The following screenshot shows a sample template with a nested merge block. The outer block is *ProductBlocks* that contains a child block called *Products*. As *Products* is a list member of the object *ProductBlock*, it can be repeated in a separate nested block.

![Template](https://s1-www.textcontrol.com/assets/dist/blog/2017/04/21/a/assets/template.webp "Template")

The following code shows the creation of a data object and how to call the [MergeObject](https://docs.textcontrol.com/textcontrol/windows-forms/ref.txtextcontrol.documentserver.mailmerge.mergeobject.method.htm) method to start the merge process:

```
// create a new Order 
Order order = new Order()
{
    Customer = new Customer() { Name = "Peter Customer" }
};

// create a new ProductBlock
ProductBlock block = new ProductBlock();
block.Products = new List<Product>()
{
   new Product() { Name = "Product 1", Price = 200 },
   new Product() { Name = "Product 2", Price = 400 },
   new Product() { Name = "Product 3", Price = 800 }
};

// add the ProductBlock to the ObservableCollection
order.ProductBlocks.Add(block);

// create a second ProductBlock
ProductBlock block2 = new ProductBlock();
block2.Products = new List<Product>()
{
   new Product() { Name = "Product 4", Price = 231 },
   new Product() { Name = "Product 5", Price = 32424 },
   new Product() { Name = "Product 6", Price = 33 },
   new Product() { Name = "Product 7", Price = 332 },
};

order.ProductBlocks.Add(block2);

// merge the template with the business object
using (MailMerge mailMerge = new MailMerge())
{
    mailMerge.TextComponent = textControl1;
    mailMerge.MergeObject(order);
}
```

After merging the template, the *ProductBlocks* are repeated and the block sum is rendered at the end of each block. At the very end, the total sum is shown which has been calculated automatically inside the business object.

![Merge results](https://s1-www.textcontrol.com/assets/dist/blog/2017/04/21/a/assets/results.webp "Merge results")

---

## About Bjoern Meyer

As CEO, Bjoern is the visionary behind our strategic direction and business development, bridging the gap between our customers and engineering teams. His deep passion for coding and web technologies drives the creation of innovative products. If you're at a tech conference, be sure to stop by our booth - you'll most likely meet Bjoern in person. With an advanced graduate degree (Dipl. Inf.) in Computer Science, specializing in AI, from the University of Bremen, Bjoern brings significant expertise to his role. In his spare time, Bjoern enjoys running, paragliding, mountain biking, and playing the piano.

- [LinkedIn](https://www.linkedin.com/in/bjoernmeyer/)
- [X](https://x.com/txbjoern)
- [GitHub](https://github.com/bjoerntx)
