Showing posts with label RichTextBox. Show all posts
Showing posts with label RichTextBox. Show all posts

Thursday, June 7, 2012

How to programmatically add hyperlinks to a Silverlight RichTextBox

Sir Edmund Percival Hillary

There are many examples demonstrating how to embed hyperlinks into a Silverlight RichTextBox, but they all tend to be for design-time use.  This post discusses a technique for adding hyperlinks dynamically at runtime.

To begin, the following RichTextBox contains text from a Wikipedia article about Sir Edmund Hillary.

<UserControl

    x:Class="SilverlightApplication12.MainPage"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    mc:Ignorable="d"

    d:DesignHeight="300"

    d:DesignWidth="400"

    >

    <Grid>

        <RichTextBox

            x:Name="rtb"

            Margin="10,10,10,10"

            IsReadOnly="True"

            FontFamily="Georgia"

            FontSize="16"

            TextAlignment="Justify"

            BorderThickness="0"

            >

            <Paragraph>

                <Run

                    FontStyle="Italic"

                    Text="&quot;Sir Edmund Percival Hillary, KG, ONZ, KBE (20 July 1919 – 11 January 2008) was a New Zealand mountaineer, explorer and philanthropist. On 29 May 1953, Hillary and Nepalese Sherpa mountaineer Tenzing Norgay became the first climbers confirmed as having reached the summit of Mount Everest – see Timeline of climbing Mount Everest. They were part of the ninth British expedition to Everest, led by John Hunt. He was named by Time magazine as one of the 100 most influential people of the 20th century.&quot;"

                    />

                <LineBreak />

                <LineBreak />

                <Run Text="from Edmund Hillary/Wikipedia"/>

                <LineBreak />

            </Paragraph>

        </RichTextBox>

    </Grid>

</UserControl>

Below, the developer is calling a custom extension method called “hyperlink” to add links to other Wikipedia articles. The key capability here is that hyperlinks are being added programmatically at runtime.

public partial class MainPage : UserControl {

    public MainPage() {

        InitializeComponent();

 

        this.rtb.Hyperlink("KG",
            "http://en.wikipedia.org/wiki/Order_of_the_Garter");

        this.rtb.Hyperlink("ONZ",
            "http://en.wikipedia.org/wiki/Order_of_New_Zealand");

        this.rtb.Hyperlink("KBE",
            "http://en.wikipedia.org/wiki/Order_of_the_British_Empire");

        this.rtb.Hyperlink("mountaineer",
            "http://en.wikipedia.org/wiki/Mountaineering");

        this.rtb.Hyperlink("Nepalese", "http://en.wikipedia.org/wiki/Nepal");

        this.rtb.Hyperlink("Sherpa", "http://en.wikipedia.org/wiki/Sherpa_people");

        this.rtb.Hyperlink("Tenzing Norgay",
            "http://en.wikipedia.org/wiki/Tenzing_Norgay");

        this.rtb.Hyperlink("Mount Everest",
            "http://en.wikipedia.org/wiki/Mount_Everest");

        this.rtb.Hyperlink("Timeline of climbing Mount Everest",
            "http://en.wikipedia.org/wiki/Timeline_of_climbing_Mount_Everest");

        this.rtb.Hyperlink("John Hunt",
            "http://en.wikipedia.org/wiki/John_Hunt,_Baron_Hunt");

        this.rtb.Hyperlink("100 most influential",
"http://en.wikipedia.org/wiki/Time_100:_The_Most_Important_People_of_the_Century");

        this.rtb.Hyperlink("Edmund Hillary",
           
"http://en.wikipedia.org/wiki/Sir_Edmund_Hillary");

        this.rtb.Hyperlink("Wikipedia", "http://en.wikipedia.org");

    }

}

 

Below is the code that defines the "hyperlink” extension method used above.  After considerable research, I discovered that the best way to insert a hyperlink at runtime is to programmatically select text and then insert a hyperlink with the insert method.  The complication was, how to get a TextPointer  for the linked text?  Based on logic in last year’s article, I iterated through every Paragraph and Run until the searched text was found.  When the host Run was located, a TextPointer were extracted using the ContentStart and ContentEnd properties.

 

public static class Extensions {

    public static void Hyperlink(this RichTextBox rtb, string text, string link) {

        // Argument Checking

        if (string.IsNullOrWhiteSpace(text)) {

            throw new ArgumentNullException("text");

        }

        if (string.IsNullOrWhiteSpace(link)) {

            throw new ArgumentNullException("link");

        }

        if (!Uri.IsWellFormedUriString(link, UriKind.RelativeOrAbsolute)) {

            throw new ArgumentException("link is not a valid url");

        }

 

        foreach (Block block in rtb.Blocks) {

            Paragraph paragraph = block as Paragraph;

            if (paragraph == null){continue;}

            foreach (Inline inline in paragraph.Inlines) {

                Run run = inline as Run;

                if (run == null) { continue; }

                int index = run.Text.IndexOf(text);

                if (index == -1) { continue; }

 

                TextPointer start = run.ContentStart.GetPositionAtOffset(
                    index,
LogicalDirection.Forward);

                TextPointer end = run.ContentStart.GetPositionAtOffset(
                    index + text.Length,
LogicalDirection.Forward);

                rtb.Selection.Select(start, end);

 

                Hyperlink hyperlink = new Hyperlink() {

                    NavigateUri = new Uri(link),

                    TargetName = "_blank"

                };

                hyperlink.Inlines.Add(text);

                rtb.Selection.Insert(hyperlink);

                return;

            }

        }

    }

}

Silverlight’s RichTextBox is a very powerful control for rendering text with complicated formatting. This code makes it a little more powerful by adding the ability to dynamically add hyperlinks.

Tuesday, July 26, 2011

Adding a WPF RichTextBox hyperlink at specific character positions?


Adding hyperlinks to a WPF RichTextBox can be achieved at design time in XAML or programmatically at run time.
Hyperlinks are created programmatically via the hyperlink constructor by parsing either the start and end TextPointers or InLine objects. These constructors are not particularly developer friendly unless the developer is programmatically populating a RichTextBox’s FlowDocument from Runs (see here).
In a recent project I need to add a hyperlink for text between predefined character index. For example, I needed a hyperlink for the text located between character positions 100 and 110.  The following extension method enables this.
RichTextBox extension:
namespace ESRI.PrototypeLab.Sample {
    public static class Extensions {
        public static Hyperlink ToHyperlink(this RichTextBox rtb, int start, int end) {
            TextPointer p1 = rtb.ToTextPointer(start);
            TextPointer p2 = rtb.ToTextPointer(end);
            if (p1 == null || p2 == null) { return null; }
            return new Hyperlink(p1, p2);
        }
        public static TextPointer ToTextPointer(this RichTextBox rtb, int index) {
            int count = 0;
            TextPointer position = rtb.Document.ContentStart;
            while (position != null) {
                if (position.GetPointerContext(LogicalDirection.Forward) ==
                    TextPointerContext.Text) {
                    string textRun = position.GetTextInRun(LogicalDirection.Forward);
                    int length = textRun.Length;
                    if (count + length > index) {
                        return position.GetPositionAtOffset(index - count);
                    }
                    count += length;
                }
                position = position.GetNextContextPosition(LogicalDirection.Forward);
            }
            return null;
        }
    }
}
How to use:
Hyperlink hyperlink = this.RichTextBox.ToHyperlink(100, 110);
hyperlink.Click += (s, e) => {
    MessageBox.Show("Hyperlink Clicked");};