using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace
DP.RichTextEx
{
public class RichTextBoxEx : RichTextBox
{
#region Interop-Defines
[StructLayout(LayoutKind.Sequential)]
struct CharFormat2
{
public uint cbSize;
/// <summary>
/// dwMask holds the information which properties are consistent
/// throughout the selection
/// </summary>
public CharFormat2Mask dwMask;
public CharFormat2Effect dwEffects;
public int yHeight;
public int yOffset;
public int crTextColor;
public byte bCharSet;
public byte bPitchAndFamily;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
public char[] szFaceName;
public ushort wWeight;
public ushort sSpacing;
/// <summary>
/// Color.ToArgb() -> int
/// </summary>
public int crBackColor;
public int lcid;
public int dwReserved;
public short sStyle;
public short wKerning;
public byte bUnderlineType;
public byte bAnimation;
public byte bRevAuthor;
public byte bReserved1;
}
[DllImport("user32.dll", CharSet=CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd,
Message msg,
IntPtr wParam,
IntPtr lParam);
enum Message :int
{
User = 0x0400,
/// <summary>
/// determines the current character formatting in a rich edit control.
/// </summary>
GetCharFormat = User + 58,
/// <summary>
/// sets character formatting in a rich edit control.
/// </summary>
SetCharFormat = User + 68
}
/// <summary>
/// Applies the formatting to the current selection. If the selection is empty,
/// the character formatting is applied to the insertion point,
/// and the new character format is in effect only until the insertion point changes.
/// </summary>
const int SCF_SELECTION = 0x0001;
#region CHARFORMAT2 Flags
enum CharFormat2Effect :uint
{
/// <summary>
/// A rich edit control can send EN_LINK notification messages when it receives mouse messages
/// while the mouse pointer is over text with the <see cref="CharFormat2Effect.Link">CFE_LINK</see> effect.
/// </summary>
Link = 0x0020,
None = 0
}
enum CharFormat2Mask :uint
{
/// <summary>
/// Indicates the <see cref="CharFormat2Effect.Link">CFE_LINK</see> value is valid.
/// </summary>
Link = 0x00000020
}
#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)]
new public
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(CharFormat2Mask.Link, link ? CharFormat2Effect.Link : CharFormat2Effect.None);
}
/// <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(CharFormat2Mask.Link, CharFormat2Effect.Link);
}
void SetSelectionStyle(CharFormat2Mask mask, CharFormat2Effect effect)
{
CharFormat2 cf = new CharFormat2();
cf.cbSize = (uint) 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);
SendMessage(
Handle, Message.SetCharFormat, wpar, lpar);
Marshal.FreeCoTaskMem(lpar);
}
int GetSelectionStyle(CharFormat2Mask mask, CharFormat2Effect effect)
{
CharFormat2 cf = new CharFormat2();
cf.cbSize = (uint) 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);
SendMessage(
Handle, Message.GetCharFormat, wpar, lpar);
cf = (CharFormat2) Marshal.PtrToStructure(lpar, typeof (CharFormat2));
int state;
if ((cf.dwMask & mask) == mask)
{
if ((cf.dwEffects & effect) == effect)
{
state = 1;
} else
{
state = 0;
}
} else
{
state = -1;
}
Marshal.FreeCoTaskMem(lpar);
return state;
}
}
}