Changing Font for Richtextbox Without Losing Formatting

Changing font for richtextbox without losing formatting

This is a RichTextBox that I have used in the past. It's spliced together from code found here at Stack Overflow and the internet at large:

public class RichBox : RichTextBox {
private const UInt32 CFM_BOLD = 0x00000001;
private const UInt32 CFM_ITALIC = 0x00000002;
private const UInt32 CFM_UNDERLINE = 0x00000004;
private const UInt32 CFM_STRIKE = 0x00000008;
private const UInt32 CFM_FACE = 0x20000000;
private const UInt32 CFM_SIZE = 0x80000000;

private const int WM_PAINT = 0xF;
private const int WM_SETREDRAW = 0xB;
private const int WM_USER = 0x400;

private const int EM_SETCHARFORMAT = (WM_USER + 68);
private const int SCF_SELECTION = 0x0001;
private const int EM_GETEVENTMASK = WM_USER + 59;
private const int EM_SETEVENTMASK = WM_USER + 69;
private const int EM_GETSCROLLPOS = WM_USER + 221;
private const int EM_SETSCROLLPOS = WM_USER + 222;

[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;
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("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr LoadLibrary(string lpFileName);

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

[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);

[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, IntPtr lParam);

private bool frozen = false;
private Point lastScroll = Point.Empty;
private IntPtr lastEvent = IntPtr.Zero;
private int lastIndex = 0;
private int lastWidth = 0;

protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
if (LoadLibrary("msftedit.dll") != IntPtr.Zero) {
cp.ClassName = "RICHEDIT50W";
}
return cp;
}
}

[Browsable(false)]
[DefaultValue(typeof(bool), "False")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool FreezeDrawing {
get { return frozen; }
set {
if (value != frozen) {
frozen = value;
if (frozen) {
this.SuspendLayout();
SendMessage(this.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref lastScroll);
lastEvent = SendMessage(this.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
lastIndex = this.SelectionStart;
lastWidth = this.SelectionLength;
} else {
this.Select(lastIndex, lastWidth);
SendMessage(this.Handle, EM_SETEVENTMASK, 0, lastEvent);
SendMessage(this.Handle, EM_SETSCROLLPOS, 0, ref lastScroll);
SendMessage(this.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
this.Invalidate();
this.ResumeLayout();
}
}
}
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Font CurrentFont {
get {
Font result = this.Font;
if (this.SelectionLength == 0) {
result = SelectionFont;
} else {
using (RichBox rb = new RichBox()) {
rb.FreezeDrawing = true;
rb.SelectAll();
rb.SelectedRtf = this.SelectedRtf;
rb.Select(0, 1);
result = rb.SelectionFont;
}
}
return result;
}
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string SelectionFontName {
get { return CurrentFont.FontFamily.Name; }
set {
CHARFORMAT cf = new CHARFORMAT();
cf.cbSize = Marshal.SizeOf(cf);
cf.szFaceName = new char[32];
cf.dwMask = CFM_FACE;
value.CopyTo(0, cf.szFaceName, 0, Math.Min(31, value.Length));
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
SendMessage(this.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
}
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public float SelectionFontSize {
get { return CurrentFont.Size; }
set {
CHARFORMAT cf = new CHARFORMAT();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_SIZE;
cf.yHeight = Convert.ToInt32(value * 20);
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
SendMessage(this.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
}
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool SelectionBold {
get { return CurrentFont.Bold; }
set {
CHARFORMAT cf = new CHARFORMAT();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_BOLD;
cf.dwEffects = value ? CFM_BOLD : 0;
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
SendMessage(this.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
}
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool SelectionItalic {
get { return CurrentFont.Italic; }
set {
CHARFORMAT cf = new CHARFORMAT();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_ITALIC;
cf.dwEffects = value ? CFM_ITALIC : 0;
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
SendMessage(this.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
}
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool SelectionStrikeout {
get { return CurrentFont.Strikeout; }
set {
CHARFORMAT cf = new CHARFORMAT();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_STRIKE;
cf.dwEffects = value ? CFM_STRIKE : 0;
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
SendMessage(this.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
}
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool SelectionUnderline {
get { return CurrentFont.Underline; }
set {
CHARFORMAT cf = new CHARFORMAT();
cf.cbSize = Marshal.SizeOf(cf);
cf.dwMask = CFM_UNDERLINE;
cf.dwEffects = value ? CFM_UNDERLINE : 0;
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lParam, false);
SendMessage(this.Handle, EM_SETCHARFORMAT, SCF_SELECTION, lParam);
}
}
}

It adds new properties such as SelectionBold, SelectionItalic, etc. where you can apply the attribute and not lose the other formatting of the text.

How to preserve current font/style while making changes to RTF text in C# Windows Forms?

Doesn't seem to be an easy way to do this.

This link might be of interest:
How do I maintain RichText formatting (bold/italic/etc) when changing any one element?

Change RichTextBox font family only not FontSize

If you want to avoid the pinvoking solution, then you would have to loop through the characters individually. To avoid flicker, you can use an offscreen RichTextBox control to apply the changes:

using (RichTextBox tmpRB = new RichTextBox()) {
tmpRB.SelectAll();
tmpRB.SelectedRtf = rtb.SelectedRtf;
for (int i = 0; i < tmpRB.TextLength; ++i) {
tmpRB.Select(i, 1);
tmpRB.SelectionFont = new Font("Arial", tmpRB.SelectionFont.Size);
}
tmpRB.SelectAll();
rtb.SelectedRtf = tmpRB.SelectedRtf;
}

Replace RichTextBox Text but keep formatting

Its working, this is how I did it in the end, not too pretty but it functions. WPF RTB really should have rtf property like winforms...

Thanks to Kent for putting me on the right track.

            var textRange = new TextRange(rtb_wording.Document.ContentStart, rtb_wording.Document.ContentEnd);
string rtf;
using (var memoryStream = new MemoryStream())
{
textRange.Save(memoryStream, DataFormats.Rtf);
rtf = ASCIIEncoding.Default.GetString(memoryStream.ToArray());
}

rtf = rtf.Replace("<our_name>", "Bob Cratchet");

MemoryStream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(rtf));
rtb_wording.SelectAll();
rtb_wording.Selection.Load(stream, DataFormats.Rtf);

C#: Changing font style of WinForm RichTextBox without selecting the text

Oops turns out I was using Hans code incorrectly. I was supposed to call BeginUpdate() to stop drawing control and EndUpdate() to start drawing it again. I was doing it the other way around.

Thanks for all the help, everyone (especially Hans)!

Change font style to richtextbox

You need to set Bold in FontWeight-property:

sp.FontWeight = FontWeights.Bold;

Same in WinForms:

sp.Font = new Font(sp.Font.Name, sp.Font.Size, FontStyle.Bold); 

C# losing Font Style in RichTextBox after deleting lines

Never change the Text of a RichtTextBox if it contains any formatting.

Changing the Lines property (by Skip) is just another way to change the Text.

Instead only use the functions the RTB provides: Always start by selecting the portion you want to format, then apply one or more of the functions and/or set one or more of the properties..:

To delete portions use Cut.

Here is a function that will delete a number of entire lines:

void DeleteLines(RichTextBox rtb, int fromLine, int count)
{
int p1 = rtb.GetFirstCharIndexFromLine(fromLine);
int p2 = rtb.GetFirstCharIndexFromLine(fromLine + count);

rtb.SelectionStart = p1;
rtb.SelectionLength = p2 - p1;

bool readOnly = rtb.ReadOnly; // allow change even when the RTB is readonly
rtb.ReadOnly = false; ;
rtb.Cut();
rtb.ReadOnly = readOnly;
}

Trying to keept the formatting alive yourself is a tedious and error-prone waste of your time.

In addition to font properties you would also have to resore all other things you can set with the SelectedXXX properties, like colors, alignment, spacing etc etc..

To delete the first 3 lines use:

DeleteLines(yourRTB, 0, 3);

To restrict the text to 10 lines use:

DeleteLines(yourRTB, 0, yourRTB.Lines.Length - 10);

Note that the function above should have a few checks for valid input; I left them out as the checks somehow need a decision what to do, if count or fromLine if greater than Lines.Length or if fromLine is negative..

While we are at it, here is how to append a bold line:

yourRTB.SelectionStart = yourRTB.Text.Length;
yourRTB.SelectionLength = 0;
using (Font font = new Font(yourRTB.SelectionFont, FontStyle.Bold))
yourRTB.SelectionFont = font;
yourRTB.AppendText(yourNewLine + textOfNewLine);

Of course it really shold go into a reuseable function that the the bolding as a parameter..

Update:

since you are using WordWrap you may prefer this function. It deletes the actual lines, not the visible ones:

void DeleteLinesWW(RichTextBox rtb, int fromLine, int count)
{
int nll = 1; // <<=== pick the length of your new line character(s)!!
int pS = rtb.Lines.Take(fromLine).Select(x => x.Length + nll).Sum() - nll;
int pL = rtb.Lines.Take(fromLine + count).Select(x => x.Length + nll).Sum() - nll;
if (pS < 0) { pS = 0; pL++; }
rtb.SelectionStart = pS;
rtb.SelectionLength = pL - pS ;

bool readOnly = rtb.ReadOnly;
rtb.ReadOnly = false; // allow change even when the RTB is readonly
rtb.Cut();
rtb.ReadOnly = readOnly;
}

A word on NewLine: Do note that I have not used the Environment.NewLine constant as it not really a good idea. If you add multiline text to the RichTextBox in the designer and then look at it you will see that it uses simple '\n' new lines, no returns, no NL-CR, just '\n'. So this seems to be the generic way in a winforms RTB and I recommend using it..

The new function relies on all lines having a newline of the same length!

To make sure you can use this replacement function:

int RTBReplace(RichTextBox rtb, string oldText, string newText)
{
int p = 0; int count = 0;
do
{
p = richTextBox1.Text.IndexOf(oldText);
if (p >= 0)
{
richTextBox1.SelectionStart = p;
richTextBox1.SelectionLength = oldText.Length;
richTextBox1.SelectedText = newText;
count ++;
}
}
while (p >= 0);
return count;
}

Calling it like this:

RTBReplace(yourrichTextBox, "\r\n", "\n");

Update 2:

Here is an example how to add your chat lines:

private void button1_Click(object sender, EventArgs e)
{
string cLine = "Taw: Hello World"; // use your own lines!
var chatstr = cLine.Split(new string[] { ": " }, 2, StringSplitOptions.None);
AppendLineBold(yourrichTextBox, "\n" + chatstr[0], true);
AppendLineBold(yourrichTextBox, chatstr[1], false);
yourrichTextBox.ScrollToCaret();
}

void AppendLineBold(RichTextBox rtb, string text, bool bold)
{
rtb.SelectionStart = richTextBox1.Text.Length;
rtb.SelectionLength = 0;
using (Font font = new Font(rtb.SelectionFont,
bold ? FontStyle.Bold : FontStyle.Regular))
rtb.SelectionFont = font;
rtb.AppendText(text);
}

Update 3:

Looks like the ReadOnly property disallows the use of Cut. So we need to temporatily allow changes.

Funny: SelectedText can't be set either, but AppendText works fine..



Related Topics



Leave a reply



Submit