TX Text Control .NET for WPF

Images

Topic

How does a company logo get into a letter, and how does it stay at the right edge while the text flows around it? In the WPF TextControl, users insert images from the Insert tab, move and resize them with the mouse and set the text flow on the Frame Formatting tab. Your code does the same with TextControl.Images and handles the image events in XAML.

The examples are event handlers in MainWindow.xaml.cs of a window with a TextControl named textControl1, as built in Create Your First WPF Application; replace SampleApp with the namespace of your project, and copy an image named logo.png into the output folder. How ServerTextControl handles images without a user interface is described in Images, Text Frames, and Shapes.

How Images Are Placed

An Image of the TXTextControl namespace is a frame, like a text frame, a barcode or a drawing, and inherits its name, ID, size, location and text distances from FrameBase. TextControl.Images returns the ImageCollection of the main text; headers, footers and text frames have their own.

Class diagram of the image classes: TextControl, WPF.TextControl and ServerTextControl with their Images property and image events, ImageCollection with its Add, GetItem and Remove methods, and the Image class with its properties.

Each image is placed in one of three ways:

  • Inline. The image is treated like a single character and moves with the text around it.
  • Anchored to a paragraph. The image moves with its paragraph, and its location is measured from the paragraph's top-left corner. When the user drags it, it's anchored to the nearest paragraph at its new position.
  • At a fixed position. The image stays at a location on a page, or in the document, while the text changes.

Locations and sizes of images are measured in twips, 1/1440 inch, not in the device-independent units of WPF.

Insert and Arrange Images in the Ribbon

Image in the Illustrations group of the Insert tab, the RibbonInsertTab, inserts an image at the input position: Image... opens a file dialog box, and Image Placeholder adds a placeholder that a mail merge fills, as Images and Barcodes describes.

A click on an image selects it. Users move it by dragging and resize it with its handles. The Frame Formatting tab, a RibbonFrameLayoutTab, changes the selected image:

GroupCommands
ArrangeWrap Text with In Line with Text, Top and Bottom, Square, In Front of Text, Behind Text and More Layout Options...; Bring to Front, Send to Back and Position
SizeHeight and Width
PropertiesName, ID and Descriptive Text

The tab works once the RibbonFrameLayoutTab property of the TextControl holds its name; Connecting Ribbon Tabs in XAML shows the XAML. The same tab formats text frames, drawings and barcodes.

Insert an Image From Code

The following handlers open the file dialog box and anchor a logo at the right edge of the paragraph with the input position:

using System.Windows;
using TXTextControl;

namespace SampleApp;

public partial class MainWindow : Window
{
    private void InsertImage_Click(object sender, RoutedEventArgs e)
    {
        // Opens a file dialog box and inserts the image inline at the input position
        textControl1.Images.Add();
        textControl1.Focus();
    }

    private void InsertLogo_Click(object sender, RoutedEventArgs e)
    {
        // 4 is the import filter for PNG
        TXTextControl.Image logo = new TXTextControl.Image("logo.png", 4);

        // Anchored to the paragraph at the input position, the text flows around it
        textControl1.Images.Add(logo, TXTextControl.HorizontalAlignment.Right, -1,
            ImageInsertionMode.DisplaceText);

        logo.Name = "logo";
        logo.DescriptiveText = "Company logo";
    }
}

The code works as follows:

  • ImageCollection.Add without parameters shows a file dialog box and inserts the chosen image inline at the input position. The other overloads also show it when the image has no file name and no filter index.
  • The Image constructor takes a file name and the number of the import filter, 4 for PNG.
  • Add(image, alignment, textPosition, insertionMode) anchors the image to the paragraph that contains the text position, where -1 is the input position. The alignment is Left, Right or Center, and a value of ImageInsertionMode sets the text flow: DisplaceText lets the text flow around the image, DisplaceCompleteLines continues it below the image, and AboveTheText and BelowTheText put the image in front of or behind the text.
  • FrameBase.Name identifies the image for your code, and DescriptiveText is its alternative text for screen readers and tagged PDF files.
  • WPF has classes named Image and HorizontalAlignment of its own, so the code writes TXTextControl.Image and TXTextControl.HorizontalAlignment.

Add(image, page, location, insertionMode) places an image at a fixed location on a page, measured from the page's top-left corner. Add(image, location, insertionMode) measures from the top-left corner of the whole document, gaps between the pages included: horizontally from the left edge of the pages, vertically from the top of the document with the gap above the first page. Whichever of the two inserted a fixed image, its Location is relative to the document; for an anchored image, it's relative to the paragraph, and for an inline image, or an anchored image with an alignment, it's zero.

Alignment, Left, Right or Center, exists only for images anchored to a paragraph. Inline images and images at a fixed position have no horizontal alignment.

Change the Z-Order

Images in front of or behind the text can overlap each other and other frames. ChangeZOrder does in code what Bring to Front and Send to Back do in the ribbon:

private void LogoToFront_Click(object sender, RoutedEventArgs e)
{
    TXTextControl.Image logo = textControl1.Images.GetItem();

    // Move the selected image in front of the text, above all other objects
    logo?.ChangeZOrder(ZOrder.TopMost);
}

A value of ZOrder says where the frame goes: TopMost in front of the text above all other objects, BottomMost behind the text below all others (only for a frame that is behind the text), Up and Down one plane without crossing the text, and Top and Bottom to the top or the bottom of the objects on its own side of the text. The method returns false when the order can't be changed.

Insert an Image From Memory

Images from a database or a web service arrive as bytes. The Image constructor that takes a MemoryStream needs a stream whose buffer is publicly visible:

using System.IO;
using System.Windows;
using TXTextControl;

namespace SampleApp;

public partial class MainWindow : Window
{
    private void InsertImageFromBytes(byte[] data)
    {
        // The Image class needs a stream whose buffer is publicly visible
        using (MemoryStream stream = new MemoryStream(data, 0, data.Length, false, true))
        {
            TXTextControl.Image image = new TXTextControl.Image(stream);
            textControl1.Images.Add(image, -1);
        }
    }
}

The fifth argument of the MemoryStream constructor, publiclyVisible, is true. The Image class also has a constructor for a System.Drawing.Image, which a .NET WPF project doesn't reference: to compile a call of the stream constructor, install the NuGet package System.Drawing.Common in the major version of your .NET, for example 8.0 for .NET 8. Otherwise the compiler reports error CS0012 for the type Image in System.Drawing.Common.

Image Formats

ImageCollection.ImportFilters returns the formats TX Text Control reads as a filter string. Assigned to the Filter property of an OpenFileDialog of your own, it lists them the way the blog article Custom Insert Image Dialog in TX Text Control .NET for WPF does:

A Windows Open dialog box whose file type list shows the image formats of TX Text Control, starting with Windows Bitmap, Tagged Image Format, Windows Metafile and Portable Network Graphics.

The number of a format in this list is its filter index, and the first eight are:

Filter indexFormat
1Windows Bitmap (BMP)
2Tagged Image Format (TIF)
3Windows Metafile (WMF)
4Portable Network Graphics (PNG)
5JPEG (JPG, JPEG)
6Graphics Interchange Format (GIF)
7Windows Enhanced Metafile (EMF)
8Scalable Vector Graphics (SVG)

React to the Selected Image

Image events are declared on the TextControl element in MainWindow.xaml, where WPF is the prefix of the namespace TXTextControl.WPF:

<WPF:TextControl x:Name="textControl1"
                 ImageDoubleClicked="TextControl1_ImageDoubleClicked" />

The following handlers open the image dialog box on a double-click and halve the size of the selected image:

using System.Windows;
using TXTextControl;

namespace SampleApp;

public partial class MainWindow : Window
{
    private void TextControl1_ImageDoubleClicked(object sender, ImageEventArgs e)
    {
        // A double-click selects the image, so the dialog box opens for it
        textControl1.ImageAttributesDialog();
    }

    private void HalveImage_Click(object sender, RoutedEventArgs e)
    {
        // The image the user has selected, or null
        TXTextControl.Image image = textControl1.Images.GetItem();

        if (image != null)
        {
            // Scaling factors in percent
            image.HorizontalScaling = 50;
            image.VerticalScaling = 50;
        }
    }
}

The code works as follows:

ImageClicked, ImageMoved, ImageSized and ImageDeleted report other actions, and ImageCreated occurs for pasted and loaded images, not for images added by your code. FrameSelected and TextControl.Frames cover images, text frames, barcodes and drawings together. See Events.

Control How Images Are Saved

An image inserted from a file is saved as a reference to the file with its absolute path; a pasted image is saved with its data. On another computer, a missing file is shown as a rectangle with the file name, so save documents that leave the machine with the image data:

The saving tab of the image dialog box offers users the same options. See Loading and Saving Documents for SaveSettings and LoadSettings.

Image Formats per Document Format

Not every document format can hold every image format. When a document format doesn't support the export format you set, the image is saved in the default format of the document format, the first one in the table:

Document formatImage formats, default first
RTFPNG, BMP, WMF, JPG, EMF
DOCWMF, BMP, PNG, JPG, EMF, TIF
DOCXPNG, BMP, WMF, JPG, EMF, TIF, GIF, SVG
HTMLPNG, BMP, JPG, GIF
PDFBMP, JPG; the vector formats EMF, WMF and SVG are converted to PostScript and stay vector graphics
TX Text Control formatWMF, BMP, PNG, JPG, EMF, TIF, GIF, SVG

Where Image Files Are Written

An image saved as a file reference, with ImageSaveMode.SaveAsFileReference, needs a file. If the file doesn't exist, for example because the image was pasted or converted to another format, TX Text Control creates it when the document is saved, by these rules:

  • Folder. SaveSettings.ImageSavePath sets the folder, which is created if it doesn't exist. Without it, a folder named after the document with _Images appended is created next to the document: C:\Temp\sample.rtf writes its images to C:\Temp\sample_Images\.
  • File name. Image.ExportFileName sets the name of one image; users set it on the saving tab of the image dialog box. Without it, the original file name of the image is used, with the extension of the export format.
  • Numbered names. An image without any file name, such as a pasted one, is named after the document and a number: sample_1.bmp, sample_2.bmp and so on.
private void SaveWithImageFiles_Click(object sender, RoutedEventArgs e)
{
    SaveSettings saveSettings = new SaveSettings()
    {
        // Save all images as files in C:\Temp\images, as PNG
        ImageSaveMode = ImageSaveMode.SaveAsFileReference,
        ImageSavePath = @"C:\Temp\images",
        ImageExportFilterIndex = 3
    };

    textControl1.Save(@"C:\Temp\sample.htm", StreamType.HTMLFormat, saveSettings);
}

Next Steps