Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 3349
  • Last Modified:

RichTextBox displaying RTF with hyperlinks?

I want to read an RTF file, parse it to insert hyperlinks at certain points, and display it in a RichTextBox.

The RTF file is an activity journal.  In that journal, there are times and dates mentioned.  I want to turn those time/dates into links that open up windows showing data collected at those times.

I've got a RichTextBox displaying the RTF.  I can use Word to manually insert a link, and my LinkClicked event is being fired.  I just don't know how to parse the file and modify some of the text entries to contain links in the control.
0
rschaaf
Asked:
rschaaf
  • 7
  • 5
  • 3
1 Solution
 
Bob LearnedCommented:
What would be an example of the kind of text that you want to make a link out of?

Bob
0
 
rschaafAuthor Commented:
Here is a snip from an example RTF file that I want to display with hyperlinks.  In this example, I want to make DN308,200,14:30:24 (and the others) hyperlinked so that when clicked, I get a LinkClicked event.  In the event, I will parse the link as Day Number, year, and time, and then pull up the appropriate window.

DN308,2000,14:30:24," UPDATED system”
DN308/2000/14/33/16,"Reset topside"
DN308,2000,14:35:13,"FILE WILL NOT OPEN”
DN308,2000,14:41:11,”Zoomto SMF”
DN308,2000,14:47:56,”Flyto BOI”
DN308,2000,14:51:12,”Port side gain +3db”
DN308,2000,14:53:33,"Media change sn 8a9a383”
DN308,2000,18,42,35,”Shore power cycle – phase error noted”
DN308,2000,18:43:35,”UPS #2 alert – taken offline”
DN308,2000,18:47:11,”YELLOW BUOY”
0
 
Bob LearnedCommented:
You could use Regular Expression replacement.  C# or VB.NET?

Untested sample expression:
DN(?<day>\d{3}),(?<year>\d{4}),(?<time>(\d{2}):(\d{2}):(\d{2})),(?<text>.+)

Bob

0
Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

 
rschaafAuthor Commented:
My problem isn't parsing the date/time.

Given an RTF, I need to change the DN308,... phrases into hyperlinks so that they show up as normal-looking links (blue text) in the RichTextBox control.
0
 
Bob LearnedCommented:
You need to create an anchor tag.  Do you know how to do that?

Bob
0
 
rschaafAuthor Commented:
Yes, I know how to create an anchor tag.  <a href=...

My question remains: how do I change a phrase in an RTF into a hyperlink that displays correctly in a .NET RichTextBox control?
0
 
Bob LearnedCommented:
Actually, that only works with the WebBrowser control.  The RichTextBox doesn't process this tag.  This operation is going to require something a little more elaborate, since the RichTextBox only detects URLs as links, AFAIK.

Sorry, I had my head up my you know what yesterday.

Bob
0
 
Bob LearnedCommented:
Are you using C# or VB.NET?

Bob
0
 
rschaafAuthor Commented:
I'm using C#.  The RichTextBox control has a DetectUrls property which I set true.  If I use Word to create a link in my RTF file, I can see the the fully exposed URL in the control (the link should be blue, and the URL should be hidden, but it's not) and when I click the URL the control fires a LinkClicked event which I can see.

I just can't find a way to programatically change the RTF to include the links, and to get the links to display correctly.

When you suggest the WebBrowser control... where is that?  I see the COM component AxSHDocVw.AxWebBrowser, is that what you mean?
0
 
Bob LearnedCommented:
Try this:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace RichTextBoxLinks
{
  public class RichTextBoxEx : RichTextBox
  {
    #region Interop-Defines
    [ StructLayout( LayoutKind.Sequential )]
      private struct CHARFORMAT2_STRUCT
    {
      public UInt32      cbSize;
      public UInt32   dwMask;
      public UInt32   dwEffects;
      public Int32    yHeight;
      public Int32    yOffset;
      public Int32      crTextColor;
      public byte     bCharSet;
      public byte     bPitchAndFamily;
      [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
      public char[]   szFaceName;
      public UInt16      wWeight;
      public UInt16      sSpacing;
      public int            crBackColor; // Color.ToArgb() -> int
      public int            lcid;
      public int            dwReserved;
      public Int16      sStyle;
      public Int16      wKerning;
      public byte            bUnderlineType;
      public byte            bAnimation;
      public byte            bRevAuthor;
      public byte            bReserved1;
    }

    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

    private const int WM_USER                   = 0x0400;
    private const int EM_GETCHARFORMAT       = WM_USER+58;
    private const int EM_SETCHARFORMAT       = WM_USER+68;

    private const int SCF_SELECTION      = 0x0001;
    private const int SCF_WORD            = 0x0002;
    private const int SCF_ALL            = 0x0004;

    #region CHARFORMAT2 Flags
    private const UInt32 CFE_BOLD            = 0x0001;
    private const UInt32 CFE_ITALIC            = 0x0002;
    private const UInt32 CFE_UNDERLINE      = 0x0004;
    private const UInt32 CFE_STRIKEOUT      = 0x0008;
    private const UInt32 CFE_PROTECTED      = 0x0010;
    private const UInt32 CFE_LINK            = 0x0020;
    private const UInt32 CFE_AUTOCOLOR      = 0x40000000;
    private const UInt32 CFE_SUBSCRIPT      = 0x00010000;            /* Superscript and subscript are */
    private const UInt32 CFE_SUPERSCRIPT= 0x00020000;            /*  mutually exclusive                   */

    private const int CFM_SMALLCAPS            = 0x0040;                  /* (*)      */
    private const int CFM_ALLCAPS            = 0x0080;                  /* Displayed by 3.0      */
    private const int CFM_HIDDEN            = 0x0100;                  /* Hidden by 3.0 */
    private const int CFM_OUTLINE            = 0x0200;                  /* (*)      */
    private const int CFM_SHADOW            = 0x0400;                  /* (*)      */
    private const int CFM_EMBOSS            = 0x0800;                  /* (*)      */
    private const int CFM_IMPRINT            = 0x1000;                  /* (*)      */
    private const int CFM_DISABLED            = 0x2000;
    private const int CFM_REVISED            = 0x4000;

    private const int CFM_BACKCOLOR            = 0x04000000;
    private const int CFM_LCID                  = 0x02000000;
    private const int CFM_UNDERLINETYPE      = 0x00800000;            /* Many displayed by 3.0 */
    private const int CFM_WEIGHT            = 0x00400000;
    private const int CFM_SPACING            = 0x00200000;            /* Displayed by 3.0      */
    private const int CFM_KERNING            = 0x00100000;            /* (*)      */
    private const int CFM_STYLE                  = 0x00080000;            /* (*)      */
    private const int CFM_ANIMATION            = 0x00040000;            /* (*)      */
    private const int CFM_REVAUTHOR            = 0x00008000;


    private const UInt32 CFM_BOLD            = 0x00000001;
    private const UInt32 CFM_ITALIC            = 0x00000002;
    private const UInt32 CFM_UNDERLINE      = 0x00000004;
    private const UInt32 CFM_STRIKEOUT      = 0x00000008;
    private const UInt32 CFM_PROTECTED      = 0x00000010;
    private const UInt32 CFM_LINK            = 0x00000020;
    private const UInt32 CFM_SIZE            = 0x80000000;
    private const UInt32 CFM_COLOR            = 0x40000000;
    private const UInt32 CFM_FACE            = 0x20000000;
    private const UInt32 CFM_OFFSET            = 0x10000000;
    private const UInt32 CFM_CHARSET      = 0x08000000;
    private const UInt32 CFM_SUBSCRIPT      = CFE_SUBSCRIPT | CFE_SUPERSCRIPT;
    private const UInt32 CFM_SUPERSCRIPT= CFM_SUBSCRIPT;

    private const byte CFU_UNDERLINENONE            = 0x00000000;
    private const byte CFU_UNDERLINE                  = 0x00000001;
    private const byte CFU_UNDERLINEWORD            = 0x00000002; /* (*) displayed as ordinary underline      */
    private const byte CFU_UNDERLINEDOUBLE            = 0x00000003; /* (*) displayed as ordinary underline      */
    private const byte CFU_UNDERLINEDOTTED            = 0x00000004;
    private const byte CFU_UNDERLINEDASH            = 0x00000005;
    private const byte CFU_UNDERLINEDASHDOT            = 0x00000006;
    private const byte CFU_UNDERLINEDASHDOTDOT      = 0x00000007;
    private const byte CFU_UNDERLINEWAVE            = 0x00000008;
    private const byte CFU_UNDERLINETHICK            = 0x00000009;
    private const byte CFU_UNDERLINEHAIRLINE      = 0x0000000A; /* (*) displayed as ordinary underline      */

    #endregion

    #endregion

    public RichTextBoxEx()
    {
      // Otherwise, non-standard links get lost when user starts typing
      // next to a non-standard link
      this.DetectUrls = false;
    }

    [DefaultValue(false)]
    public new bool DetectUrls
    {
      get { return base.DetectUrls; }
      set { base.DetectUrls = value; }
    }

    /// <summary>
    /// Insert a given text as a link into the RichTextBox at the current insert position.
    /// </summary>
    /// <param name="text">Text to be inserted</param>
    public void InsertLink(string text)
    {
      InsertLink(text, this.SelectionStart);
    }

    /// <summary>
    /// Insert a given text at a given position as a link.
    /// </summary>
    /// <param name="text">Text to be inserted</param>
    /// <param name="position">Insert position</param>
    public void InsertLink(string text, int position)
    {
      if (position < 0 || position > this.Text.Length)
        throw new ArgumentOutOfRangeException("position");

      this.SelectionStart = position;
      this.SelectedText = text;
      this.Select(position, text.Length);
      this.SetSelectionLink(true);
      this.Select(position + text.Length, 0);
    }
            
    /// <summary>
    /// Insert a given text at at the current input position as a link.
    /// The link text is followed by a hash (#) and the given hyperlink text, both of
    /// them invisible.
    /// When clicked on, the whole link text and hyperlink string are given in the
    /// LinkClickedEventArgs.
    /// </summary>
    /// <param name="text">Text to be inserted</param>
    /// <param name="hyperlink">Invisible hyperlink string to be inserted</param>
    public void InsertLink(string text, string hyperlink)
    {
      InsertLink(text, hyperlink, this.SelectionStart);
    }

    /// <summary>
    /// Insert a given text at a given position as a link. The link text is followed by
    /// a hash (#) and the given hyperlink text, both of them invisible.
    /// When clicked on, the whole link text and hyperlink string are given in the
    /// LinkClickedEventArgs.
    /// </summary>
    /// <param name="text">Text to be inserted</param>
    /// <param name="hyperlink">Invisible hyperlink string to be inserted</param>
    /// <param name="position">Insert position</param>
    public void InsertLink(string text, string hyperlink, int position)
    {
      if (position < 0 || position > this.Text.Length)
        throw new ArgumentOutOfRangeException("position");

      this.SelectionStart = position;
      this.SelectedRtf = @"{\rtf1\ansi "+text+@"\v #"+hyperlink+@"\v0}";
      this.Select(position, text.Length + hyperlink.Length + 1);
      this.SetSelectionLink(true);
      this.Select(position + text.Length + hyperlink.Length + 1, 0);
    }

    /// <summary>
    /// Set the current selection's link style
    /// </summary>
    /// <param name="link">true: set link style, false: clear link style</param>
    public void SetSelectionLink(bool link)
    {
      SetSelectionStyle(CFM_LINK, link ? CFE_LINK : 0);
    }
    /// <summary>
    /// Get the link style for the current selection
    /// </summary>
    /// <returns>0: link style not set, 1: link style set, -1: mixed</returns>
    public int GetSelectionLink()
    {
      return GetSelectionStyle(CFM_LINK, CFE_LINK);
    }


    private void SetSelectionStyle(UInt32 mask, UInt32 effect)
    {
      CHARFORMAT2_STRUCT cf = new CHARFORMAT2_STRUCT();
      cf.cbSize = (UInt32)Marshal.SizeOf(cf);
      cf.dwMask = mask;
      cf.dwEffects = effect;

      IntPtr wpar = new IntPtr(SCF_SELECTION);
      IntPtr lpar = Marshal.AllocCoTaskMem( Marshal.SizeOf( cf ) );
      Marshal.StructureToPtr(cf, lpar, false);

      IntPtr res = SendMessage(Handle, EM_SETCHARFORMAT, wpar, lpar);

      Marshal.FreeCoTaskMem(lpar);
    }

    private int GetSelectionStyle(UInt32 mask, UInt32 effect)
    {
      CHARFORMAT2_STRUCT cf = new CHARFORMAT2_STRUCT();
      cf.cbSize = (UInt32)Marshal.SizeOf(cf);
      cf.szFaceName = new char[32];

      IntPtr wpar = new IntPtr(SCF_SELECTION);
      IntPtr lpar =       Marshal.AllocCoTaskMem( Marshal.SizeOf( cf ) );
      Marshal.StructureToPtr(cf, lpar, false);

      IntPtr res = SendMessage(Handle, EM_GETCHARFORMAT, wpar, lpar);

      cf = (CHARFORMAT2_STRUCT)Marshal.PtrToStructure(lpar, typeof(CHARFORMAT2_STRUCT));

      int state;
      // dwMask holds the information which properties are consistent throughout the selection:
      if ((cf.dwMask & mask) == mask)
      {
        if ((cf.dwEffects & effect) == effect)
          state = 1;
        else
          state = 0;
      }
      else
      {
        state = -1;
      }
                  
      Marshal.FreeCoTaskMem(lpar);
      return state;
    }
  }
}


Bob
0
 
Bob LearnedCommented:
Code taken from here:

Links with arbitrary text in a RichTextBox:
http://www.codeproject.com/cs/miscctrl/RichTextBoxLinks.asp

Bob
0
 
rschaafAuthor Commented:
I looked and looked but never came across that link over at CodeProject.  I must be loosing my mind...

This is exactly what I need.  Thanks for your help, Bob.
0
 
Minh Võ CôngCommented:
I wrote

public void SetCurrentFontName(String strFont)
{
CHARFORMAT2 cf = new CHARFORMAT2();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_FACE;
char[] strFontName = new char[strFont.Length + 1];
strFontName = strFont.ToCharArray();
cf.szFaceName = strFontName;
// Marshal.Copy(Marshal.StringToBSTR(strFont),cf.szFaceName,0, strFont.Length*2);

SendMessage(new HandleRef(this, Handle), EM_SETCHARFORMAT, SCF_SELECTION, ref cf);
this.Focus();
}
in ExtendedRichTextBox control
but when change Font name the error occrupt
{"Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout."}
How to fix it?
public void SetCurrentFontName(String strFont)
{
CHARFORMAT2 cf = new CHARFORMAT2();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_FACE;
char[] strFontName = new char[strFont.Length + 1];
strFontName = strFont.ToCharArray();
cf.szFaceName = strFontName;
// Marshal.Copy(Marshal.StringToBSTR(strFont),cf.szFaceName,0, strFont.Length*2);
 
SendMessage(new HandleRef(this, Handle), EM_SETCHARFORMAT, SCF_SELECTION, ref cf);
this.Focus();
}

Open in new window

0
 
Minh Võ CôngCommented:
I wrote the follow code
but when change Font name the error occrupt
{"Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout."}

How to fix it?
public void SetCurrentFontName(String strFont)
{
CHARFORMAT2 cf = new CHARFORMAT2();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_FACE;
char[] strFontName = new char[strFont.Length + 1];
strFontName = strFont.ToCharArray();
char[] strFontName = new char[32];
cf.szFaceName = strFontName;
// Marshal.Copy(Marshal.StringToBSTR(strFont),cf.szFaceName,0, //strFont.Length*2);
 
SendMessage(new HandleRef(this, Handle), EM_SETCHARFORMAT, SCF_SELECTION, ref cf);
this.Focus();
}

Open in new window

0
 
Minh Võ CôngCommented:
I fix this problem with code
cf.dwMask = CFM_FACE;
char[] strFontName = new char[32];
strFont.CopyTo(cf.szFaceName,0,strFont.Length);

Open in new window

0

Featured Post

Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

  • 7
  • 5
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now