This chapter contains a collection of how-to guides for specific typical tasks that can be realized with TX Text Control. In contrast to the shipped sample applications that are described in the different user's guides, this chapter describes the non-basic functionality of TX Text Control.
TX Text Control .NET 15.0 introduced the page rendering engine that allows you to export a metafile or a bitmap of each separate page. This enables developers to create thumbnails of the pages or to export images to view them in a browser. This sample shows how to create a multipage TIFF image from all pages of a document.
Two significant steps are required to create these images:
First, it is required to iterate through all pages of TX Text Control to create separate TIFF images:
ArrayList inputImages = new ArrayList();
foreach (Page page in textControl1.GetPages())
{
MemoryStream image = new MemoryStream();
Bitmap bitmap = page.GetImage(100, TXTextControl.Page.PageContent.All);
bitmap.Save(image, ImageFormat.Tiff);
inputImages.Add(image);
}
Each TIFF image is stored in a memory stream which is added to an ArrayList for an easier handling when combining them.
In a second step, the TIFF images are combined to a single image. Therefore, a new image is created in order to append all other images from the ArrayList to a new frame of the new image using the SaveAdd method.
public static void CreateMultipageTIF(ArrayList InputImages, string Filename)
{
// set the image codec
ImageCodecInfo info = null;
foreach (ImageCodecInfo ice in ImageCodecInfo.GetImageEncoders())
{
if (ice.MimeType == "image/tiff")
{
info = ice;
break;
}
}
EncoderParameters ep = new EncoderParameters(2);
bool firstPage = true;
System.Drawing.Image img = null;
// create an image instance from the 1st image
for (int nLoopfile = 0; nLoopfile < InputImages.Count; nLoopfile++)
{
//get image from src file
System.Drawing.Image img_src = System.Drawing.Image.FromStream((Stream)InputImages[nLoopfile]);
Guid guid = img_src.FrameDimensionsList[0];
System.Drawing.Imaging.FrameDimension dimension = new System.Drawing.Imaging.FrameDimension(guid);
//get the frames from src file
for (int nLoopFrame = 0; nLoopFrame < img_src.GetFrameCount(dimension); nLoopFrame++)
{
img_src.SelectActiveFrame(dimension, nLoopFrame);
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, Convert.ToInt32(EncoderValue.CompressionLZW));
// if first page, then create the initial image
if (firstPage)
{
img = img_src;
ep.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, Convert.ToInt32(EncoderValue.MultiFrame));
img.Save(Filename, info, ep);
firstPage = false;
continue;
}
// add image to the next frame
ep.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, Convert.ToInt32(EncoderValue.FrameDimensionPage));
img.SaveAdd(img_src, ep);
}
}
ep.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, Convert.ToInt32(EncoderValue.Flush));
img.SaveAdd(ep);
}
Starting with TX Text Control .NET 14.0, MS Word Merge Fields can be imported into the TextControl or ServerTextControl with the LoadSettings class. The LoadSettings.ApplicationFieldFormat property is used to enable TX Text Control to load the ApplicationField type text fields.
For MS Word merge fields, it must be set to ApplicationFieldFormat.MSWord.
The LoadSettings.ApplicationFieldTypeNames property sets the type name of each field. For MS Word Merge Fields and Form Fields, it is MERGEFIELD.
The ApplicationField.Parameters property defines the field's parameters, e.g. its functionality.
It has the format MERGEFIELD FieldName [switches].
The following code imports MS Word merge fields from a DOCX file:
TXTextControl.LoadSettings ls = new TXTextControl.LoadSettings();
ls.ApplicationFieldFormat = ApplicationFieldFormat.MSWord;
ls.ApplicationFieldTypeNames = new string[] { "MERGEFIELD" };
textControl1.Load(@"C: est.docx", TXTextControl.StreamType.WordprocessingML, ls);
Each MS Word merge field has been imported into the TextControl.ApplicationFields collection after that.
Tables are often used to display data and TX Text Control's ApplicationField type text fields are the perfect tool to merge data into tables. The whole procedure can be divided into three simple steps:
The following code inserts a 6 x 3 table and adds the header texts to the first table row.
To format the first table row's text bold, the cursor is set to the first text position in the first table cell with
textControl1.Selection.Start = table.Cells.GetItem(1, 1).Start - 1;.
The lenght of the selection is calculated with
textControl1.Selection.Length = table.Cells.GetItem(1, 3).Start - 1 + table.Cells.GetItem(1, 3).Length;.
As the Selection class is zero-based and the TableCell text index one-based, it is necessary to subtract one from the TableCell text index.
int tableID = 10;
textControl1.Tables.Add(6, 3, tableID);
TXTextControl.Table table = textControl1.Tables.GetItem(tableID);
table.Cells.GetItem(1, 1).Text = "Date / Time";
table.Cells.GetItem(1, 2).Text = "Customer data";
table.Cells.GetItem(1, 3).Text = "Result";
textControl1.Selection.Start = table.Cells.GetItem(1, 1).Start - 1;
textControl1.Selection.Length = table.Cells.GetItem(1, 3).Start - 1
+ table.Cells.GetItem(1, 3).Length;
textControl1.Selection.Bold = true;
textControl1.Selection.ParagraphFormat.Alignment = TXTextControl.HorizontalAlignment.Center;
textControl1.Selection.Length = 0;
Next, the TableCellCollection is looped to insert variable data, like date, customer number and result. The customer number will be inserted in an ApplicationField text field.
foreach (TXTextControl.TableCell cell in table.Cells)
{
if (cell.Row == 1)
continue;
if (cell.Column == 1)
cell.Text = DateTime.UtcNow.ToString();
if (cell.Column == 2)
{
textControl1.Selection.Start = cell.Start - 1;
TXTextControl.ApplicationField appField = new TXTextControl.ApplicationField(
TXTextControl.ApplicationFieldFormat.MSWord,
"MERGEFIELD",
cell.Row.ToString(),
new string[] { "FieldName" });
appField.DoubledInputPosition = true;
appField.ShowActivated = true;
textControl1.ApplicationFields.Add(appField);
textControl1.Selection.ParagraphFormat.Alignment = TXTextControl.HorizontalAlignment.Center;
}
if (cell.Column == 3)
cell.Text = Guid.NewGuid().ToString().Substring(1, 10);
}
For some applications, it is necessary to replace the default keys with another key or action. The following code intercepts the Enter key and inserts a line break instead of a paragraph.
private void textControl1_KeyPress(object sender, KeyPressEventArgs e)
{
if(e.KeyChar.Equals('
'))
{
e.Handled = true;
textControl1.Selection.Text = "";
}
}
Scrolling in TX Text Control .NET is pretty easy when working with text fields. However, if you want to scroll to a certain text position, it is not that easy. The following method shows a way how to do that.
Please note: The textPosition value is zero-based (like the Selection.Start or the InputPosition.TextPosition property) and has to be increased by 1 to get the requested character of the TextCharCollection. If the increased value is greater than the number of TextChars of the TextCharCollection, the Y-coordinate of the last line in the TextControl will be used to set the TextControl.ScrollLocation.
private void scrollToTextPosition(int textPosition)
{
Point newScrollLocation = textControl1.ScrollLocation;
if (textPosition & 1 <= textControl1.TextChars.Count)
{
newScrollLocation = new Point(0, textControl1.TextChars[textPosition + 1].Bounds.Y);
}
else
{
newScrollLocation = new Point(0, textControl1.Lines[textControl1.Lines.Count].TextBounds.Y);
}
textControl1.ScrollLocation = newScrollLocation;
}
The default print controller of .NET displays a status dialog that shows the status of the printing process like this:
'Page 5 of document'
If you want to print without such a print status dialog, you will need to use the StandardPrintController instead of the default PrintControllerWithStatusDialog.
The StandardPrintController is part of the System.Drawing.Printing namespace. The following code prints the content of TX Text Control without such print status dialog:
PrintDocument printDoc = new PrintDocument();
PrintController printCtl = new StandardPrintController();
printDoc.PrintController = printCtl;
textControl1.Print(printDoc);
There are two ways to manipulate a range of text:
Most users are going with the first, more obvious approach. A range of text is selected and changed using a sequence of Selection properties:
textControl1.Select(0, 5);
textControl1.Selection.Bold = true;
textControl1.Selection.Italic = true;
textControl1.Selection.Text = "New Text";
This works very reliably and can be used in most scenarios. But this approach has two drawbacks:
In the above code, three undo steps are required to revert to the previous state.
So, what if we could modify the Selection virtually before applying to our TextControl instance? This is what the second approach below does.
A new virtual Selection object is created and manipulated using the same properties. After the object is finished, it will be assigned to the Selection property of our TextControl instance.
TXTextControl.Selection sel = new TXTextControl.Selection();
sel.Text = "New Text";
sel.Bold = true;
sel.Italic = true;
textControl1.Selection = sel;
This is counted as a single Undo step and there is no visible selection background.
Cleaning up documents might be necessary when building documents dynamically. Various document sections are added, forced page breaks are inserted at different positions and end-users can modify the resulting document. A typical task is to remove all empty pages from a document that consist only of a page break or section break character.
The following method uses the PageEnumerator to loop through all pages. If the Length of a page equals 1, the page is removed.
private void RemoveEmptyPages()
{
TXTextControl.PageCollection.PageEnumerator pageEnum =
textControl1.GetPages().GetEnumerator();
pageEnum.MoveNext();
int pageCounter = textControl1.GetPages().Count;
for (int i = 0; i < pageCounter; i++)
{
TXTextControl.Page curPage = (TXTextControl.Page)pageEnum.Current;
if (curPage.Length == 1)
{
textControl1.Select(curPage.Start - 1, 1);
textControl1.Selection.Text = "";
}
else
pageEnum.MoveNext();
}
}
Generally, inserting page columns is a very easy task. Using the SectionFormat.Columns property, you can define the number of columns. TX Text Control will then automatically adjust the width of the columns based on the page size and number of columns.
But if the columns should have a different size and should not be averaged and spread out on the page, we need to set the width of each column. This can be done in the SectionFormat constructor:
public SectionFormat(int columns, int[] columnWidths, int[] columnDistances);
The second column width is calculated based on the first width, the distance between the columns, the page size and margins. This formula shows the calculation:
SecondColumnWidth = PageSize - PageMargins - FirstColumnWidth - ColumnDistance
The following code shows how to insert a new section with two columns where you just need to pass the first column width:
private void InsertColumns(int FirstColumnWidth, int ColumnDistance)
{
textControl1.Sections.Add(TXTextControl.SectionBreakKind.BeginAtNewLine);
TXTextControl.SectionFormat currentFormat =
textControl1.Sections.GetItem().Format;
int iSecondColumnWidth = (int)(
(currentFormat.PageSize.Width * 14.4) -
(currentFormat.PageMargins.Left * 14.4) -
(currentFormat.PageMargins.Right * 14.4) -
FirstColumnWidth -
ColumnDistance
);
TXTextControl.SectionFormat newSectionFormat =
new TXTextControl.SectionFormat(
2,
new int[] { FirstColumnWidth, iSecondColumnWidth },
new int[] { ColumnDistance });
textControl1.Sections.GetItem().Format = newSectionFormat;
}
When using TX Text Control in read-only mode, it might be required to display the page number of the currently visible page. In order to get the page number at the current input position, you just need to get the appropriate property from the InputPosition object.
But when scrolling through the pages, the actual input position is not changed, so that this property can't be used to get the page number.
But thanks to the flexible and powerful class library, it is very easy to retrieve the page number of the currently visible page.
In order to get the page number, we use the LineCollection to get the line in the middle of the visible TextControl. TX Text Control automatically factors the current scroll location into the calculation, so that we just need to pass the half of the control size to the GetItem method. It returns the Line object in the middle of the visible document part. And the Line object provides the according page number among other information.
private int GetPageAtScrollPosition()
{
return textControl1.Lines.GetItem(
new Point(0, textControl1.Height / 2)).Page;
}
We just need to update the information using the VScroll event:
private void textControl1_VScroll(object sender, EventArgs e)
{
int iPageNumber = GetPageAtScrollPosition();
Console.WriteLine("Page " +
iPageNumber.ToString() +
" of " +
textControl1.Pages.ToString());
Console.WriteLine("Section " +
textControl1.GetPages()[iPageNumber].Section +
" of " +
textControl1.Sections.Count.ToString());
}
TX Text Control .NET for Windows Forms's Print method introduces many advantages over the Print method of the ActiveX control. It enables you to print into a PrintDocument of the System.Drawing.Printing namespace.
TX Text Control .NET for Windows Forms's Print method uses the following settings of this object:
If you would like to print one specific page of the document, not only FromPage and ToPage must be specified, but also the PrintRange property.
The following code sample prints page 2 of TX Text Control and sets the current page size.
int m_curPage = 2;
PrintDocument myPrintDoc = new PrintDocument();
myPrintDoc.DefaultPageSettings.PaperSize = new PaperSize("default",
textControl1.PageSize.Width,
textControl1.PageSize.Height);
myPrintDoc.DefaultPageSettings.Margins = new Margins(textControl1.PageMargins.Left,
textControl1.PageMargins.Right,
textControl1.PageMargins.Top,
textControl1.PageMargins.Bottom);
myPrintDoc.PrinterSettings.PrintRange = PrintRange.SomePages;
myPrintDoc.PrinterSettings.FromPage = m_curPage;
myPrintDoc.PrinterSettings.ToPage = m_curPage;
textControl1.Print(myPrintDoc);
As you can see, the PrintPage property must be specified with SomePages which indicates that the FromPage and ToPage is considered.