TX Text Control .NET for WPF

Exceptions and Error Handling

Topic

Which calls of TX Text Control can fail, and how does a WPF application find out? Errors of the document engine, such as a file that can't be converted or a feature outside the licensed edition, arrive as exceptions of TX Text Control. Wrong arguments cause the usual .NET exceptions, and many expected failures come back as a return value instead. Each needs its own handling in the code-behind: a try block, a check of the result, or a handler of last resort for the whole application.

The examples belong in MainWindow.xaml.cs with the TextControl named textControl1. Specific error messages and their fixes are collected in Troubleshooting.

MainWindow.xaml.cs needs these using directives:

using System;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Win32;
using TXTextControl;

How TX Text Control Reports Errors

Kind of errorHow it's reportedExamples
An error of the document engineAn exception class of TX Text ControlA document that can't be loaded
Wrong use of the programming interfaceArgumentException, ArgumentNullException or ArgumentOutOfRangeException for a wrong parameter or property value; InvalidOperationException for a call that the object's state doesn't allowA conditional instruction without a name
An expected failureA return valueAn Add method returns false or an AddResult; ServerTextControl.Create returns false

Return values throw nothing, so check them where your code depends on the result.

TX Text Control Exceptions

Each message contains an error number in parentheses, for example (1-1D01). It is more exact than the text of the message; the blog article Text Control Error Navigator Launched explains how to look it up.

Catch Errors When Opening a Document

Documents come from outside your application, so loading needs a try block. FilterException.Reason is a FilterException.FilterError value that names the cause:

private void Open_Click(object sender, RoutedEventArgs e)
{
    OpenFileDialog dialog = new OpenFileDialog { Filter = "Word documents|*.docx" };

    if (dialog.ShowDialog(this) != true)
    {
        return;
    }

    try
    {
        textControl1.Load(dialog.FileName, StreamType.WordprocessingML);
    }
    catch (FilterException exception)
    {
        string text = exception.Reason switch
        {
            FilterException.FilterError.WrongFileType => "The file isn't a Word document.",
            FilterException.FilterError.Encrypted => "The document is encrypted.",
            FilterException.FilterError.CannotOpen => "The file couldn't be opened.",
            _ => "The document couldn't be loaded."
        };

        MessageBox.Show(this, $"{text}\n\n{exception.Message}", "Open", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

The code works as follows:

  • OpenFileDialog of Microsoft.Win32 is the file dialog of WPF; its ShowDialog returns a nullable bool.
  • WPF.TextControl.Load throws the FilterException when the document can't be converted.
  • The switch expression gives three common reasons a message of their own. Others are CannotRead, FileTooBig, Unsupported, NotWellFormed and InvalidPassword, and saving can fail with NonEmbeddableFont, for example.
  • Errors of the file system, such as an IOException for a locked file, aren't a FilterException; catch them separately if needed.

Every reason and its fix is listed in Document Conversion in Troubleshooting.

Handle a Feature of Another Edition

A LicenseLevelException points to code that uses a feature the licensed edition doesn't have, such as form fields in the Professional edition. Change the code or the license: Editions lists the features per edition, and Licensing in Troubleshooting shows the messages. Where the application can go on without the feature, catch the exception:

private void InsertFormField_Click(object sender, RoutedEventArgs e)
{
    try
    {
        textControl1.FormFields.Add(new TextFormField(1440));
    }
    catch (LicenseLevelException exception)
    {
        MessageBox.Show(this, exception.Message, "Not available", MessageBoxButton.OK, MessageBoxImage.Information);
    }
}

Catch Errors of Background Work

Code in Task.Run runs on another thread, and an exception there is stored in the task. await throws it again on the UI thread, so a try block around the await catches it:

private async void Convert_Click(object sender, RoutedEventArgs e)
{
    try
    {
        await Task.Run(() =>
        {
            using (ServerTextControl tx = new ServerTextControl())
            {
                if (!tx.Create())
                {
                    throw new InvalidOperationException("TX Text Control could not be initialized.");
                }

                tx.Load("contract.docx", StreamType.WordprocessingML);
                tx.Save("contract.pdf", StreamType.AdobePDF);
            }
        });

        MessageBox.Show(this, "contract.pdf created.");
    }
    catch (FilterException exception)
    {
        MessageBox.Show(this, $"{exception.Reason}: {exception.Message}", "Conversion failed");
    }
}

The using block disposes the ServerTextControl even when an exception leaves it. Using ServerTextControl in Desktop Applications covers background work. A MergeCanceledException from a merge you cancel in a DataRowMerged handler is caught the same way around the call that merges; Merge Events describes the event.

Handle Exceptions Nobody Caught

An exception that reaches the WPF dispatcher without a catch block ends the application. The TX Words samples handle the DispatcherUnhandledException event of the application, show the message and mark the exception as handled, so that the user can continue after a LicenseLevelException. Attach the handler in App.xaml.cs:

using System.Windows;
using System.Windows.Threading;

public partial class App : Application
{
    public App()
    {
        DispatcherUnhandledException += App_DispatcherUnhandledException;
    }

    private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        MessageBox.Show(e.Exception.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);

        // Continue only after exceptions the application can recover from
        e.Handled = e.Exception is TXTextControl.LicenseLevelException;
    }
}

e.Handled = true keeps the application running. Set it only for exceptions after which your application is in a usable state, and catch other errors where you call TX Text Control. Dispatcher and Thread Access in Troubleshooting describes the case.

Next Steps