7

How to change the font of all the contents of a richtextbox without losing formatting?

I am trying to use

rtb.SelectAll();
rtb.SelectionFont = new Font(fontName,...);

but the font constructor has to take besides the font type either the font style (bold, italics, ...) or font size. So using this would change the style/size of all the content of the richtextbox.

Of course the same applies for any selection in the richtextbox.

Jerry
  • 4,258
  • 3
  • 31
  • 58
  • 1
    See [How do I maintain RichText formatting (bold/italic/etc) when changing any one element?](http://stackoverflow.com/q/5325918/719186). Ultimately, pinvoking is probably the best solution to this. – LarsTech Apr 30 '13 at 18:21
  • Can you be clearer on pinvoking? – Jerry Apr 30 '13 at 18:27

5 Answers5

11

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.

LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • Hi LarsTech, Sometimes when I change the font for a large selection containing bold, the bolding is lost. When I click ctrl+Z once, the bolding is restored. Do you have an idea why this may occur? Thanks – Jerry Jun 11 '13 at 21:49
  • 1
    @Jerry If you are changing the `SelectionFont` property, then yes, you are telling the document to replace all of the font attributes with the new font. To change the font but keep the attributes, try using the `SelectionFontName` or `SelectionFontSize` properties instead. Also, Ctrl-Z is the undo function. – LarsTech Jun 17 '13 at 13:25
  • Thanks, this was the problem. I forgot to update one function to use `SelectionFontName` instead of `SelectionFont`. – Jerry Jun 17 '13 at 19:56
  • Why am I getting this error `The designer cannot process unknown name 'AutoScaleMode' at line 32. The code within the method 'InitializeComponent' is generated by the designer and should not be manually modified. Please remove any changes and try opening the designer again. ` – Ghasem Aug 30 '15 at 04:04
  • 1
    @AlexJolig AutoScaleMode is a form property, not a RichTextBox property. Remove the line. – LarsTech Aug 30 '15 at 13:48
  • 1
    This is MUCH faster than changing the font of one char at a time. With this solution, it took 12ms to do what used to take over 4 seconds for me. – 41686d6564 stands w. Palestine Sep 02 '21 at 02:55
3

You can pass the new font name while keeping other values intact by using the existing richtextbox font properties. For changing only font name of a selected text, you need to do:

if (rtb.SelectionFont !=null)
    rtb.SelectionFont = new Font(fontName, rtb.SelectionFont.Size, rtb.SelectionFont.Style);

Note that above code will only work, if all the selected text has same formatting (font size, style etc). This is detected by checking the SelectionFont property first, it will be null if the selection contains a mix of styles.

Now to change the font name of all the contents of richtextbox while keeping other formatting intact, you need to loop through all the characters of the richtextbox and apply font name one by one.

for (int i = 0; i < rtb.TextLength; i++)
{
    rtb.Select(i, 1);
    rtb.SelectionFont = new Font(fontName, rtb.SelectionFont.Size, rtb.SelectionFont.Style);
}
ePandit
  • 2,905
  • 2
  • 24
  • 15
1

You can pass in new values for whatever parameter you want and use the rtb properties to preserve other values. For example, if you want to change the font family but want to preserve the font size, this is what you'd do:

rtb.SelectionFont = new Font(fontName, rtb.Font.Size);

This will change the SelectionFont family to fontName but preserves the font size. You can follow the same pattern for other overloads.

Arian Motamedi
  • 7,123
  • 10
  • 42
  • 82
  • what do you mean different font sizes? Explain what it is exactly that you're trying to achieve and then maybe we can figure it out. – Arian Motamedi Apr 30 '13 at 18:18
  • Different texts with different font sizes. Anyway even your answer causes font style to change. – Jerry Apr 30 '13 at 18:20
0
Private Sub changeFont(ByVal fontz As FontStyle, getStr As RichTextBox)
   Dim currentFont As System.Drawing.Font = txt.SelectionFont
   Dim newFontStyle As System.Drawing.FontStyle
   newFontStyle = fontz
   getStr.SelectionFont = New Font(currentFont.FontFamily, currentFont.Size, newFontStyle)
End Sub

This will change the font property of the selected text.
Sample:

changeFont(FontStyle.Italic, [textbox_name])
croxy
  • 4,082
  • 9
  • 28
  • 46
jomz
  • 1
0

Changing font for richtextbox without losing formatting

private void Change_RichTextBox_FontName(string fontName )
{

        if (richTextBox1.TextLength ==0)
        {
            return;
        }

        richTextBox1.Select(0, 1);
        var lastTextColor = richTextBox1.SelectionColor;
        var lastFontStyle  = richTextBox1.SelectionFont.Style;
        var lastFontSize = richTextBox1.SelectionFont.Size;
        var lastSelectionStart = 0;
        for (int i=1; i < richTextBox1.TextLength;i++)
        {
            richTextBox1.Select(i, 1);

            var selColor = richTextBox1.SelectionColor;
            var selStyle = richTextBox1.SelectionFont.Style;
            var selSize = richTextBox1.SelectionFont.Size;

            if (selColor != lastTextColor ||
                selStyle != lastFontStyle ||
                selSize !=  lastFontSize ||
                 i== richTextBox1.TextLength-1)
            {
                richTextBox1.Select(lastSelectionStart, i - lastSelectionStart);
                richTextBox1.SelectionColor = lastTextColor;
                richTextBox1.SelectionFont = 
                    new Font(fontName, lastFontSize, lastFontStyle);
                
                lastTextColor = selColor;
                lastFontStyle = selStyle;
                lastFontSize =  selSize;
                lastSelectionStart = i;
            }
        }
    }
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 27 '22 at 05:28