Loading Documents
Whether a WPF application opens a contract the user picked, a template stored in a database or a PDF attached to an email, it loads the document into the TextControl of its window or into a ServerTextControl. Beyond the basic Load call, you choose whether a document replaces, extends or goes into the current one, control what is imported, read the properties of the loaded file and react to documents that can't be loaded.
For the Open dialog box and a first set of load and save buttons, see Load and Save Documents and Loading and Saving Documents. Supported File Formats lists the formats and their StreamType values. The examples are Click handlers in MainWindow.xaml.cs, which needs using System.Windows; and using TXTextControl;, and work on the TextControl named textControl1. A ServerTextControl has the same methods.
Load, Append or Insert
The three methods that load a document accept the same sources and formats and an optional LoadSettings object. They differ in what they do with the current contents:
| Method | What happens to the current contents |
|---|---|
| TextControl.Load | They're replaced completely |
| TextControl.Append | They stay; the document is added at the end, as an AppendSettings value sets |
| Selection.Load | The selected text is replaced; with nothing selected, the document is inserted at the input position |
The following handler loads a letter, appends a signature and puts an attachment where a placeholder was:
private void LoadLetterButton_Click(object sender, RoutedEventArgs e)
{
// Load replaces the complete contents
textControl1.Load("letter.docx", StreamType.WordprocessingML);
// Append adds a document at the end, here in a new paragraph
textControl1.Append("signature.rtf", StreamType.RichTextFormat,
AppendSettings.StartWithNewParagraph);
// Selection.Load replaces the selected text, here a placeholder
const string placeholder = "[ATTACHMENT]";
int position = textControl1.Find(placeholder, 0, FindOptions.NoMessageBox);
if (position >= 0)
{
textControl1.Select(position, placeholder.Length);
LoadSettings loadSettings = new LoadSettings()
{
// Create a new paragraph before the document is loaded
AddParagraph = true
};
textControl1.Selection.Load("attachment.docx", StreamType.WordprocessingML, loadSettings);
}
}
Only Selection.Load uses LoadSettings.AddParagraph. By default, the document goes into the existing paragraph, whose attributes are merged with those of the last loaded paragraph. With true, a new paragraph is created first, and it takes the attributes of the last loaded paragraph. The AppendSettings values are listed in Creating Documents.
Control the Import With LoadSettings
Set these properties of LoadSettings before you load, and they decide what is imported:
| Property | Sets | Default |
|---|---|---|
| ApplicationFieldFormat | Whether fields of Word and RTF documents are imported as application fields, as a value of ApplicationFieldFormat | None |
| ApplicationFieldTypeNames | The field types to import, such as MERGEFIELD, when ApplicationFieldFormat is MSWord | All types |
| LoadSubTextParts | Whether bookmarks that extend over several characters become subtext parts, for DOCX, DOC and RTF | false |
| LoadImages | Whether images are loaded; otherwise a rectangle with the image's name is shown | true |
| LoadHypertextLinks | Whether hypertext links are loaded | true |
| LoadDocumentBackColor | Whether the background color of the document is loaded | true |
| ImageSearchPath | A folder that replaces the absolute paths of linked images and other resources | — |
| DocumentBasePath | A base path that resolves the relative file paths of the document | — |
| PDFImportSettings | How the structure of a PDF document is rebuilt | GenerateTextFrames |
| DocumentPartName | The worksheet of an XLSX file; an empty string lets the user choose it in a dialog box | The first worksheet |
| UserPassword, MasterPassword | The passwords of an encrypted PDF document | — |
The following handler loads a Word template with only its merge fields and its merge blocks, leaving out hypertext links and the background color:
private void LoadTemplateButton_Click(object sender, RoutedEventArgs e)
{
LoadSettings loadSettings = new LoadSettings()
{
ApplicationFieldFormat = ApplicationFieldFormat.MSWord,
ApplicationFieldTypeNames = new string[] { "MERGEFIELD" }, // only merge fields
LoadSubTextParts = true, // bookmarks over several characters become subtext parts
LoadHypertextLinks = false,
LoadDocumentBackColor = false
};
textControl1.Load("template.docx", StreamType.WordprocessingML, loadSettings);
MessageBox.Show($"Merge fields: {textControl1.ApplicationFields.Count}, " +
$"subtext parts: {textControl1.SubTextParts.Count}");
}
ApplicationFieldFormat takes one of four values, and it applies to Word and RTF documents only:
| Value | Imports |
|---|---|
None | No application fields; Word form fields become form fields of TX Text Control |
MSWord | The fields of Microsoft Word as application fields, form fields and content controls included |
MSWordTXFormFields | The fields of Microsoft Word as application fields, except form fields, which become form fields of TX Text Control |
HighEdit | The fields of the Heiler HighEdit component as application fields |
When ApplicationFieldFormat isn't set, Word fields aren't imported as fields, and ApplicationFields stays empty. The merge blocks of a mail merge template are subtext parts whose names begin with txmb_; with LoadSubTextParts set to true, they are back in SubTextParts after loading. Fields and Subtext Parts describes both collections, and PDF Import and Its Limitations the values of PDFImportSettings.
Read Information About a Loaded Document
The LoadSettings object you pass is filled with information about the document during the load, also when the user picks the file in the Open dialog box, which Open and Save With the Built-In Dialog Boxes describes. The following handler opens the dialog box and shows what the LoadSettings object contains afterward:
private void OpenButton_Click(object sender, RoutedEventArgs e)
{
LoadSettings loadSettings = new LoadSettings();
// Opens the Open dialog box with the filters of these formats
TXTextControl.WPF.DialogResult result = textControl1.Load(
StreamType.WordprocessingML | StreamType.RichTextFormat | StreamType.AdobePDF,
loadSettings);
if (result != TXTextControl.WPF.DialogResult.OK)
{
return;
}
StringBuilder info = new StringBuilder();
info.AppendLine($"File: {loadSettings.LoadedFile}");
info.AppendLine($"Format: {loadSettings.LoadedStreamType}, {loadSettings.BytesRead} bytes");
info.AppendLine($"Title: {loadSettings.DocumentTitle}");
info.AppendLine($"Author: {loadSettings.Author}");
info.AppendLine($"Last modified: {loadSettings.LastModificationDate}");
if (loadSettings.UserDefinedDocumentProperties != null)
{
foreach (string name in loadSettings.UserDefinedDocumentProperties.Names)
{
info.AppendLine($"{name}: {loadSettings.UserDefinedDocumentProperties[name]}");
}
}
MessageBox.Show(info.ToString());
}
The handler also needs using System.Text;. The information properties are read only:
| Property | Contains |
|---|---|
| LoadedFile, LoadedStreamType, BytesRead | The file with its path, the format that was loaded and the number of bytes read |
DocumentTitle, Author, DocumentSubject, DocumentKeywords, CreatorApplication | The metadata of the document |
CreationDate, LastModificationDate | The dates of the document; January 1, 0001 if the document has none |
| UserDefinedDocumentProperties | A UserDefinedPropertyDictionary with the user-defined properties, or null |
| PageSize, PageMargins | The page size, 0 if the document has none, and the page margins, -1 if the document has none |
| DocumentAccessPermissions | What may be done with a loaded PDF document |
The keys of the dictionary are in its Names, and its values are strings, Boolean values, integers or double values. The same metadata goes into DocumentSettings of the TextControl and is saved with the document again.
Load From Byte Arrays, Strings and Streams
A document stored in a database comes as a byte array, and HTML or RTF often as a string. Use a BinaryStreamType for bytes and a StringStreamType for a string:
private void LoadFromDatabaseButton_Click(object sender, RoutedEventArgs e)
{
// For example the contents of a database column
byte[] data = File.ReadAllBytes("invoice.docx");
textControl1.Load(data, BinaryStreamType.WordprocessingML);
// HTML and RTF are loaded from a string
string html = "<h1>Terms</h1><p>Payment is due within 14 days.</p>";
textControl1.Append(html, StringStreamType.HTMLFormat, AppendSettings.StartWithNewSection);
}
This handler needs using System.IO;. BinaryStreamType covers DOCX, DOC, PDF, XLSX and the TX Text Control formats; HTML and RTF come from a string or a file. The only stream Load accepts is a FileStream, so a MemoryStream or a stream from a web request is loaded through its bytes, stream.ToArray().
Load Password-Protected PDF Documents
Without its user password, a protected PDF document can't be opened, and Load throws a FilterException with the Reason InvalidPassword. Your application asks the user for the password and loads the document again with it in UserPassword of the LoadSettings; afterward, DocumentAccessPermissions says what the document allows. Open a Password-Protected PDF in Passwords and Access Permissions shows the code with a password prompt.
Handle Errors and the DocumentLoaded Event
A document that can't be imported makes Load throw a FilterException, whose Reason is a value of FilterException.FilterError, such as WrongFileType or InvalidPassword. Catch Errors When Opening a Document in Exceptions and Error Handling shows the try block, and Read the Reason of a FilterException in Troubleshooting lists every reason with its meaning. AdobePDFA and CascadingStylesheet can only be saved, not loaded.
When a document has been loaded, the TextControl raises DocumentLoaded. Connect it in XAML and update the window there:
<WPF:TextControl x:Name="textControl1" DocumentLoaded="TextControl1_DocumentLoaded" />
private void TextControl1_DocumentLoaded(object sender, System.EventArgs e)
{
Title = $"{textControl1.Pages} pages";
}
DocumentLoaded isn't the WPF Loaded event, which occurs once when the control is added to the window.
Next Steps
- Saving and Converting Documents: save loaded documents in another format.
- Creating Documents: assemble documents from several sources.
- Fields and Subtext Parts: work with the fields imported from Word documents.
- Document Loading and Conversion: load large numbers of documents efficiently.