Extending RichTextBox
RichTextBox inside .net winforms is a useful component, however it does not allow you to control and change some aspects of it. One thing it fails to allow you to do is get access to the scroll bars and change their values, it also doesn't let you control or set the underline style for the various items. All of these are actually possible, just not able to be done.
In .net 1.1 (although this should be fixed in .net 2.0 beta 2) the VScroll event is not fired when someone clicks and drags the thumbnail thingy on the scroll bar up and down, only when you click on the up and down arrows. This is extremely annoying if you are wanting to do things when the RichTextBox scrolls. I have included here my cobbled together RichTextBox which does a bunch of these things. I have taken some of this code from other blog sites, and some of it I wrote myself.
So this RichTextBox implements these extensions:
- Printing
- Appending RTF directly
- Scroll bar messages when the thumb is moved
- Underline colour and style changes
- Accessing the scroll bar information
- BeginUpdate/EndUpdate calls (I believe these exist in 2.0 now)
- Code to run an application based on a clicked link in the RichTextBox
Explanation of how all the bits actually work
Over the next few days I will post explanations of how each bit works. To start with I will talk about how to catch the events so you can see when the Thumb position moves on the scroll bars.
Scroll events for the thumb
To get the scroll events for moving the thumb in the scroll bar, we override the WndProc function on the RichTextBox, this is the main procedure that is used to process all of the incoming windows messages. The two messages of WM_VSCROLL and WM_HSCROLL are sent when the thumb is moved, we check the bottom 16 bits of the WParam to see if it is set to SB_THUMTRACK or SB_THUMBPOSITION, if it is, then we fire the event. It is very important to call base.WndProc() in this procedure, or your control will not respond to anything at all any more.
This should work in .net framework v2.0 beta2 and onwards correctly, or at least it should according to the bug I filed. So you will not need to do this in newer versions of the framework, however this is a useful demonstration of how to override the things inside RichTextBox.
To find the details of the messages and how to use them, msdn is quite useful. The links to details of the WM_HSCROLL and WM_VSCROLL event is available on msdn.
To find the actual numbers to use for these events inside your c# code, you need to search using google or look inside your your visual studio code to find the includes for windows. Inside this header file you can find the #define that is used for the WM_VSCROLL and all the other associated things, like SB_ENDSCROLL. You then define all of these as constants inside c# and use them when referencing the specific messages.
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_VSCROLL:
base.WndProc(ref m);
if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBTRACK)
{
OnVScroll(EventArgs.Empty);
}
if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBPOSITION)
{
OnVScroll(EventArgs.Empty);
}
break;
case WM_HSCROLL:
base.WndProc(ref m);
if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBTRACK)
{
OnHScroll(EventArgs.Empty);
}
if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBPOSITION)
{
OnHScroll(EventArgs.Empty);
}
break;
default:
base.WndProc(ref m);
break;
}
}
Here is the actual code for the ExtendedRichTextBox
#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using WhelkMud.Interfaces;
#endregion
namespace WhelkMud.WindowOutput
{
#region Control enums
public enum UnderlineStyle
{
None = 0,
Normal = 1,
Word = 2,
Double = 3,
Dotted = 4,
Dash = 5,
DashDot = 6,
DashDotDot = 7,
Wave = 8,
Thick = 9,
HairLine = 10,
DoubleWave = 11,
HeavyWave = 12,
LongDash = 13
}
public enum UnderlineColor
{
Black = 0x00,
None = 0x00,
Blue = 0x10,
Cyan = 0x20,
LimeGreen = 0x30,
Magenta = 0x40,
Red = 0x50,
Yellow = 0x60,
White = 0x70,
DarkBlue = 0x80,
DarkCyan = 0x90,
Green = 0xA0,
DarkMagenta = 0xB0,
Brown = 0xC0,
OliveGreen = 0xD0,
DarkGray = 0xE0,
Gray = 0xF0
}
public enum RtfColor
{
Black, Maroon, Green, Olive, Navy, Purple, Teal, Gray, Silver,
Red, Lime, Yellow, Blue, Fuchsia, Aqua, White
}
#endregion
public class ExtendedRichTextBox : RichTextBox
{
#region Private fields and constructors
private int _Updating = 0;
private int _OldEventMask = 0;
private ToolTip myToolTip;
public ExtendedRichTextBox()
{
this.myToolTip = new ToolTip();
this.ContextMenu = new ContextMenu();
this.LinkClicked += new LinkClickedEventHandler(MudRichTextBox_LinkClicked);
textColor = RtfColor.Black;
highlightColor = RtfColor.White;
rtfColor = new Dictionary<RtfColor, string>();
rtfColor[RtfColor.Aqua] = RtfColorDef.Aqua;
rtfColor[RtfColor.Black] = RtfColorDef.Black;
rtfColor[RtfColor.Blue] = RtfColorDef.Blue;
rtfColor[RtfColor.Fuchsia] = RtfColorDef.Fuchsia;
rtfColor[RtfColor.Gray] = RtfColorDef.Gray;
rtfColor[RtfColor.Green] = RtfColorDef.Green;
rtfColor[RtfColor.Lime] = RtfColorDef.Lime;
rtfColor[RtfColor.Maroon] = RtfColorDef.Maroon;
rtfColor[RtfColor.Navy] = RtfColorDef.Navy;
rtfColor[RtfColor.Olive] = RtfColorDef.Olive;
rtfColor[RtfColor.Purple] = RtfColorDef.Purple;
rtfColor[RtfColor.Red] = RtfColorDef.Red;
rtfColor[RtfColor.Silver] = RtfColorDef.Silver;
rtfColor[RtfColor.Teal] = RtfColorDef.Teal;
rtfColor[RtfColor.White] = RtfColorDef.White;
rtfColor[RtfColor.Yellow] = RtfColorDef.Yellow;
rtfFontFamily = new Dictionary<string, string>();
rtfFontFamily[FontFamily.GenericMonospace.Name] = RtfFontFamilyDef.Modern;
rtfFontFamily[FontFamily.GenericSansSerif.Name] = RtfFontFamilyDef.Swiss;
rtfFontFamily[FontFamily.GenericSerif.Name] = RtfFontFamilyDef.Roman;
rtfFontFamily[FF_UNKNOWN] = RtfFontFamilyDef.Unknown;
using (Graphics _graphics = this.CreateGraphics())
{
xDpi = _graphics.DpiX;
yDpi = _graphics.DpiY;
}
HideCaret(Handle);
}
#endregion
#region Elements required to create an RTF document
private const string RTF_HEADER = @"{\rtf1\ansi\ansicpg1252\deff0\deflang1033";
private const string RTF_DOCUMENT_PRE = @"\viewkind4\uc1\pard\cf1\f0\fs20";
private const string RTF_DOCUMENT_POST = @"\cf0\fs17}";
private string RTF_IMAGE_POST = @"}";
private const string FF_UNKNOWN = "UNKNOWN";
private enum EmfToWmfBitsFlags
{
EmfToWmfBitsFlagsDefault = 0x00000000,
EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
EmfToWmfBitsFlagsNoXORClip = 0x00000004
};
#endregion
#region Windows API
private const int WM_VSCROLL = 0x115;
private const int WM_HSCROLL = 0x114;
private const int SB_LINEUP = 0;
private const int SB_LINEDOWN = 1;
private const int SB_PAGEUP = 2;
private const int SB_PAGEDOWN = 3;
private const int SB_THUMBPOSITION = 4;
private const int SB_THUMBTRACK = 5;
private const int SB_TOP = 6;
private const int SB_BOTTOM = 7;
private const int SB_ENDSCROLL = 8;
private const int WM_SETREDRAW = 0x0B;
private const int EM_SETEVENTMASK = 0x0431;
private const int EM_SETCHARFORMAT = 0x0444;
private const int EM_GETCHARFORMAT = 0x043A;
private const int EM_GETPARAFORMAT = 0x043D;
private const int EM_SETPARAFORMAT = 0x0447;
private const int EM_SETTYPOGRAPHYOPTIONS = 0x04CA;
private const int CFM_UNDERLINETYPE = 0x800000;
private const int CFM_BACKCOLOR = 0x4000000;
private const int CFE_AUTOBACKCOLOR = 0x4000000;
private const int SCF_SELECTION = 0x01;
private const int PFM_ALIGNMENT = 0x08;
private const int TO_ADVANCEDTYPOGRAPHY = 0x01;
private const int SBS_HORIZ = 0;
private const int SBS_VERT = 1;
private const int SIF_RANGE = 0x0001;
private const int SIF_PAGE = 0x0002;
private const int SIF_POS = 0x0004;
private const int SIF_DISABLENOSCROLL = 0x0008;
private const int SIF_TRACKPOS = 0x0010;
private const int SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS);
[DllImport("user32", CharSet = CharSet.Auto)]
private static extern int SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);
[DllImport("user32", CharSet = CharSet.Auto)]
private static extern int SendMessage(HandleRef hWnd, int msg, int wParam, ref CHARFORMAT2 lParam);
[DllImport("user32", CharSet = CharSet.Auto)]
private static extern int SendMessage(HandleRef hWnd, int msg, int wParam, ref PARAFORMAT2 lParam);
[DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
private static extern int SetWindowTheme(
HandleRef hWnd,
[MarshalAs(UnmanagedType.LPWStr)]
string pszSubAppName,
[MarshalAs(UnmanagedType.LPWStr)]
string pszSubIdList);
[DllImport("user32.dll")]
protected static extern bool HideCaret(IntPtr hWnd);
[DllImport("user32", CharSet = CharSet.Auto)]
private static extern int GetScrollInfo(HandleRef hWnd, int nBar, ref SCROLLINFO info);
[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;
}
[StructLayout(LayoutKind.Sequential)]
private struct CHARFORMAT2
{
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;
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;
}
[StructLayout(LayoutKind.Sequential)]
private struct PARAFORMAT
{
public int cbSize;
public uint dwMask;
public short wNumbering;
public short wReserved;
public int dxStartIndent;
public int dxRightIndent;
public int dxOffset;
public short wAlignment;
public short cTabCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public int[] rgxTabs;
}
[StructLayout(LayoutKind.Sequential)]
private struct PARAFORMAT2
{
public int cbSize;
public uint dwMask;
public short wNumbering;
public short wReserved;
public int dxStartIndent;
public int dxRightIndent;
public int dxOffset;
public short wAlignment;
public short cTabCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public int[] rgxTabs;
public int dySpaceBefore;
public int dySpaceAfter;
public int dyLineSpacing;
public short sStyle;
public byte bLineSpacingRule;
public byte bOutlineLevel;
public short wShadingWeight;
public short wShadingStyle;
public short wNumberingStart;
public short wNumberingStyle;
public short wNumberingTab;
public short wBorderSpace;
public short wBorderWidth;
public short wBorders;
}
[StructLayout(LayoutKind.Sequential)]
private struct SCROLLINFO
{
public int cbSize;
public int fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
}
private struct RtfColorDef
{
public const string Black = @"\red0\green0\blue0";
public const string Maroon = @"\red128\green0\blue0";
public const string Green = @"\red0\green128\blue0";
public const string Olive = @"\red128\green128\blue0";
public const string Navy = @"\red0\green0\blue128";
public const string Purple = @"\red128\green0\blue128";
public const string Teal = @"\red0\green128\blue128";
public const string Gray = @"\red128\green128\blue128";
public const string Silver = @"\red192\green192\blue192";
public const string Red = @"\red255\green0\blue0";
public const string Lime = @"\red0\green255\blue0";
public const string Yellow = @"\red255\green255\blue0";
public const string Blue = @"\red0\green0\blue255";
public const string Fuchsia = @"\red255\green0\blue255";
public const string Aqua = @"\red0\green255\blue255";
public const string White = @"\red255\green255\blue255";
}
private struct RtfFontFamilyDef
{
public const string Unknown = @"\fnil";
public const string Roman = @"\froman";
public const string Swiss = @"\fswiss";
public const string Modern = @"\fmodern";
public const string Script = @"\fscript";
public const string Decor = @"\fdecor";
public const string Technical = @"\ftech";
public const string BiDirect = @"\fbidi";
}
#endregion
#region Property: SelectionUnderlineStyle
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public UnderlineStyle SelectionUnderlineStyle
{
get
{
CHARFORMAT2 fmt = new CHARFORMAT2();
fmt.cbSize = Marshal.SizeOf(fmt);
SendMessage(new HandleRef(this, Handle), EM_GETCHARFORMAT, SCF_SELECTION, ref fmt);
if ((fmt.dwMask & CFM_UNDERLINETYPE) == 0)
{
return UnderlineStyle.None;
}
else
{
byte style = (byte)(fmt.bUnderlineType & 0x0F);
return (UnderlineStyle)style;
}
}
set
{
UnderlineColor color = SelectionUnderlineColor;
if (value == UnderlineStyle.None)
color = UnderlineColor.Black;
CHARFORMAT2 fmt = new CHARFORMAT2();
fmt.cbSize = Marshal.SizeOf(fmt);
fmt.dwMask = CFM_UNDERLINETYPE;
fmt.bUnderlineType = (byte)((byte)value | (byte)color);
SendMessage(new HandleRef(this, Handle), EM_SETCHARFORMAT, SCF_SELECTION, ref fmt);
}
}
#endregion
#region Property: SelectionUnderlineColor
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public UnderlineColor SelectionUnderlineColor
{
get
{
CHARFORMAT2 fmt = new CHARFORMAT2();
fmt.cbSize = Marshal.SizeOf(fmt);
SendMessage(new HandleRef(this, Handle), EM_GETCHARFORMAT, SCF_SELECTION, ref fmt);
if ((fmt.dwMask & CFM_UNDERLINETYPE) == 0)
{
return UnderlineColor.None;
}
else
{
byte style = (byte)(fmt.bUnderlineType & 0xF0);
return (UnderlineColor)style;
}
}
set
{
if (value == UnderlineColor.None)
{
SelectionUnderlineStyle = UnderlineStyle.None;
}
else
{
UnderlineStyle style = SelectionUnderlineStyle;
if (style == UnderlineStyle.None)
value = UnderlineColor.Black;
CHARFORMAT2 fmt = new CHARFORMAT2();
fmt.cbSize = Marshal.SizeOf(fmt);
fmt.dwMask = CFM_UNDERLINETYPE;
fmt.bUnderlineType = (byte)((byte)style | (byte)value);
SendMessage(new HandleRef(this, Handle), EM_SETCHARFORMAT, SCF_SELECTION, ref fmt);
}
}
}
#endregion
#region Method: BeginUpdate
public void BeginUpdate()
{
_Updating++;
if (_Updating > 1)
return;
_OldEventMask = SendMessage(new HandleRef(this, Handle), EM_SETEVENTMASK, 0, 0);
SendMessage(new HandleRef(this, Handle), WM_SETREDRAW, 0, 0);
}
#endregion
#region Method: EndUpdate
public void EndUpdate()
{
_Updating--;
if (_Updating > 0)
return;
SendMessage(new HandleRef(this, Handle), WM_SETREDRAW, 1, 0);
SendMessage(new HandleRef(this, Handle), EM_SETEVENTMASK, 0, _OldEventMask);
}
#endregion
#region ScrollBarDetails
public void ScrollToBottom()
{
SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_BOTTOM, 0);
}
public void ScrollPageUp()
{
SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_PAGEUP, 0);
}
public void ScrollPageDown()
{
SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_PAGEDOWN, 0);
}
public void ScrollLineUp(int num)
{
for (int i = 0; i < num; i++)
{
SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_LINEUP, 0);
}
}
public void ScrollLineDown(int num)
{
for (int i = 0; i < num; i++)
{
SendMessage(new HandleRef(this, Handle), WM_VSCROLL, SB_LINEDOWN, 0);
}
}
public ScrollBarInformation VerticalScrollInformation
{
get
{
SCROLLINFO info = new SCROLLINFO();
info.cbSize = Marshal.SizeOf(info);
info.fMask = SIF_ALL;
int ret = GetScrollInfo(new HandleRef(this, Handle), SBS_VERT, ref info);
return new ScrollBarInformation(info.nMin, info.nMax, info.nPage, info.nPos, info.nTrackPos);
}
}
#endregion
#region Rtf Privates
private const int MM_TEXT = 1;
private const int MM_LOMETRIC = 2;
private const int MM_HIMETRIC = 3;
private const int MM_LOENGLISH = 4;
private const int MM_HIENGLISH = 5;
private const int MM_TWIPS = 6;
private const int MM_ISOTROPIC = 7;
private const int MM_ANISOTROPIC = 8;
private const int HMM_PER_INCH = 2540;
private const int TWIPS_PER_INCH = 1440;
private RtfColor textColor;
private RtfColor highlightColor;
private Dictionary<RtfColor, string> rtfColor;
private Dictionary<string, string> rtfFontFamily;
private float xDpi;
private float yDpi;
#endregion
#region Append RTF or Text to RichTextBox Contents
public void AppendRtf(string _rtf)
{
this.Select(this.TextLength, 0);
this.SelectedRtf = _rtf;
}
public void InsertRtf(string _rtf)
{
this.SelectedRtf = _rtf;
}
public void AppendTextAsRtf(string _text)
{
AppendTextAsRtf(_text, this.Font);
}
public void AppendTextAsRtf(string _text, Font _font)
{
AppendTextAsRtf(_text, _font, textColor);
}
public void AppendTextAsRtf(string _text, Font _font, RtfColor _textColor)
{
AppendTextAsRtf(_text, _font, _textColor, highlightColor);
}
public void AppendTextAsRtf(string _text, Font _font, RtfColor _textColor, RtfColor _backColor)
{
this.Select(this.TextLength, 0);
InsertTextAsRtf(_text, _font, _textColor, _backColor);
}
#endregion
#region RTF Insert Plain Text
public void InsertTextAsRtf(string _text)
{
InsertTextAsRtf(_text, this.Font);
}
public void InsertTextAsRtf(string _text, Font _font)
{
InsertTextAsRtf(_text, _font, textColor);
}
public void InsertTextAsRtf(string _text, Font _font, RtfColor _textColor)
{
InsertTextAsRtf(_text, _font, _textColor, highlightColor);
}
public void InsertTextAsRtf(string _text, Font _font, RtfColor _textColor, RtfColor _backColor)
{
StringBuilder _rtf = new StringBuilder();
_rtf.Append(RTF_HEADER);
_rtf.Append(GetFontTable(_font));
_rtf.Append(GetColorTable(_textColor, _backColor));
_rtf.Append(GetDocumentArea(_text, _font));
this.SelectedRtf = _rtf.ToString();
}
private string GetDocumentArea(string _text, Font _font)
{
StringBuilder _doc = new StringBuilder();
_doc.Append(RTF_DOCUMENT_PRE);
_doc.Append(@"\highlight2");
if (_font.Bold)
_doc.Append(@"\b");
if (_font.Italic)
_doc.Append(@"\i");
if (_font.Strikeout)
_doc.Append(@"\strike");
if (_font.Underline)
_doc.Append(@"\ul");
_doc.Append(@"\f0");
_doc.Append(@"\fs");
_doc.Append((int)Math.Round((2 * _font.SizeInPoints)));
_doc.Append(@" ");
_doc.Append(_text.Replace("\n", @"\par "));
_doc.Append(@"\highlight0");
if (_font.Bold)
_doc.Append(@"\b0");
if (_font.Italic)
_doc.Append(@"\i0");
if (_font.Strikeout)
_doc.Append(@"\strike0");
if (_font.Underline)
_doc.Append(@"\ulnone");
_doc.Append(@"\f0");
_doc.Append(@"\fs20");
_doc.Append(RTF_DOCUMENT_POST);
return _doc.ToString();
}
#endregion
#region RTF Insert Image
public void InsertImage(Image _image)
{
StringBuilder _rtf = new StringBuilder();
_rtf.Append(RTF_HEADER);
_rtf.Append(GetFontTable(this.Font));
_rtf.Append(GetImagePrefix(_image));
_rtf.Append(GetRtfImage(_image));
_rtf.Append(RTF_IMAGE_POST);
this.SelectedRtf = _rtf.ToString();
}
private string GetImagePrefix(Image _image)
{
StringBuilder _rtf = new StringBuilder();
int picw = (int)Math.Round((_image.Width / xDpi) * HMM_PER_INCH);
int pich = (int)Math.Round((_image.Height / yDpi) * HMM_PER_INCH);
int picwgoal = (int)Math.Round((_image.Width / xDpi) * TWIPS_PER_INCH);
int pichgoal = (int)Math.Round((_image.Height / yDpi) * TWIPS_PER_INCH);
_rtf.Append(@"{\pict\wmetafile8");
_rtf.Append(@"\picw");
_rtf.Append(picw);
_rtf.Append(@"\pich");
_rtf.Append(pich);
_rtf.Append(@"\picwgoal");
_rtf.Append(picwgoal);
_rtf.Append(@"\pichgoal");
_rtf.Append(pichgoal);
_rtf.Append(" ");
return _rtf.ToString();
}
[DllImportAttribute("gdiplus.dll")]
private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize,
byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags);
private string GetRtfImage(Image _image)
{
StringBuilder _rtf = null;
MemoryStream _stream = null;
Graphics _graphics = null;
Metafile _metaFile = null;
IntPtr _hdc;
try
{
_rtf = new StringBuilder();
_stream = new MemoryStream();
using (_graphics = this.CreateGraphics())
{
_hdc = _graphics.GetHdc();
_metaFile = new Metafile(_stream, _hdc);
_graphics.ReleaseHdc(_hdc);
}
using (_graphics = Graphics.FromImage(_metaFile))
{
_graphics.DrawImage(_image, new Rectangle(0, 0, _image.Width, _image.Height));
}
IntPtr _hEmf = _metaFile.GetHenhmetafile();
uint _bufferSize = GdipEmfToWmfBits(_hEmf, 0, null, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
byte[] _buffer = new byte[_bufferSize];
uint _convertedSize = GdipEmfToWmfBits(_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC,
EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
for (int i = 0; i < _buffer.Length; ++i)
{
_rtf.Append(String.Format("{0:X2}", _buffer[i]));
}
return _rtf.ToString();
}
finally
{
if (_graphics != null)
_graphics.Dispose();
if (_metaFile != null)
_metaFile.Dispose();
if (_stream != null)
_stream.Close();
}
}
#endregion
#region RTF Helpers
private string GetFontTable(Font _font)
{
StringBuilder _fontTable = new StringBuilder();
_fontTable.Append("{\\fonttbl{\\f0");
_fontTable.Append("\\");
if (rtfFontFamily.ContainsKey(_font.FontFamily.Name))
{
_fontTable.Append(rtfFontFamily[_font.FontFamily.Name]);
}
else
{
_fontTable.Append(rtfFontFamily[FF_UNKNOWN]);
}
_fontTable.Append("\\fcharset0 ");
_fontTable.Append(_font.Name);
_fontTable.Append(";}}");
return _fontTable.ToString();
}
private string GetColorTable(RtfColor _textColor, RtfColor _backColor)
{
StringBuilder _colorTable = new StringBuilder();
_colorTable.Append(@"{\colortbl ;");
_colorTable.Append(rtfColor[_textColor]);
_colorTable.Append(@";");
_colorTable.Append(rtfColor[_backColor]);
_colorTable.Append(@";}\n");
return _colorTable.ToString();
}
private string RemoveBadChars(string _originalRtf)
{
return _originalRtf.Replace("\0", "");
}
#endregion
#region Printing
private const double anInch = 14.4;
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct CHARRANGE
{
public int cpMin; public int cpMax; }
[StructLayout(LayoutKind.Sequential)]
private struct FORMATRANGE
{
public IntPtr hdc; public IntPtr hdcTarget; public RECT rc; public RECT rcPage; public CHARRANGE chrg; }
private const int WM_USER = 0x0400;
private const int EM_FORMATRANGE = WM_USER + 57;
[DllImport("USER32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
public int Print(int charFrom, int charTo, PrintPageEventArgs e)
{
RECT rectToPrint;
rectToPrint.Top = (int)(e.MarginBounds.Top * anInch);
rectToPrint.Bottom = (int)(e.MarginBounds.Bottom * anInch);
rectToPrint.Left = (int)(e.MarginBounds.Left * anInch);
rectToPrint.Right = (int)(e.MarginBounds.Right * anInch);
RECT rectPage;
rectPage.Top = (int)(e.PageBounds.Top * anInch);
rectPage.Bottom = (int)(e.PageBounds.Bottom * anInch);
rectPage.Left = (int)(e.PageBounds.Left * anInch);
rectPage.Right = (int)(e.PageBounds.Right * anInch);
IntPtr hdc = e.Graphics.GetHdc();
FORMATRANGE fmtRange;
fmtRange.chrg.cpMax = charTo; fmtRange.chrg.cpMin = charFrom;
fmtRange.hdc = hdc; fmtRange.hdcTarget = hdc; fmtRange.rc = rectToPrint; fmtRange.rcPage = rectPage;
IntPtr res = IntPtr.Zero;
IntPtr wparam = IntPtr.Zero;
wparam = new IntPtr(1);
IntPtr lparam = IntPtr.Zero;
lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
Marshal.StructureToPtr(fmtRange, lparam, false);
res = SendMessage(Handle, EM_FORMATRANGE, wparam, lparam);
Marshal.FreeCoTaskMem(lparam);
e.Graphics.ReleaseHdc(hdc);
return res.ToInt32();
}
#endregion
#region Link Clicked
void MudRichTextBox_LinkClicked(object sender, LinkClickedEventArgs e)
{
System.Diagnostics.Process.Start(e.LinkText);
}
#endregion
#region WndProc
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_VSCROLL:
base.WndProc(ref m);
if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBTRACK)
{
OnVScroll(EventArgs.Empty);
}
if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBPOSITION)
{
OnVScroll(EventArgs.Empty);
}
break;
case WM_HSCROLL:
base.WndProc(ref m);
if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBTRACK)
{
OnHScroll(EventArgs.Empty);
}
if ((m.WParam.ToInt32() & 0xffff) == SB_THUMBPOSITION)
{
OnHScroll(EventArgs.Empty);
}
break;
default:
base.WndProc(ref m);
break;
}
}
#endregion
}
}
Here is the code for the ScrollBarInformation
#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
#endregion
namespace WhelkMud.WindowOutput
{
public class ScrollBarInformation
{
int nMin = 0;
int nMax = 0;
int nPage = 0;
int nPos = 0;
int nTrackPos = 0;
public ScrollBarInformation()
{
}
public ScrollBarInformation(int min, int max, int page, int pos, int trackpos)
{
this.nMin = min;
this.nMax = max;
this.nPage = page;
this.nPos = pos;
this.nTrackPos = trackpos;
}
public int Minimum
{
get { return nMin; }
set { nMin = value; }
}
public int Maximum
{
get { return nMax; }
set { nMax = value; }
}
public int Page
{
get { return nPage; }
set { nPage = value; }
}
public int Position
{
get { return nPos; }
set { nPos = value; }
}
public int TrackPosition
{
get { return nTrackPos; }
set { nTrackPos = value; }
}
}
}