Question RichTextBox with BackgroundImage. How do I get the Text to display?

tim8w

Well-known member
Joined
Sep 8, 2020
Messages
130
Programming Experience
10+
RTB.png


Hi,
I am trying to get my custom WinForms RichTextBox to be able to use Transparent Background and a BackgroundImage. I got both of those to work from the many examples out there, but when I do, the RichTextBox Text doesn't display correctly at all, even for simple non-rich type of text. Below is the new class I am using to attempt this:

Custom RichTextBox Control:
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Security;

namespace RichEditControl
{
    public partial class customRichTextBox : RichTextBox
    {
        #region private members
        private Image m_BackgroundImage = null;
        #endregion

        public customRichTextBox()
        {
            //SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            //SetStyle(ControlStyles.Opaque, true);
            //SetStyle(ControlStyles.EnableNotifyMessage, true);

            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            SetStyle(ControlStyles.UserPaint, true);
            base.BackColor = Color.Transparent;
            this.ScrollBars = RichTextBoxScrollBars.None;

            InitializeComponent();
        }

        public override Color BackColor
        {
            get
            {
                return base.BackColor;
            }
            set
            {
                // Don't change from Transparent;
            }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            base.OnPaintBackground(e);
            e.Graphics.DrawImage(new Bitmap(BackgroundImage), e.ClipRectangle);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
        }

        [Description("BackgroundImage"), AmbientValue((string)null), Category(""), Localizable(true)]
        public override Image BackgroundImage
        {
            get { return m_BackgroundImage; }
            set { if (m_BackgroundImage != value) { m_BackgroundImage = value; this.Invalidate(false); } }
        }

    }
}

I was thinking I could draw the text somehow in the OnPaint() event, but am unsure how to go about doing this. Or am I going about this all wrong and is there a better way to do this?
 
Last edited:
Just to confirm, things work correctly for your other approach when using a plain text box, but not with a rich text box. Is that correct?

So that is why you are posting the question here about an alternative way to get the transparent effect?
 
Just to confirm, things work correctly for your other approach when using a plain text box, but not with a rich text box. Is that correct?

So that is why you are posting the question here about an alternative way to get the transparent effect?
Not sure what you are referring to. I am posting here because the RTB Text appears strange. It doesn't write over the BackgroundImage as I expected. It kind of pushes it out of the way. Picture of this is below.
This is what it looks like after I type ABCD and hit 'return'.


1643420164574.png
 
Nevermind. I misread your statement about things working based on other examples. I thought that the other examples used other controls.

Anyway the problem you are seeing there stems from passing in the clip rectangle to DrawImage(). The rectangle parameter of that method is a size parameter, not a clipping parameter, so the method is trying to scale down the image to that size.

 
Nevermind. I misread your statement about things working based on other examples. I thought that the other examples used other controls.

Anyway the problem you are seeing there stems from passing in the clip rectangle to DrawImage(). The rectangle parameter of that method is a size parameter, not a clipping parameter, so the method is trying to scale down the image to that size.

OK. I changed the value passed to DrawImage to the size of the Image and now when I type A, B, C, D 'return', 'return', I get the following:

1643643127633.png


So I still get no Text, but now the Image only shifts down.
 
That is because the rich text box has drawing optimization code that just shifts down part of its visuals in response to line inserts instead of redrawing all the lines.

You are dealing with RichEdit 2.0 which Murray Sargent and the Office team's re-wrote based on RichEdit 1.0 which David Fulmer, Peter Durham, Kyle Larson, and I first wrote for Win9x and Win2K. In our first version, we had some redrawing optimizations like doing some line shifts, etc., but I know that Murray took it to a whole new level for even more features and performance.

When you subclass a Windows control, you do so at your own risk. You basically have to just deal with the current behavior of that control and try to workaround it's quirks. (Try subclassing the TabControl sometime and be prepared to buy a lot of aspirin and anti-acids.)
 
Hello Tim This Is My Code I Hope It Will Help You And Others RichTextBox With Transparent Back Color And Also Support Background Image With Changeable highlight Color

Screenshot 2024-03-04 020723.jpg
Screenshot 2024-03-04 020628.jpg


ImageRichTextBox:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
//----------------     Hello It's Me Amro Omran    ---------------- \\
//----------------      Hotmail.Home@gmail.com     ---------------- \\
//---------------- Visit My Youtube Channel ------------------------\\
//----------------- https://www.youtube.com/@Amro-Omran ---------------\\
//----------------     Please Like And subscribe For More useful Codes---------------- \\
namespace ImageRichTextBox
{
    public class ImageRichTextBox : RichTextBox
    {
        private Image backgroundImage;
        private Bitmap OnpaintImage;
        private Color highlightColor = Color.Yellow; // You can set this to any desired highlight color
        private const int WM_SETFONT = 0x30;
        private const int WM_NCPAINT = 0x85;
        private const int WM_CTLCOLOR = 0x0019;
        private const int EM_SETOPTIONS = 0x2003;
        private const int ECOOP_SET = 0x0002;
        public bool ClearTextOnTransparent = false;
        private StringFormat TextFormat { get; set; }
        private Bitmap buffer;
        public bool AutoSizeTextBoxToText = false;
        public bool AutoSizeTextBoxToImage = false;
        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string lpFileName);
        [DllImport("user32.dll")]
        private static extern IntPtr GetWindowDC(IntPtr hwnd);
        [DllImport("user32.dll")]
        private static extern int ReleaseDC(IntPtr hwnd, IntPtr dc);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool HideCaret(IntPtr hWnd);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool ShowCaret(IntPtr hWnd);
        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref PARAFORMAT lParam);
        private const int WM_USER = 0x0400;
        private const int EM_SETPARAFORMAT = WM_USER + 71;
        private const int PF_LEFT = 0x0001;
        private const int PF_RIGHT = 0x0002;
        private const int PF_CENTER = 0x0003;
        [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;
        }
        public ImageRichTextBox()
        {
            InitializeComponent();
            SetStyle(ControlStyles.UserPaint |
              ControlStyles.SupportsTransparentBackColor |
              ControlStyles.AllPaintingInWmPaint |
              ControlStyles.OptimizedDoubleBuffer, true);
            Multiline = true;
            BorderStyle = BorderStyle.Fixed3D;
            // Set the Font property with the desired size
            Font = new Font(Font.FontFamily, 12);
            BackColor = Color.FromArgb(80, 20, 60, 170);
       }
       [
          Category("Appearance"),
          Description("clear Text On Transparent Back Ground"),
          Browsable(true),
          DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)
         ]
       public new bool clearTextOnTransparent
        {
            get { return ClearTextOnTransparent; }
            set
            {
                ClearTextOnTransparent = value;
                Invalidate();
            }
        }
       [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public new Image Image
        {
            get { return backgroundImage; }
            set
            {
                backgroundImage = value;
                Invalidate();
            }
        }
       // New property for text alignment
        private HorizontalAlignment textAlign = HorizontalAlignment.Left;
        public HorizontalAlignment TextAlign
        {
            get { return textAlign; }
            set
            {
                textAlign = value;
                Invalidate();
            }
        }
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public new Color HighlightColor
        {
            get { return highlightColor; }
            set
            {
                highlightColor = value;
                Invalidate();
            }
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            using (var br = new SolidBrush(ForeColor))
            using (var sf = new StringFormat())
            {
                // Set text alignment based on the TextAlign property
                switch (TextAlign)
                {
                    case HorizontalAlignment.Left:
                        sf.Alignment = StringAlignment.Near;
                        break;
                    case HorizontalAlignment.Center:
                        sf.Alignment = StringAlignment.Center;
                        break;
                    case HorizontalAlignment.Right:
                        sf.Alignment = StringAlignment.Far;
                        break;
                }
                if (backgroundImage != null)
                {
                    // Draw the background image
                    e.Graphics.DrawImage(backgroundImage, 0, 0, Width, Height);
                }
                else
                {
                    // Create a buffer for double buffering
                    OnpaintImage = new Bitmap(Width, Height);
                    e.Graphics.DrawImage(OnpaintImage, 0, 0, Width, Height);
                }
                // Highlight the selected text
                if (SelectionLength > 0)
                {
                    int selectionStart = SelectionStart;
                    int selectionEnd = SelectionStart + SelectionLength;
                    // Ensure that the selection is within the bounds of the text
                    selectionStart = Math.Max(0, Math.Min(selectionStart, Text.Length - 1));
                    selectionEnd = Math.Max(0, Math.Min(selectionEnd, Text.Length));
                    // Calculate the position and width of the selected text
                    float selectionX;
                    float selectionWidth;
                    switch (TextAlign)
                    {
                        case HorizontalAlignment.Left:
                            selectionX = e.Graphics.MeasureString(Text.Substring(0, selectionStart), Font, PointF.Empty, sf).Width;
                            selectionWidth = e.Graphics.MeasureString(Text.Substring(selectionStart, selectionEnd - selectionStart), Font, PointF.Empty, sf).Width;
                            break;
                        case HorizontalAlignment.Center:
                            float textWidth = e.Graphics.MeasureString(Text, Font, PointF.Empty, sf).Width;
                            selectionX = (Width - textWidth) / 2 + e.Graphics.MeasureString(Text.Substring(0, selectionStart), Font, PointF.Empty, sf).Width;
                            selectionWidth = e.Graphics.MeasureString(Text.Substring(selectionStart, selectionEnd - selectionStart), Font, PointF.Empty, sf).Width;
                            break;
                        case HorizontalAlignment.Right:
                            float textWidthRight = e.Graphics.MeasureString(Text, Font, PointF.Empty, sf).Width;
                            selectionX = Width - textWidthRight + e.Graphics.MeasureString(Text.Substring(0, selectionStart), Font, PointF.Empty, sf).Width;
                            selectionWidth = e.Graphics.MeasureString(Text.Substring(selectionStart, selectionEnd - selectionStart), Font, PointF.Empty, sf).Width;
                            break;
                        default:
                            selectionX = 0;
                            selectionWidth = 0;
                            break;
                    }
                   float textHeight = TextRenderer.MeasureText(Text, Font).Height;
                   RectangleF selectionRect = new RectangleF(
                        selectionX,
                        0,
                        selectionWidth,
                       textHeight
                    );
                    if (this.Focused)
                    {
                        using (var highlightBrush = new SolidBrush(highlightColor))
                        {
                            e.Graphics.FillRectangle(highlightBrush, selectionRect);
                        }
                    }
                    else
                    {
                    }
                }
               // Draw the text
                if (!ClearTextOnTransparent)
                {
                    e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
                }
                else
                {
                    e.Graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
                }
                e.Graphics.DrawString(Text, Font, br, ClientRectangle, sf);
                //using (var brush = new SolidBrush(BackColor))
                //{
                //    if (BorderStyle != BorderStyle.None)
                //    {
                //        e.Graphics.DrawRectangle(Pens.Green, 0, 0, Width - 5.55f, Height - 5.55f);
                //    }
                //}
            }
            base.OnPaint(e);
       }
        //protected override CreateParams CreateParams
        //{
        //    get
        //    {
        //        CreateParams cp = base.CreateParams;
        //        cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
        //        if (imageTextBox.LoadLibrary("msftedit.dll") != IntPtr.Zero)
        //        {
        //            cp.ClassName = "RICHEDIT50W";
        //        }
        //        else if (imageTextBox.LoadLibrary("riched20.dll") != IntPtr.Zero)
        //        {
        //            cp.ClassName = "RichEdit20W";
        //        }
       //        return cp;
        //    }
       //}
       private bool bFist = true;
        private string OldText = "";
        protected override void WndProc(ref Message m)
        {
            if ((m.Msg == WM_NCPAINT || m.Msg == 0xF) && this.Focused && backgroundImage != null)
            {
                IntPtr dc = GetWindowDC(Handle);
                using (Graphics g = Graphics.FromHdc(dc))
                {
                    // Create a buffer for double buffering
                    buffer = new Bitmap(Width + 1, Height + 1);
                    using (Graphics bufferGraphics = Graphics.FromImage(buffer))
                    {
                        using (var brush = new SolidBrush(BackColor))
                        {
                            if (BorderStyle != BorderStyle.None)
                            {
                                bufferGraphics.DrawRectangle(Pens.DeepSkyBlue, 0, 0, Width - 0.5f, Height - 0.5f);
                            }
                            bufferGraphics.FillRectangle(brush, new Rectangle(1, 1, ClientRectangle.Width, ClientRectangle.Height));
                        }
                        using (var br = new SolidBrush(ForeColor))
                        using (var sf = new StringFormat())
                        {
                            // Set text alignment based on the TextAlign property
                            switch (TextAlign)
                            {
                                case HorizontalAlignment.Left:
                                    sf.Alignment = StringAlignment.Near;
                                    break;
                                case HorizontalAlignment.Center:
                                    sf.Alignment = StringAlignment.Center;
                                    break;
                                case HorizontalAlignment.Right:
                                    sf.Alignment = StringAlignment.Far;
                                    break;
                            }
                            // Draw the background image
                            bufferGraphics.DrawImage(backgroundImage, 0, 0, Width, Height);
                            bufferGraphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
                            bufferGraphics.DrawString(Text, Font, br, ClientRectangle, sf);
                        }
                    }
                   if (m.Msg == 15)
                    {
                        if (this.bFist)
                        {
                            this.bFist = false;
                            this.OldText = this.Text;
                        }
                        if (this.OldText != this.Text)
                        {
                            this.OldText = this.Text;
                            //EventArgs e = new EventArgs();
                            //this.OnTextChanged(e);
                           // Handle the change here
                            OnTextChanged(EventArgs.Empty);
                       }
                    }
                    const int EN_CHANGE = 0x0300;
                   if (m.Msg == EN_CHANGE)
                    {
                        // Handle the change here
                        OnTextChanged(EventArgs.Empty);
                   }
                   // Draw the buffer onto the control's graphics
                   // g.DrawImage(buffer, 0, 0);
                    ReleaseDC(Handle, dc);
                    base.WndProc(ref m);
                }
            }
            else
            {
                if (m.Msg == 15)
                {
                    if (this.bFist)
                    {
                        this.bFist = false;
                        this.OldText = this.Text;
                    }
                    if (this.OldText != this.Text)
                    {
                        this.OldText = this.Text;
                        //EventArgs e = new EventArgs();
                        //this.OnTextChanged(e);
                       // Handle the change here
                        OnTextChanged(EventArgs.Empty);
                   }
                }
                const int EN_CHANGE = 0x0300;
               if (m.Msg == EN_CHANGE)
                {
                    // Handle the change here
                    OnTextChanged(EventArgs.Empty);
               }
               base.WndProc(ref m);
           }
       }
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                buffer?.Dispose();
                components?.Dispose();
            }
            base.Dispose(disposing);
        }
        private System.ComponentModel.IContainer components = null;
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
        public void SetMultiline(bool multiline)
        {
            int options = multiline ? ECOOP_SET : 0;
            SendMessage(Handle, EM_SETOPTIONS, (IntPtr)options, IntPtr.Zero);
            Multiline = multiline;
            // Redraw the control
            Invalidate();
        }
        public void SetFontFamily(string fontFamilyName)
        {
            Font newFont = new Font(fontFamilyName, Font.Size);
            SendMessage(Handle, WM_SETFONT, newFont.ToHfont(), (IntPtr)1);
            //// Dispose the previous font to prevent memory leaks
            //Font.Dispose();
            Font = newFont;
            // Redraw the control
            Invalidate();
        }
        public void SetFontSize(int newSize)
        {
            Font = new Font(Font.FontFamily, newSize);
            SendMessage(Handle, WM_SETFONT, Font.ToHfont(), (IntPtr)1);
            Invalidate();
        }
        private void AutoSizeTextBox(TextBox txt)
        {
            if (AutoSizeTextBoxToText)
            {
                const int x_margin = 50;
                const int y_margin = 10;
                Size size = TextRenderer.MeasureText(txt.Text, txt.Font);
                txt.ClientSize = new Size(size.Width + x_margin, size.Height + y_margin);
            }
            else if (AutoSizeTextBoxToImage && backgroundImage != null)
            {
                const int x_margin = -2;
                const int y_margin = 2;
                // Size size = TextRenderer.MeasureText(txt.Text, txt.Font);
                txt.ClientSize = new Size(backgroundImage.Width + x_margin, backgroundImage.Height + y_margin);
            }
            else
            {
            }
        }
        public void SetTextAlignment(HorizontalAlignment alignment)
        {
            PARAFORMAT paraFormat = new PARAFORMAT();
            paraFormat.cbSize = Marshal.SizeOf(typeof(PARAFORMAT));
            paraFormat.dwMask = 0x0000000A; // PFM_ALIGNMENT
            paraFormat.wAlignment = GetWin32TextAlignment(alignment);
            SendMessage(Handle, EM_SETPARAFORMAT, 0, ref paraFormat);
        }
        private short GetWin32TextAlignment(HorizontalAlignment alignment)
        {
            switch (alignment)
            {
                case HorizontalAlignment.Left:
                    return PF_LEFT;
                case HorizontalAlignment.Center:
                    return PF_CENTER;
                case HorizontalAlignment.Right:
                    return PF_RIGHT;
                default:
                    if (TextAlign == HorizontalAlignment.Left)
                    {
                        return PF_LEFT;
                    }
                    else if (TextAlign == HorizontalAlignment.Right)
                    {
                        return PF_RIGHT;
                    }
                    else if (TextAlign == HorizontalAlignment.Center)  // Fix the syntax here
                    {
                        return PF_CENTER;
                    }
                    else
                    {
                        // Handle the case when TextAlign is not one of the specified values
                        // You might want to return a default value or throw an exception
                        return PF_LEFT; // or throw new InvalidOperationException("Invalid TextAlign value");
                    }
            }
        }
        private ContextMenuStrip CreateCustomContextMenu()
        {
            ContextMenuStrip contextMenu = new ContextMenuStrip();
            // Undo
            ToolStripMenuItem undoMenuItem = new ToolStripMenuItem("Undo");
            undoMenuItem.ShortcutKeys = Keys.Control | Keys.Z; // Set Ctrl+Z as the shortcut key
            undoMenuItem.Click += (sender, e) => Undo();
            contextMenu.Items.Add(undoMenuItem);
            // Cut
            ToolStripMenuItem cutMenuItem = new ToolStripMenuItem("Cut");
            cutMenuItem.ShortcutKeys = Keys.Control | Keys.X; // Set Ctrl+X as the shortcut key
            cutMenuItem.Click += (sender, e) => Cut();
            contextMenu.Items.Add(cutMenuItem);
            // Copy
            ToolStripMenuItem copyMenuItem = new ToolStripMenuItem("Copy");
            copyMenuItem.ShortcutKeys = Keys.Control | Keys.C; // Set Ctrl+C as the shortcut key
            copyMenuItem.Click += (sender, e) => Copy();
            contextMenu.Items.Add(copyMenuItem);
            // Paste
            ToolStripMenuItem pasteMenuItem = new ToolStripMenuItem("Paste");
            pasteMenuItem.ShortcutKeys = Keys.Control | Keys.V; // Set Ctrl+V as the shortcut key
            pasteMenuItem.Click += (sender, e) => Paste();
            contextMenu.Items.Add(pasteMenuItem);
            // Select All
            ToolStripMenuItem selectAllMenuItem = new ToolStripMenuItem("Select All");
            selectAllMenuItem.ShortcutKeys = Keys.Control | Keys.A; // Set Ctrl+A as the shortcut key
            selectAllMenuItem.Click += (sender, e) => SelectAll();
            contextMenu.Items.Add(selectAllMenuItem);
            return contextMenu;
        }
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            UpdateStyles();
            if (ContextMenuStrip == null)
            {
                ContextMenuStrip = CreateCustomContextMenu();
            }
        }
        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);
            // Force a repaint when leaving the control
            buffer = null;
            Invalidate();
        }
        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            buffer = null;
            Invalidate();
        }
        protected override void OnClick(EventArgs e)
        {
            base.OnClick(e);
            buffer = null;
            Invalidate();
        }
        protected override void OnDoubleClick(EventArgs e)
        {
            base.OnDoubleClick(e);
            buffer = null;
            Invalidate();
        }
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            buffer = null;
            Invalidate();
        }
        protected override void OnLocationChanged(EventArgs e)
        {
            buffer = null;
            base.OnLocationChanged(e);
        }
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            HideCaret(Handle);
            //SetFontSize((int)Font.Size);
            SetMultiline(true);
            SetFontFamilyAndStyle((string)Font.FontFamily.Name);
            SetTextAlignment(textAlign);
       }
        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            buffer = null;
            SetTextAlignment(textAlign);
            // Force a repaint when text changes
            UpdateStyles();
            Invalidate();
        }
       public void SetFontFamilyAndStyle(string fontFamilyName)
        {
            // Get the existing font style
            FontStyle currentFontStyle = Font.Style;
           // Create a new font with the specified font family and the existing font style
            Font newFont = new Font(fontFamilyName, Font.Size, currentFontStyle);
           // Set the new font
            SendMessage(Handle, WM_SETFONT, newFont.ToHfont(), (IntPtr)1);
            Font = newFont;
           // Redraw the control
            Invalidate();
        }
   }
}
 
Last edited:
Back
Top Bottom