RichTextBoxEx.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. using System;
  2. using System.ComponentModel;
  3. using System.Drawing;
  4. using System.Windows.Forms;
  5. using System.Runtime.InteropServices;
  6. namespace RichTextBoxLinks
  7. {
  8. public class RichTextBoxEx : RichTextBox
  9. {
  10. #region Interop-Defines
  11. [ StructLayout( LayoutKind.Sequential )]
  12. private struct CHARFORMAT2_STRUCT
  13. {
  14. public UInt32 cbSize;
  15. public UInt32 dwMask;
  16. public UInt32 dwEffects;
  17. public Int32 yHeight;
  18. public Int32 yOffset;
  19. public Int32 crTextColor;
  20. public byte bCharSet;
  21. public byte bPitchAndFamily;
  22. [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
  23. public char[] szFaceName;
  24. public UInt16 wWeight;
  25. public UInt16 sSpacing;
  26. public int crBackColor; // Color.ToArgb() -> int
  27. public int lcid;
  28. public int dwReserved;
  29. public Int16 sStyle;
  30. public Int16 wKerning;
  31. public byte bUnderlineType;
  32. public byte bAnimation;
  33. public byte bRevAuthor;
  34. public byte bReserved1;
  35. }
  36. [DllImport("user32.dll", CharSet=CharSet.Auto)]
  37. private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
  38. private const int WM_USER = 0x0400;
  39. private const int EM_GETCHARFORMAT = WM_USER+58;
  40. private const int EM_SETCHARFORMAT = WM_USER+68;
  41. private const int SCF_SELECTION = 0x0001;
  42. private const int SCF_WORD = 0x0002;
  43. private const int SCF_ALL = 0x0004;
  44. #region CHARFORMAT2 Flags
  45. private const UInt32 CFE_BOLD = 0x0001;
  46. private const UInt32 CFE_ITALIC = 0x0002;
  47. private const UInt32 CFE_UNDERLINE = 0x0004;
  48. private const UInt32 CFE_STRIKEOUT = 0x0008;
  49. private const UInt32 CFE_PROTECTED = 0x0010;
  50. private const UInt32 CFE_LINK = 0x0020;
  51. private const UInt32 CFE_AUTOCOLOR = 0x40000000;
  52. private const UInt32 CFE_SUBSCRIPT = 0x00010000; /* Superscript and subscript are */
  53. private const UInt32 CFE_SUPERSCRIPT= 0x00020000; /* mutually exclusive */
  54. private const int CFM_SMALLCAPS = 0x0040; /* (*) */
  55. private const int CFM_ALLCAPS = 0x0080; /* Displayed by 3.0 */
  56. private const int CFM_HIDDEN = 0x0100; /* Hidden by 3.0 */
  57. private const int CFM_OUTLINE = 0x0200; /* (*) */
  58. private const int CFM_SHADOW = 0x0400; /* (*) */
  59. private const int CFM_EMBOSS = 0x0800; /* (*) */
  60. private const int CFM_IMPRINT = 0x1000; /* (*) */
  61. private const int CFM_DISABLED = 0x2000;
  62. private const int CFM_REVISED = 0x4000;
  63. private const int CFM_BACKCOLOR = 0x04000000;
  64. private const int CFM_LCID = 0x02000000;
  65. private const int CFM_UNDERLINETYPE = 0x00800000; /* Many displayed by 3.0 */
  66. private const int CFM_WEIGHT = 0x00400000;
  67. private const int CFM_SPACING = 0x00200000; /* Displayed by 3.0 */
  68. private const int CFM_KERNING = 0x00100000; /* (*) */
  69. private const int CFM_STYLE = 0x00080000; /* (*) */
  70. private const int CFM_ANIMATION = 0x00040000; /* (*) */
  71. private const int CFM_REVAUTHOR = 0x00008000;
  72. private const UInt32 CFM_BOLD = 0x00000001;
  73. private const UInt32 CFM_ITALIC = 0x00000002;
  74. private const UInt32 CFM_UNDERLINE = 0x00000004;
  75. private const UInt32 CFM_STRIKEOUT = 0x00000008;
  76. private const UInt32 CFM_PROTECTED = 0x00000010;
  77. private const UInt32 CFM_LINK = 0x00000020;
  78. private const UInt32 CFM_SIZE = 0x80000000;
  79. private const UInt32 CFM_COLOR = 0x40000000;
  80. private const UInt32 CFM_FACE = 0x20000000;
  81. private const UInt32 CFM_OFFSET = 0x10000000;
  82. private const UInt32 CFM_CHARSET = 0x08000000;
  83. private const UInt32 CFM_SUBSCRIPT = CFE_SUBSCRIPT | CFE_SUPERSCRIPT;
  84. private const UInt32 CFM_SUPERSCRIPT= CFM_SUBSCRIPT;
  85. private const byte CFU_UNDERLINENONE = 0x00000000;
  86. private const byte CFU_UNDERLINE = 0x00000001;
  87. private const byte CFU_UNDERLINEWORD = 0x00000002; /* (*) displayed as ordinary underline */
  88. private const byte CFU_UNDERLINEDOUBLE = 0x00000003; /* (*) displayed as ordinary underline */
  89. private const byte CFU_UNDERLINEDOTTED = 0x00000004;
  90. private const byte CFU_UNDERLINEDASH = 0x00000005;
  91. private const byte CFU_UNDERLINEDASHDOT = 0x00000006;
  92. private const byte CFU_UNDERLINEDASHDOTDOT = 0x00000007;
  93. private const byte CFU_UNDERLINEWAVE = 0x00000008;
  94. private const byte CFU_UNDERLINETHICK = 0x00000009;
  95. private const byte CFU_UNDERLINEHAIRLINE = 0x0000000A; /* (*) displayed as ordinary underline */
  96. #endregion
  97. #endregion
  98. public RichTextBoxEx()
  99. {
  100. // Otherwise, non-standard links get lost when user starts typing
  101. // next to a non-standard link
  102. this.DetectUrls = false;
  103. }
  104. [DefaultValue(false)]
  105. public new bool DetectUrls
  106. {
  107. get { return base.DetectUrls; }
  108. set { base.DetectUrls = value; }
  109. }
  110. /// <summary>
  111. /// Insert a given text as a link into the RichTextBox at the current insert position.
  112. /// </summary>
  113. /// <param name="text">Text to be inserted</param>
  114. public void InsertLink(string text)
  115. {
  116. InsertLink(text, this.SelectionStart);
  117. }
  118. /// <summary>
  119. /// Insert a given text at a given position as a link.
  120. /// </summary>
  121. /// <param name="text">Text to be inserted</param>
  122. /// <param name="position">Insert position</param>
  123. public void InsertLink(string text, int position)
  124. {
  125. if (position < 0 || position > this.Text.Length)
  126. throw new ArgumentOutOfRangeException("position");
  127. this.SelectionStart = position;
  128. this.SelectedText = text;
  129. this.Select(position, text.Length);
  130. this.SetSelectionLink(true);
  131. this.Select(position + text.Length, 0);
  132. }
  133. /// <summary>
  134. /// Insert a given text at at the current input position as a link.
  135. /// The link text is followed by a hash (#) and the given hyperlink text, both of
  136. /// them invisible.
  137. /// When clicked on, the whole link text and hyperlink string are given in the
  138. /// LinkClickedEventArgs.
  139. /// </summary>
  140. /// <param name="text">Text to be inserted</param>
  141. /// <param name="hyperlink">Invisible hyperlink string to be inserted</param>
  142. public void InsertLink(string text, string hyperlink)
  143. {
  144. InsertLink(text, hyperlink, this.SelectionStart);
  145. }
  146. /// <summary>
  147. /// Insert a given text at a given position as a link. The link text is followed by
  148. /// a hash (#) and the given hyperlink text, both of them invisible.
  149. /// When clicked on, the whole link text and hyperlink string are given in the
  150. /// LinkClickedEventArgs.
  151. /// </summary>
  152. /// <param name="text">Text to be inserted</param>
  153. /// <param name="hyperlink">Invisible hyperlink string to be inserted</param>
  154. /// <param name="position">Insert position</param>
  155. public void InsertLink(string text, string hyperlink, int position)
  156. {
  157. if (position < 0 || position > this.Text.Length)
  158. throw new ArgumentOutOfRangeException("position");
  159. this.SelectionStart = position;
  160. this.SelectedRtf = @"{\rtf1\ansi "+text+@"\v #"+hyperlink+@"\v0}";
  161. this.Select(position, text.Length + hyperlink.Length + 1);
  162. this.SetSelectionLink(true);
  163. this.Select(position + text.Length + hyperlink.Length + 1, 0);
  164. }
  165. /// <summary>
  166. /// Set the current selection's link style
  167. /// </summary>
  168. /// <param name="link">true: set link style, false: clear link style</param>
  169. public void SetSelectionLink(bool link)
  170. {
  171. SetSelectionStyle(CFM_LINK, link ? CFE_LINK : 0);
  172. }
  173. /// <summary>
  174. /// Get the link style for the current selection
  175. /// </summary>
  176. /// <returns>0: link style not set, 1: link style set, -1: mixed</returns>
  177. public int GetSelectionLink()
  178. {
  179. return GetSelectionStyle(CFM_LINK, CFE_LINK);
  180. }
  181. private void SetSelectionStyle(UInt32 mask, UInt32 effect)
  182. {
  183. CHARFORMAT2_STRUCT cf = new CHARFORMAT2_STRUCT();
  184. cf.cbSize = (UInt32)Marshal.SizeOf(cf);
  185. cf.dwMask = mask;
  186. cf.dwEffects = effect;
  187. IntPtr wpar = new IntPtr(SCF_SELECTION);
  188. IntPtr lpar = Marshal.AllocCoTaskMem( Marshal.SizeOf( cf ) );
  189. Marshal.StructureToPtr(cf, lpar, false);
  190. IntPtr res = SendMessage(Handle, EM_SETCHARFORMAT, wpar, lpar);
  191. Marshal.FreeCoTaskMem(lpar);
  192. }
  193. private int GetSelectionStyle(UInt32 mask, UInt32 effect)
  194. {
  195. CHARFORMAT2_STRUCT cf = new CHARFORMAT2_STRUCT();
  196. cf.cbSize = (UInt32)Marshal.SizeOf(cf);
  197. cf.szFaceName = new char[32];
  198. IntPtr wpar = new IntPtr(SCF_SELECTION);
  199. IntPtr lpar = Marshal.AllocCoTaskMem( Marshal.SizeOf( cf ) );
  200. Marshal.StructureToPtr(cf, lpar, false);
  201. IntPtr res = SendMessage(Handle, EM_GETCHARFORMAT, wpar, lpar);
  202. cf = (CHARFORMAT2_STRUCT)Marshal.PtrToStructure(lpar, typeof(CHARFORMAT2_STRUCT));
  203. int state;
  204. // dwMask holds the information which properties are consistent throughout the selection:
  205. if ((cf.dwMask & mask) == mask)
  206. {
  207. if ((cf.dwEffects & effect) == effect)
  208. state = 1;
  209. else
  210. state = 0;
  211. }
  212. else
  213. {
  214. state = -1;
  215. }
  216. Marshal.FreeCoTaskMem(lpar);
  217. return state;
  218. }
  219. }
  220. }