This article explains how to implement Bullets & Numbering and Indenting in Silverlight 4 RichTextBox control. By default, the RichTextBox control of Silverlight 4 does not support Bullets & Numbering and Indenting. We have to implement them programmatically by finding selected paragraphs (paragraphs selected by user) and inserting corresponding characters like bullets, numbers and tabs.
Bullets & Numbering Demo in Silverlight
The following demo illustrates a basic Silverlight text/message editor with Bullets & Numbering and Indenting features.
Steps to add Bullets & Numbering in Silverlight RichTextBox
This involves following steps:
- Find all Paragraph elements in selected text.
- Insert bullet/number/tab at start of each Paragraph found.
Finding Offset of a position
In order to insert any content in a specified location inside RichTextBox, we have to find the offset of that location. A location/position corresponds to a TextPointer object. Offset of a given-position means number of characters (more precisely number of insertion-points) between first position of RichTextBox and given-position.
The following function finds the offset of a given position.
public static int FindOffsetOfPosition(RichTextBox rtb, TextPointer position)
{
int offset = 0;
while (true)
{
TextPointer currentPosition = rtb.ContentStart.GetPositionAtOffset(offset, LogicalDirection.Forward);
if (currentPosition == null) break;
if (currentPosition.CompareTo(position) == 0) break;
offset += 1;
}
return (offset);
}
Finding Selected Paragraphs
In order to find Paragraph elements in selected text, first we have to find offsets of all Paragraph elements inside RichTextBox. This will be used as a lookup-table while finding selected-paragraphs. The following function find offsets of all Paragraph elements inside RichTextBox.
public static Dictionary<Paragraph, int> GetAllParagraphOffsets(RichTextBox rtb)
{
Dictionary<Paragraph, int> allParagraphs = new Dictionary<Paragraph, int>();
foreach (Block block in rtb.Blocks)
{
if (block is Paragraph)
{
Paragraph paragraph = (Paragraph)block;
int offset = FindOffsetOfPosition(rtb, paragraph.ContentStart);
allParagraphs.Add(paragraph, offset);
}
}
return (allParagraphs);
}
Then, we find the selected-paragraphs by comparing the offset of each character inside selected-text with the lookup-table. The following function find all Paragraph elements inside selected-text.
private static List<Paragraph> GetSelectedParagraphs(RichTextBox rtb)
{
Dictionary<Paragraph, int> allParagraphs = GetAllParagraphOffsets(rtb);
List<Paragraph> selectedParagraphs = new List<Paragraph>();
TextPointer currentPosition = rtb.Selection.Start;
TextPointer endPosition = rtb.Selection.End;
while (true)
{
int currentOffset = FindOffsetOfPosition(rtb, currentPosition);
Paragraph currentParagraph = FindParagraphAtOffset(allParagraphs, currentOffset);
if (currentParagraph != null && !selectedParagraphs.Contains(currentParagraph)) selectedParagraphs.Add(currentParagraph);
currentPosition = currentPosition.GetNextInsertionPosition(LogicalDirection.Forward);
if (currentPosition == null) break;
if (currentPosition.CompareTo(endPosition) > 0) break;
}
return (selectedParagraphs);
}
The FindParagraphAtOffset() function searches in the lookup-table and return the corresponding Paragraph element.
Applying Bullets, Numbering and Indenting
After finding all Paragraph elements in selected-text, we insert the corresponding character (bullet or number or tab) at the start of each Paragraph element. The following function inserts bullet character in selected-paragraphs.
public static void ApplyBullets(RichTextBox rtb, string bulletChar)
{
List<Paragraph> selectedParagraphs = GetSelectedParagraphs(rtb);
//insert bullet character in selected-paragraphs
foreach (Paragraph paragraph in selectedParagraphs)
{
int paragraphStartOffset = FindOffsetOfPosition(rtb, paragraph.ContentStart);
InsertAtOffset(rtb, new Run() { Text = bulletChar + " " }, paragraphStartOffset);
}
}
The InsertAtOffset() function is used to insert an Inline at a given offset.
Applying bullets/numbering/indenting will be very slow if SelectionChanged event is not disabled; because each insert and delete operation in a RichTextBox will trigger the SelectionChanged event.
For Numbering, we insert seqential numbers before each Paragraph element. To increase indent, we insert a tab (\t) character before each Paragraph element. To decrease indent, we check the first character of each Paragraph element and remove it if it is a tab (\t) character.
When applying bullets/numbering, we have to check whether bullets/numbering is already applied to the selected paragraphs and remove the corresponding text (bullet character or numbers) before inserting the actual text (bullet character or numbers) at the start of each paragraph.
Downloads for this article
File |
Language |
Tools |
Silverlight Bullets & Numbering Source 150.49 kb (420 downloads) |
C#, Silverlight 4 |
Visual Studio 2010 |