# Objects and Collections

> Merge WPF view models and business objects with MergeObjects and MergeObject, rename fields with DisplayName and load the classes into the Reporting tab.

- **Product:** TX Text Control .NET for WPF
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET for WPF > Mail Merge and Reporting > Data Sources > Objects and Collections
- **3339 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/objects/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/objects/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/objects/llms-full.txt

---

WPF applications often have their data in classes already, for example the models a window binds to. `MailMerge` merges such objects without conversion: public properties become merge fields, and properties that hold collections fill merge blocks. You can merge collections and single objects, rename fields with attributes, format text in `ToString`, offer the classes in the **Reporting** tab and merge dictionaries whose keys are known only at run time.

The examples extend the invoice classes `Invoice` and `InvoiceItem` of the Getting Started tutorial [Add Repeating Merge Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/getting-started/mail-merge/blocks/llms-full.txt). They assume a window with a `TextControl` named `textControl1` and a `RibbonReportingTab` named `ribbonReportingTab1`, connected through the `RibbonReportingTab` property of the `TextControl`, as the tutorials set them up.

### How MailMerge Reads Objects

[MergeObjects](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/mergeobjects-method/llms-full.txt) takes any `IEnumerable`, and [MergeObject](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver/mailmerge/mergeobject-method/llms-full.txt) one object. Both read the public properties through .NET reflection:

| Property | Becomes |
|---|---|
| A simple type, such as `string`, `int`, `DateTime` or an enumeration | A column: the merge field of the same name receives the text of its `ToString` method |
| A type with public properties of its own, such as `Customer` | A column merged with `ToString`, or a related table with one row whose properties are addressed with dot notation, such as `Customer.City` |
| A collection, a type that implements `IEnumerable`, such as `List<InvoiceItem>` | A related table that fills the merge block of the same name, also in nested blocks |
| A read-only calculated property, such as `Total => Quantity * Price` | A column like any other property |

The type of the first object in a collection defines the structure; objects of other types in the same collection still merge, but some of their fields stay empty. Each object of the collection produces one copy of the template, and with `append` set to `true`, the default, the copies form one document.

View models of an MVVM application are objects like any other. Merge them, or classes built from them, as long as their public properties have the names the template uses.

In the following class diagram of a Text Control sample, the `Products` property of `Invoice` is a list of `Product` objects and fills a merge block `Products` with the fields `Name` and `Price`:

![A class diagram with the class Invoice, whose property Products is a generic List of the class Product with the properties Name and Price.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-wpf/invoice-class-diagram.85702aad.webp)

### Control Field Names and Merged Text

Two attributes of `System.ComponentModel` affect the merge:

- **`DisplayName`** gives a property a different field name: with `[DisplayName("Date")]`, `InvoiceDate` fills the field `Date`.
- **`Description`** on an enumeration value sets the text merged for it.

The invoice classes with a date, a status and a calculated item total:

```csharp
using System;
using System.Collections.Generic;
using System.ComponentModel;

public class Invoice
{
    public string Company { get; set; }
    public string Name { get; set; }
    public string InvoiceNumber { get; set; }

    // Merged into a field named "Date"
    [DisplayName("Date")]
    public DateTime InvoiceDate { get; set; }

    public InvoiceStatus Status { get; set; }
    public List<InvoiceItem> Items { get; set; } = new List<InvoiceItem>();
}

public class InvoiceItem
{
    public string Description { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }

    // Calculated by the object, merged like any other property
    public decimal Total => Quantity * Price;
}

public enum InvoiceStatus
{
    [Description("Awaiting payment")]
    Open,
    Paid
}
```

For an open invoice, the field `Status` shows "Awaiting payment", and a field `Total` in the `Items` block shows each item's total. Calculations like this belong in the objects rather than in the template; the blog article [Merging Self-Calculating Business Objects with TX Text Control MailMerge in C#](https://www.textcontrol.com/blog/2024/12/27/merging-self-calculating-business-objects-with-tx-text-control-mailmerge-in-csharp/) builds a complete invoice on this idea.

### Merge a Collection Into the Editor

Add these methods to *MainWindow.xaml.cs*. The second one is the `Click` handler of a button named `mergeObjectsButton`, connected in XAML with `Click="mergeObjectsButton_Click"`:

```csharp
using System;
using System.Collections.Generic;
using System.Windows;
using TXTextControl.DocumentServer;
```

```csharp
private List<Invoice> GetInvoices()
{
    Invoice invoice = new Invoice
    {
        Company = "Contoso Ltd.",
        Name = "Jane Miller",
        InvoiceNumber = "INV-1001",
        InvoiceDate = new DateTime(2026, 9, 1),
        Status = InvoiceStatus.Open
    };

    invoice.Items.Add(new InvoiceItem { Description = "Consulting", Quantity = 2, Price = 150.00m });
    invoice.Items.Add(new InvoiceItem { Description = "Training", Quantity = 1, Price = 480.00m });

    return new List<Invoice> { invoice };
}

private void mergeObjectsButton_Click(object sender, RoutedEventArgs e)
{
    using (MailMerge mailMerge = new MailMerge { TextComponent = textControl1 })
    {
        // Merge the template in the editor; the result replaces it
        mailMerge.MergeObjects(GetInvoices());
    }
}
```

The code works as follows:

- `TextComponent = textControl1` connects `MailMerge` to the WPF `TextControl`, so the document in the editor is the template and the merged invoice replaces it. A `ServerTextControl` keeps the template untouched, as [Overview](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/overview/#create-one-document-or-one-per-record) shows.
- `MergeObjects` merges each `Invoice` of the list: the fields receive `Company`, `Name`, `InvoiceNumber`, `Date` and `Status`, and the `Items` block one row per `InvoiceItem`.

Data from a database can take the same route: query it, for example with Entity Framework, into classes shaped like the document, and merge those.

### Offer the Classes in the Reporting Tab

To design a template, users need the names of the fields and blocks. [LoadObjects](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/loadobjects-method/llms-full.txt) of the [DataSourceManager](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-wpf/ribbonreportingtab/datasourcemanager-property/llms-full.txt) that the Reporting tab provides reads a collection like `MergeObjects` and fills **Insert Merge Field** and **Insert Merge Block** with its properties. Call it when the window has loaded, for example in the `Loaded` event handler of the `TextControl`, connected with `Loaded="textControl1_Loaded"` on the `TextControl` element:

```csharp
private void textControl1_Loaded(object sender, RoutedEventArgs e)
{
    // Offer the properties of Invoice in the drop-downs of the Reporting tab
    ribbonReportingTab1.DataSourceManager.LoadObjects(GetInvoices());
}
```

[LoadSingleObject](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/loadsingleobject-method/llms-full.txt) loads a single object. [LoadAssembly](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/reference/txtextcontrol-documentserver-datasources/datasourcemanager/loadassembly-method/llms-full.txt) loads a compiled assembly and deduces tables, columns and relations from the public properties of all its public classes; users do the same with **Load Assembly** in the **Select Data Source** drop-down.

### Format Text in ToString

A property whose type has properties of its own can also fill one field as a whole. A merge field with the property's name receives the text of `ToString`, so an override can return a formatted block, such as an address on two lines:

```csharp
using System;

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

    // The text a merge field named "Customer" shows
    public override string ToString() => Name + Environment.NewLine + City;
}
```

With a property `Customer` of this type, the field `Customer` shows the name and the city on two lines, and fields with dot notation, such as `Customer.City`, address the single values instead.

The `Customer` class of the same sample overrides `ToString` this way:

![The class diagram of Customer with the properties Address, FirstName, LastName and Phone and the methods Customer and ToString.](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-wpf/customer-class-diagram.f11743a4.webp)

### Merge Dictionaries and Anonymous Objects

When the field names are known only at run time, for example because users define them, merge dictionaries with string keys: `MergeObjects` accepts a collection of `Dictionary` instances, and the keys are the field names. `MergeObject` accepts an object of any type, an anonymous object included:

```csharp
Dictionary<string, object> invoice = new Dictionary<string, object>
{
    ["Company"] = "Contoso Ltd.",
    ["Name"] = "Jane Miller",
    ["InvoiceNumber"] = "INV-1001"
};

mailMerge.MergeObjects(new List<Dictionary<string, object>> { invoice });

// or, with an anonymous object
mailMerge.MergeObject(new { Company = "Contoso Ltd.", Name = "Jane Miller", InvoiceNumber = "INV-1001" });
```

The key names and value types of the first dictionary define the structure.

### Next Steps

- [DataSets and DataTables](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/datasets/llms-full.txt): merge tables with relations.
- [JSON and XML](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/json-xml/llms-full.txt): merge the same invoice as JSON.
- [Merge Blocks](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/blocks/llms-full.txt): the blocks that collections fill.
- [Nested Blocks and Master-Detail Reports](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/nested-blocks/llms-full.txt): collections inside collections.

---

## Nearby

- **Previous:** [Data Sources](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/llms-full.txt)
- **Next:** [DataSets and DataTables](https://docs.textcontrol.com/tx-text-control-dotnet-wpf/mail-merge/data-sources/datasets/llms-full.txt)
