Richer RichTextBox (Part 1)






3.59/5 (7 votes)
Oct 24, 2005
2 min read

101142

1757
An article on extending the RichTextBox class in C#.
Introduction
If you ever tried to write a word processor in C#, you probably encountered some missing features in the RichTextBox
class. One of them is lack of methods that can change the FontStyle
of a selection without losing the styles that are currently present. There is workaround for this problem described in the WinForms FAQ.
"If you visit the selection a character at a time, you can get the current FontStyle
and modify it directly to add or remove a style like Bold or Italic. Doing it a character at a time will avoid losing the other styles that are set. The problem with doing it a whole selection at a time is that the FontStyle
of the entire selection is the common styles set among all the characters in the selection. So, if one char is bold and one is not, then bold is not set when you retrieve the font style for the entire selection. Doing things a character at a time avoids this problem and allows things to work OK."
Although working, this solution is not without flaws. Visiting one character at a time causes flickering and it is inefficient with large selections. Fortunately, the .NET RichTextBox
control is based on the Win32 RichEdit control that has wanted functionality. To reach it, we will extend the RichTextBox
and add the necessary wrappers that send the messages to the underlying RichEdit control.
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Nik.UserControls
{
public class RicherTextBox1 : System.Windows.Forms.RichTextBox
{
private const int EM_SETCHARFORMAT = 1092;
private const int CFM_BOLD = 1;
private const int CFM_ITALIC = 2;
private const int CFM_UNDERLINE = 4;
private const int SCF_SELECTION = 1;
[StructLayout( LayoutKind.Sequential )]
private struct CHARFORMAT
{
public int cbSize;
public uint dwMask;
public uint dwEffects;
public int yHeight;
public int yOffset;
public int crTextColor;
public byte bCharSet;
public byte bPitchAndFamily;
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 32 )]
public char[] szFaceName;
// CHARFORMAT2 from here onwards.
public short wWeight;
public short sSpacing;
public int crBackColor;
public int LCID;
public uint dwReserved;
public short sStyle;
public short wKerning;
public byte bUnderlineType;
public byte bAnimation;
public byte bRevAuthor;
}
[DllImport( "user32", CharSet = CharSet.Auto )]
private static extern int SendMessage( HandleRef hWnd,
int msg, int wParam, ref CHARFORMAT lp );
private void SetCharFormatMessage( ref CHARFORMAT fmt )
{
SendMessage(new HandleRef(this, Handle),
EM_SETCHARFORMAT, SCF_SELECTION, ref fmt);
}
public void SetSelectionBoldOn()
{
ApplyStyle( CFM_BOLD, true );
}
public void SetSelectionBoldOff()
{
ApplyStyle( CFM_BOLD, false );
}
public void SetSelectionItalicOn()
{
ApplyStyle( CFM_ITALIC, true );
}
public void SetSelectionItalicOff()
{
ApplyStyle( CFM_ITALIC, false );
}
public void SetSelectionUnderlineOn()
{
ApplyStyle( CFM_UNDERLINE, true );
}
public void SetSelectionUnderlineOff()
{
ApplyStyle( CFM_UNDERLINE, false );
}
private void ApplyStyle( uint style, bool on )
{
CHARFORMAT fmt = new CHARFORMAT();
fmt.cbSize = Marshal.SizeOf( fmt );
fmt.dwMask = style;
if ( on )
fmt.dwEffects = style;
SetCharFormatMessage( ref fmt );
}
}
}
Usage
Our new control retains all of the functionality of the standard RichTextbox
control but also incorporates custom functionality. We can now use our control instead of the RichTextBox
and invoke new methods like any other method in the RichTextBox
control (see demo 2).
private void boldOn_Click(object sender, System.EventArgs e)
{
rtb.SetSelectionBoldOn();
}
private void italicOn_Click(object sender, System.EventArgs e)
{
rtb.SetSelectionItalicOn();
}
private void underlineOn_Click(object sender, System.EventArgs e)
{
rtb.SetSelectionUnderlineOn();
}
private void boldOff_Click(object sender, System.EventArgs e)
{
rtb.SetSelectionBoldOff();
}
private void italicOff_Click(object sender, System.EventArgs e)
{
rtb.SetSelectionItalicOff();
}
private void underlineOff_Click(object sender, System.EventArgs e)
{
rtb.SetSelectionUnderlineOff();
}