Transparent RichTextBox with Graphic not showing Text

1.3k views Asked by At

I have the following code working, and commented out the Text being drawn onto the control as I started out using this as a graphic only control. Moving on, I have switched the control to be RichTextBox instead of Control so that I can have all the goodies that come with it such as clickable hyperlinks, selectable text, and so on.

The problem I face, is the text can be selected, and even copied, but it is invisible, and is also not positioned exactly where I would like it (uncomment the text print lines in the Messages.OnPaint() method to see how I wish the text to appear)

PRETTY PICTURE OF PROBLEM enter image description here

PRETTY PICTURE OF WHAT I AM TRYING TO ACCOMPLISH enter image description here

PRETTY PICTURE OF WHAT IT LOOKS LIKE WITH THE TEXT PRINT CODE UNCOMMENTED enter image description here

THE CODE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using System.Diagnostics;

public class MessageControl : ScrollableControl {

    public List<Message> Messages { get; private set; }

    private Color _LeftBubbleColor=Color.FromArgb(217,217,217);
    private Color _RightBubbleColor=Color.FromArgb(192,206,215);
    private Color _LeftBubbleTextColor=Color.FromArgb(52,52,52);
    private Color _RightBubbleTextColor=Color.FromArgb( 52, 52, 52 );
    private bool _DrawArrow=true;
    private int _BubbleIndent=40;
    private int _BubbleSpacing=10;

    private int _TruncateHistory=0;

    public enum BubblePositionEnum { Left, Right }

    public Color LeftBubbleColor { get { return _LeftBubbleColor; } set {_LeftBubbleColor = value; } }
    public Color RightBubbleColor { get { return _RightBubbleColor; } set { _RightBubbleColor=value; } }
    public Color LeftBubbleTextColor { get { return _LeftBubbleTextColor; } set { _LeftBubbleTextColor=value; } }
    public Color RightBubbleTextColor { get { return _RightBubbleTextColor; } set { _RightBubbleTextColor=value; } }
    public int BubbleIndent { get { return _BubbleIndent; } set { _BubbleIndent = value; } }
    public int BubbleSpacing { get { return _BubbleSpacing; } set { _BubbleSpacing=value; } }
    public bool DrawArrow { get { return _DrawArrow; } set { _DrawArrow = value; } }
    public int TruncatHistory { get { return _TruncateHistory; } set { _TruncateHistory = value; } }

    public MessageControl() {
        Messages = new List<Message>();
        SetStyle( ControlStyles.AllPaintingInWmPaint|ControlStyles.OptimizedDoubleBuffer|ControlStyles.ResizeRedraw|ControlStyles.SupportsTransparentBackColor|ControlStyles.UserPaint, true );
        DoubleBuffered=true;
        BackColor=Color.Orange;
        Anchor=AnchorStyles.Top|AnchorStyles.Left|AnchorStyles.Right|AnchorStyles.Bottom;
        AutoScroll=true;
    }

    public void Remove( Message message ) {
        this.Invalidate();
        Messages.Remove( message );
        RedrawControls();
    }

    public void Remove( Message[] messages ) {
        foreach ( Message m in messages ) {
            Messages.Remove( m );
        }
        RedrawControls();
        this.Invalidate();
    }

    public void Add( string Message, BubblePositionEnum Position ) {
        if ( Messages.Count>0 ) {
            Message m = Messages[Messages.Count-1];
            if ( m.BubblePosition==Position ) {
                Message=m.Text+"\n"+Message;
                Remove(m);
            }
        }

        Message b = new Message(Position);
        if ( Messages.Count>0 ) {
            b.Top=Messages[Messages.Count-1].Top+Messages[Messages.Count-1].Height+_BubbleSpacing;
        } else {
            b.Top=_BubbleSpacing;
        }

        b.Text = Message;
        b.DrawBubbleArrow=_DrawArrow;

        if ( VerticalScroll.Visible ) {
            b.Width=Width-( _BubbleIndent+_BubbleSpacing+SystemInformation.VerticalScrollBarWidth );
        } else {
            b.Width=Width-( _BubbleIndent+_BubbleSpacing );
        }
        if ( Position==BubblePositionEnum.Right ) {
            b.Left = _BubbleIndent;
            b.BubbleColor = _RightBubbleColor;
            b.ForeColor = _RightBubbleTextColor;
        } else {
            b.Left = _BubbleSpacing;
            b.BubbleColor=_LeftBubbleColor;
            b.ForeColor=_LeftBubbleTextColor;
        }

        this.Messages.Add(b);
        this.Controls.Add(b);

        if ( Messages.Count>_TruncateHistory&&_TruncateHistory>0 ) {
            Remove( Messages[0] );
            this.Invalidate();
        }
        base.ScrollControlIntoView(this.Controls[Controls.Count-1]);
    }

    public void Truncate( int count ) {
        if ( count>=( Controls.Count-1 ) ) {
//          Controls.Clear();
            Messages.Clear();
        } else if ( count>1 ) {
            int x=0;
            while ( x<count&&x<=Controls.Count ) {
                Messages.RemoveAt( 0 );
                x++;
            }
        } else {
            Messages.RemoveAt(0);
        }
        this.Invalidate();
        RedrawControls();

    }

    protected override void OnResize( System.EventArgs e ) {
        RedrawControls();
        base.OnResize( e );
    }

    private void RedrawControls() {
        int count=0;
        Message last=null;
        int new_width=this.Width;
        this.Controls.Clear();
        VerticalScroll.Visible = false;
        this.SuspendLayout();
        foreach ( Message m in this.Messages ) {
            if ( count>0 ) {
                m.Top=last.Top+last.Height+_BubbleSpacing;
                if ( VerticalScroll.Visible ) {
                    m.Width=new_width-( _BubbleIndent+_BubbleSpacing+SystemInformation.VerticalScrollBarWidth );
                } else {
                    m.Width=new_width-( _BubbleIndent+_BubbleSpacing );
                }
            } else {
                m.Top=_BubbleSpacing;
                if ( VerticalScroll.Visible ) {
                    m.Width=new_width-( _BubbleIndent+_BubbleSpacing+SystemInformation.VerticalScrollBarWidth );
                } else {
                    m.Width=new_width-( _BubbleIndent+_BubbleSpacing );
                }
            }
            last=m;
            count++;
        }
        this.Controls.AddRange(Messages.ToArray<Message>());
        if ( this.Controls.Count>0 ) {
            base.ScrollControlIntoView( this.Controls[Controls.Count-1] );
        }
        this.ResumeLayout();
    }

    public class Message : RichTextBox {
        private GraphicsPath Shape;
        private Color _TextColor=Color.FromArgb( 52, 52, 52 );
        private Color _BubbleColor=Color.FromArgb( 217, 217, 217 );
        private bool _DrawBubbleArrow=true;
        private BubblePositionEnum _BubblePosition = BubblePositionEnum.Left;

        public override Color ForeColor { get { return this._TextColor; } set { this._TextColor=value; this.Invalidate(); } }
        public BubblePositionEnum BubblePosition { get { return this._BubblePosition; } set { this._BubblePosition=value; this.Invalidate(); } }
        public Color BubbleColor { get { return this._BubbleColor; } set { this._BubbleColor=value; this.Invalidate(); } }
        public bool DrawBubbleArrow { get { return _DrawBubbleArrow; } set { _DrawBubbleArrow=value; Invalidate(); } }
        public Message(BubblePositionEnum Position) {
            _BubblePosition=Position;
            SetStyle( ControlStyles.AllPaintingInWmPaint|ControlStyles.OptimizedDoubleBuffer|ControlStyles.ResizeRedraw|ControlStyles.SupportsTransparentBackColor|ControlStyles.UserPaint, true );
            DoubleBuffered=true;
            Size=new Size( 152, 38 );
            BackColor=Color.Transparent;
            ForeColor=Color.FromArgb( 52, 52, 52 );
            Font=new Font( "Segoe UI", 10 );
            Anchor=AnchorStyles.Top|AnchorStyles.Left|AnchorStyles.Right;
            BorderStyle=System.Windows.Forms.BorderStyle.None;
            ScrollBars = RichTextBoxScrollBars.None;
        }

        [DllImport( "kernel32.dll", CharSet=CharSet.Auto )]
        static extern IntPtr LoadLibrary( string lpFileName );

        protected override CreateParams CreateParams {
            get {
                CreateParams prams=base.CreateParams;
                if ( LoadLibrary( "msftedit.dll" )!=IntPtr.Zero ) {
                    prams.ExStyle|=0x020; // transparent  
                    prams.ClassName="RICHEDIT50W";
                }
                return prams;
            }
        }

        protected override void OnResize( System.EventArgs e ) {
            SuspendLayout();
            Shape=new GraphicsPath();

            var _Shape=Shape;
            if ( BubblePosition==BubblePositionEnum.Left ) {
                _Shape.AddArc( 9, 0, 10, 10, 180, 90 );
                _Shape.AddArc( Width-11, 0, 10, 10, -90, 90 );
                _Shape.AddArc( Width-11, Height-11, 10, 10, 0, 90 );
                _Shape.AddArc( 9, Height-11, 10, 10, 90, 90 );
            } else {
                _Shape.AddArc( 0, 0, 10, 10, 180, 90 );
                _Shape.AddArc( Width-18, 0, 10, 10, -90, 90 );
                _Shape.AddArc( Width-18, Height-11, 10, 10, 0, 90 );
                _Shape.AddArc( 0, Height-11, 10, 10, 90, 90 );
            }
            _Shape.CloseAllFigures();

            Bitmap B=new Bitmap( this.Width, this.Height );
            Graphics G=Graphics.FromImage( B );

            SizeF s=G.MeasureString( Text, Font, Width-25 );
            this.Height=(int)( Math.Floor( s.Height )+10 );

            ResumeLayout();
            //Invalidate();
            base.OnResize( e );
        }

        protected override void OnClick( EventArgs e ) {
            //MessageBox.Show( base.Height.ToString() );
            base.OnClick( e );
        }

        protected override void OnPaint( PaintEventArgs e ) {
            base.OnPaint( e );
            Bitmap B=new Bitmap( this.Width, this.Height );
            Graphics G=Graphics.FromImage( B );

            B=new Bitmap( this.Width, this.Height );
            G=Graphics.FromImage( B );
            var _G=G;

            _G.SmoothingMode=SmoothingMode.HighQuality;
            _G.PixelOffsetMode=PixelOffsetMode.HighQuality;
            _G.Clear( BackColor );

            // Fill the body of the bubble with the specified color
            _G.FillPath( new SolidBrush( _BubbleColor ), Shape );
            // Draw the string specified in 'Text' property
//          if ( _BubblePosition==BubblePositionEnum.Left ) {
//              _G.DrawString( Text, Font, new SolidBrush( ForeColor ), new Rectangle( 13, 4, Width-19, Height-5 ) );
//          } else {
//              _G.DrawString( Text, Font, new SolidBrush( ForeColor ), new Rectangle( 5, 4, Width-19, Height-5 ) );
//          }

            // Draw a polygon on the right side of the bubble
            if ( _DrawBubbleArrow==true ) {
                if(_BubblePosition == BubblePositionEnum.Left) {
                    Point[] p = {
                        new Point(9, 9),
                        new Point(0, 15),
                        new Point(9, 20)
                   };
                    _G.FillPolygon( new SolidBrush( _BubbleColor ), p );
                    _G.DrawPolygon( new Pen( new SolidBrush( _BubbleColor ) ), p );
                } else {
                    Point[] p = {
                        new Point(Width - 8, 9),
                        new Point(Width, 15),
                        new Point(Width - 8, 20)
                    };
                    _G.FillPolygon( new SolidBrush( _BubbleColor ), p );
                    _G.DrawPolygon( new Pen( new SolidBrush( _BubbleColor ) ), p );
                }
            }
            G.Dispose();
            e.Graphics.InterpolationMode=InterpolationMode.HighQualityBicubic;
            e.Graphics.DrawImageUnscaled( B, 0, 0 );
            B.Dispose();
        }
    }
}

Just in case it wasn't clear, I am not looking to paint the text on as you can see I have no problems with this. I wish to bring the natural text forward over the graphic while maintaining all the capabilities of using a RichTextBox control such as

  • Clickable Links
  • Inline font-style changes
  • Possibility of embedding emotes (images)
  • Fully selectable text
  • Right-click options
  • other things not listed, but are super cool to have in a chat/message control.

(for those of you bearing with this as I bring this to life, due to community support, I will be releasing the final version of this control back into the community -- highly dependant on progression ^^ -- as you can tell, the brunt of the work is done, so aside from this minute detail, everything should be in order to add all the other goodies. )

2

There are 2 answers

2
Dialecticus On BEST ANSWER

Instead of drawing stuff in OnPaint of control derived from RichTextBox it's better to draw stuff around the text box, on user control that just contains the text box, and let the text box draws itself.

0
Amr Omran On

Here is the updated code tried several ways to make it work

Clickable Links Solved Inline font-style changes you can make get and set The possibility of embedding emotes (images) still pending Fully selectable text Solved Right-click options Solved


using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using static MessageNewControl;


public class MessageNewControl : ScrollableControl
{

    public List<MessageNew> MessageNews { get; private set; }

    private Color _LeftBubbleColor = Color.FromArgb(217, 217, 217);
    private Color _RightBubbleColor = Color.FromArgb(192, 206, 215);
    private Color _LeftBubbleTextColor = Color.FromArgb(52, 52, 52);
    private Color _RightBubbleTextColor = Color.FromArgb(52, 52, 52);
    private bool _DrawArrow = true;
    private int _BubbleIndent = 40;
    private int _BubbleSpacing = 10;

    private int _TruncateHistory = 0;

    public enum BubblePositionEnum { Left, Right }

    public Color LeftBubbleColor { get { return _LeftBubbleColor; } set { _LeftBubbleColor = value; } }
    public Color RightBubbleColor { get { return _RightBubbleColor; } set { _RightBubbleColor = value; } }
    public Color LeftBubbleTextColor { get { return _LeftBubbleTextColor; } set { _LeftBubbleTextColor = value; } }
    public Color RightBubbleTextColor { get { return _RightBubbleTextColor; } set { _RightBubbleTextColor = value; } }
    public int BubbleIndent { get { return _BubbleIndent; } set { _BubbleIndent = value; } }
    public int BubbleSpacing { get { return _BubbleSpacing; } set { _BubbleSpacing = value; } }
    public bool DrawArrow { get { return _DrawArrow; } set { _DrawArrow = value; } }
    public int TruncatHistory { get { return _TruncateHistory; } set { _TruncateHistory = value; } }

    public MessageNewControl()
    {
        MessageNews = new List<MessageNew>();
        SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
        DoubleBuffered = true;
        BackColor = Color.FromArgb(128,Color.Orange);
        Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
        AutoScroll = true;

    }

    public void Remove(MessageNew message)
    {
        this.Invalidate();
        MessageNews.Remove(message);
        RedrawControls();
    }

    public void Remove(MessageNew[] messages)
    {
        foreach (MessageNew m in messages)
        {
            MessageNews.Remove(m);
        }
        RedrawControls();
        this.Invalidate();
    }

    public void Add(string MessageNew, BubblePositionEnum Position)
    {
        if (MessageNews.Count > 0)
        {
            MessageNew m = MessageNews[MessageNews.Count - 1];
            if (m.BubblePosition == Position)
            {
                MessageNew = m.Text + "\n" + MessageNew;
                Remove(m);
            }
        }

        MessageNew b = new MessageNew(Position);


        if (MessageNews.Count > 0)
        {
            b.Top = MessageNews[MessageNews.Count - 1].Top + MessageNews[MessageNews.Count - 1].Height + _BubbleSpacing;
        }
        else
        {
            b.Top = _BubbleSpacing;

        }

        b.Text = MessageNew;
        b.DrawBubbleArrow = _DrawArrow;

        if (VerticalScroll.Visible)
        {
            b.Width = Width - (_BubbleIndent + _BubbleSpacing + SystemInformation.VerticalScrollBarWidth);
        }
        else
        {
            b.Width = Width - (_BubbleIndent + _BubbleSpacing);
        }



        if (Position == BubblePositionEnum.Right)
        {
            b.Left = _BubbleIndent;
            b.BubbleColor = _RightBubbleColor;
            b.ForeColor = _RightBubbleTextColor;
        }
        else
        {
            b.Left = _BubbleSpacing;
            b.BubbleColor = _LeftBubbleColor;
            b.ForeColor = _LeftBubbleTextColor;
        }





        this.MessageNews.Add(b);
        this.Controls.Add(b);

        if (MessageNews.Count > _TruncateHistory && _TruncateHistory > 0)
        {
            Remove(MessageNews[0]);
            this.Invalidate();
        }
        base.ScrollControlIntoView(this.Controls[Controls.Count - 1]);
    }





    public void Truncate(int count)
    {
        if (count >= (Controls.Count - 1))
        {
            //          Controls.Clear();
            MessageNews.Clear();
        }
        else if (count > 1)
        {
            int x = 0;
            while (x < count && x <= Controls.Count)
            {
                MessageNews.RemoveAt(0);
                x++;
            }
        }
        else
        {
            MessageNews.RemoveAt(0);
        }
        this.Invalidate();
        RedrawControls();

    }

    protected override void OnResize(System.EventArgs e)
    {
        RedrawControls();
        base.OnResize(e);
    }

    private void RedrawControls()
    {
        int count = 0;
        MessageNew last = null;
        int new_width = this.Width;
        this.Controls.Clear();
        VerticalScroll.Visible = false;
        this.SuspendLayout();
        foreach (MessageNew m in this.MessageNews)
        {
            if (count > 0)
            {
                m.Top = last.Top + last.Height + _BubbleSpacing;
                if (VerticalScroll.Visible)
                {
                    m.Width = new_width - (_BubbleIndent + _BubbleSpacing + SystemInformation.VerticalScrollBarWidth);
                }
                else
                {
                    m.Width = new_width - (_BubbleIndent + _BubbleSpacing);
                }
            }
            else
            {
                m.Top = _BubbleSpacing;
                if (VerticalScroll.Visible)
                {
                    m.Width = new_width - (_BubbleIndent + _BubbleSpacing + SystemInformation.VerticalScrollBarWidth);
                }
                else
                {
                    m.Width = new_width - (_BubbleIndent + _BubbleSpacing);
                }
            }
            last = m;
            count++;
        }
        this.Controls.AddRange(MessageNews.ToArray<MessageNew>());
        if (this.Controls.Count > 0)
        {
            base.ScrollControlIntoView(this.Controls[Controls.Count - 1]);
        }
        this.ResumeLayout();
    }

  
}



[ToolboxBitmap(typeof(RichTextBox))]

public class MessageNew : RichTextBox, IMessageFilter
{

    private GraphicsPath Shape;
    private Color _TextColor = Color.FromArgb(52, 152, 152);
    private Color _BubbleColor = Color.FromArgb(217, 217, 217);
    private bool _DrawBubbleArrow = true;
    private BubblePositionEnum _BubblePosition = BubblePositionEnum.Left;

    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == 0x20a) // WM_MOUSEWHEEL
        {
            OnMouseWheel(new MouseEventArgs(MouseButtons.None, 0, 0, 0, (short)((m.WParam.ToInt32() >> 16) & 0xffff)));
            return true;
        }
        return false;
    }

    protected override void OnLinkClicked(LinkClickedEventArgs e)
    {
        System.Diagnostics.Process.Start(e.LinkText);
    }


    public override Color ForeColor { get { return this._TextColor; } set { this._TextColor = value; this.Invalidate(); } }
    public BubblePositionEnum BubblePosition { get { return this._BubblePosition; } set { this._BubblePosition = value; this.Invalidate(); } }
    public Color BubbleColor { get { return this._BubbleColor; } set { this._BubbleColor = value; this.Invalidate(); } }
    public bool DrawBubbleArrow { get { return _DrawBubbleArrow; } set { _DrawBubbleArrow = value; Invalidate(); } }
    public MessageNew(BubblePositionEnum Position)
    {
        _BubblePosition = Position;
        SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
        DoubleBuffered = true;
        Size = new Size(152, 38);
        BackColor = Color.Transparent;
        ForeColor = Color.FromArgb(52, 52, 52);
        Font = new Font("Segoe UI", 10);
        Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
        BorderStyle = System.Windows.Forms.BorderStyle.None;
        ScrollBars = RichTextBoxScrollBars.None;

        this.SelectionChanged += MessageNew_SelectionChanged; // Hook up event handler

    }
    private void MessageNew_SelectionChanged(object sender, EventArgs e)
    {
        HighlightSelectedText();
    }
    private void HighlightSelectedText()
    {
        // Get the current selection start and length
        int start = this.SelectionStart;
        int length = this.SelectionLength;

        // Highlight the selected text
        this.SelectionBackColor = Color.Yellow;

        // Move the selection back to its original position
        this.SelectionStart = start;
        this.SelectionLength = length;
    }


    protected override void OnResize(System.EventArgs e)
    {
        SuspendLayout();
        Shape = new GraphicsPath();

        //var _Shape = Shape;
        //if (BubblePosition == BubblePositionEnum.Left)
        //{
        //    _Shape.AddArc(9, 0, 10, 10, 180, 90);
        //    _Shape.AddArc(Width - 11, 0, 10, 10, -90, 90);
        //    _Shape.AddArc(Width - 11, Height - 11, 10, 10, 0, 90);
        //    _Shape.AddArc(9, Height - 11, 10, 10, 90, 90);
        //}
        var _Shape = Shape;
        if (BubblePosition == BubblePositionEnum.Left)
        {
            _Shape.AddArc(0, 0, 10, 10, 180, 90);
            _Shape.AddArc(Width - 11, 0, 10, 10, -90, 90);
            _Shape.AddArc(Width - 11, Height - 11, 10, 10, 0, 90);
            _Shape.AddArc(0, Height - 11, 10, 10, 90, 90);
        }

        else
        {
            _Shape.AddArc(0, 0, 10, 10, 180, 90);
            _Shape.AddArc(Width - 18, 0, 10, 10, -90, 90);
            _Shape.AddArc(Width - 18, Height - 11, 10, 10, 0, 90);
            _Shape.AddArc(0, Height - 11, 10, 10, 90, 90);
        }
        _Shape.CloseAllFigures();

        Bitmap B = new Bitmap(this.Width, this.Height);
        Graphics G = Graphics.FromImage(B);

        SizeF s = G.MeasureString(Text, Font, Width - 25);
        this.Height = (int)(Math.Floor(s.Height) + 10);
        ResumeLayout();
        //Invalidate();
        base.OnResize(e);
    }








    protected override void OnClick(EventArgs e)
    {
        //MessageNewBox.Show( base.Height.ToString() );
        base.OnClick(e);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        Bitmap B = new Bitmap(this.Width, this.Height);
        Graphics G = Graphics.FromImage(B);

        B = new Bitmap(this.Width, this.Height);
        G = Graphics.FromImage(B);
        var _G = G;

        _G.SmoothingMode = SmoothingMode.HighQuality;
        _G.PixelOffsetMode = PixelOffsetMode.HighQuality;
        _G.Clear(BackColor);

        // Fill the body of the bubble with the specified color
        _G.FillPath(new SolidBrush(_BubbleColor), Shape);

        ////Draw the string specified in 'Text' property
        //          if (_BubblePosition == BubblePositionEnum.Left)
        //{
        //    _G.DrawString(Text, Font, new SolidBrush(ForeColor), new Rectangle(13, 4, Width - 19, Height - 5));
        //}
        //else
        //{
        //    _G.DrawString(Text, Font, new SolidBrush(ForeColor), new Rectangle(5, 4, Width - 19, Height - 5));
        //}

        // Draw a polygon on the right side of the bubble
        if (_DrawBubbleArrow == true)
        {
            //if (_BubblePosition == BubblePositionEnum.Left)
            //{
            //    Point[] p = {
            //                new Point(9, 9),
            //                new Point(0, 15),
            //                new Point(9, 20)
            //           };
            //    _G.FillPolygon(new SolidBrush(Color.Black), p);
            //    _G.DrawPolygon(new Pen(new SolidBrush(_BubbleColor)), p);


            //}
            //else
            //{
            //    Point[] p = {
            //                new Point(Width - 8, 9),
            //                new Point(Width, 15),
            //                new Point(Width - 8, 20)
            //            };
            //    _G.FillPolygon(new SolidBrush(_BubbleColor), p);
            //    _G.DrawPolygon(new Pen(new SolidBrush(_BubbleColor)), p);
            //}
        }
        G.Dispose();
        e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        e.Graphics.DrawImageUnscaled(B, 0, 0);
        B.Dispose();


        // Draw highlight selection
        if (isHighlighting)
        {
            using (Brush brush = new SolidBrush(Color.FromArgb(128, Color.HotPink)))
            {
                e.Graphics.FillRectangle(brush, highlightRect);
            }
        }

        // Render text on a transparent background
        using (Brush brush = new SolidBrush(this.ForeColor))
        {

            e.Graphics.DrawString(this.Text, this.Font, brush, 0, 0);


        }

        // Draw hyperlinks
        foreach (LinkArea linkArea in GetLinkAreas())
        {
            string linkText = this.Text.Substring(linkArea.Start, linkArea.Length);
            RectangleF rect = GetTextRectangle(linkArea);
            e.Graphics.DrawString(linkText, this.Font, Brushes.Blue, rect);
        }

        base.OnPaint(e);

    }

    // --------------------To Open Links browser 

    private IEnumerable<LinkArea> GetLinkAreas()
    {
        List<LinkArea> linkAreas = new List<LinkArea>();

        // Implement the logic to identify link areas in the text
        // For example:
        int startIndex = this.Text.IndexOf("http://");
        while (startIndex != -1)
        {
            int endIndex = this.Text.IndexOf(" ", startIndex);
            if (endIndex == -1)
                endIndex = this.Text.Length - 1;

            LinkArea linkArea = new LinkArea(startIndex, endIndex - startIndex);
            linkAreas.Add(linkArea);

            startIndex = this.Text.IndexOf("http://", endIndex);
        }

        return linkAreas;
    }

    private RectangleF GetTextRectangle(LinkArea linkArea)
    {
        // Get the position of the start and end characters of the link area
        PointF startPoint = GetPositionFromCharIndex(linkArea.Start);
        PointF endPoint = GetPositionFromCharIndex(linkArea.Start + linkArea.Length);

        // Calculate the width and height of the rectangle
        float width = endPoint.X - startPoint.X;
        float height = endPoint.Y - startPoint.Y;

        // Create and return the rectangle
        return new RectangleF(startPoint.X, startPoint.Y, width, height);
    }



    private const int WS_EX_TRANSPARENT = 0x20;

    [DllImport("user32.dll")]
    private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    [DllImport("user32.dll")]
    private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    public MessageNew()
    {
        SetStyle(
                 ControlStyles.SupportsTransparentBackColor |
                 ControlStyles.AllPaintingInWmPaint |
                 ControlStyles.OptimizedDoubleBuffer, true);
        //SetStyle(ControlStyles.UserPaint, true);
        BackColor = Color.Transparent;
        Font = new Font(Font.FontFamily, 20);

    }

    //---------------------To Make It transparent 

    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibrary(string lpFileName);
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
            if (LoadLibrary("msftedit.dll") != IntPtr.Zero)
            {
                cp.ClassName = "RICHEDIT50W"; //-----------behave like richtextbox  If you want it to be TextBox change "RICHEDIT50W" to "EDIT"
            }
            else if (LoadLibrary("riched20.dll") != IntPtr.Zero)
            {
                cp.ClassName = "RichEdit20W";
            }
            return cp;
        }
    }


    //----------------------------For Text Selection 

    private const int EM_GETSEL = 0xB0;
    private const int EM_GETRECT = 0xB2;
    private const int EM_SETRECT = 0xB3;
    private const int EM_HIDESELECTION = 0x201;
    private const int EM_EXGETSEL = 0x00B8;
    private const int EM_EXSETSEL = 0x00B1;

    private bool isHighlighting = false;
    private Rectangle highlightRect;

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        switch (m.Msg)
        {
            case EM_GETRECT:
                RECT rect = (RECT)m.GetLParam(typeof(RECT));
                if (isHighlighting)
                {
                    rect.Top = highlightRect.Top;
                    rect.Bottom = highlightRect.Bottom;
                    Marshal.StructureToPtr(rect, m.LParam, true);
                }
                break;
        }
    }

    protected override void OnSelectionChanged(EventArgs e)
    {
        base.OnSelectionChanged(e);

        if (SelectionLength > 0)
        {
            isHighlighting = true;
            int start = SelectionStart;
            int end = start + SelectionLength;
            Point startPt = GetPositionFromCharIndex(start);
            Point endPt = GetPositionFromCharIndex(end);

            highlightRect = new Rectangle(startPt, new Size(endPt.X - startPt.X, Font.Height));
            Invalidate();
        }
        else
        {
            isHighlighting = false;
            Invalidate();
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    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();
        }
    }

}


and for using

using System;
using System.Windows.Forms;

namespace testApp
{
    public partial class Form4 : Form
    {

        public Form4()
        {
            InitializeComponent();

        }

        private void button1_Click(object sender, EventArgs e)
        {
            // You can add a message to the right side
            messageNewControl1.Add("This is a right side message  https://www.youtube.com/@Amro-Omran  "+"\n"+ "Please Like And subscribe For More useful Codes", MessageNewControl.BubblePositionEnum.Right);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // You can add a message to the left side
            messageNewControl1.Add("This is a left side message ", MessageNewControl.BubblePositionEnum.Left);


        }

        private void button4_Click(object sender, EventArgs e)
        {
                        // You can add a message to the left side
            messageNewControl1.Truncate(0);


        }

        private void button3_Click(object sender, EventArgs e)
        {
            // Define the index of the message you want to remove
            int index = 0; // Replace 0 with the actual index you want to remove

            // Check if the index is within the valid range before attempting to remove the message
            if (index >= 0 && index < messageNewControl1.MessageNews.Count)
            {
                // Remove the message at the specified index
                messageNewControl1.Remove(messageNewControl1.MessageNews[index]);
            }
            else
            {
                // Handle the case where the index is out of range
                Console.WriteLine("Index is out of range.");
            }
        }

        private void Form4_Load(object sender, EventArgs e)
        {
            
        }
    }
}

namespace testApp
{
    partial class Form4
    {
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form4));
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.button3 = new System.Windows.Forms.Button();
            this.button4 = new System.Windows.Forms.Button();
            this.messageNewControl1 = new MessageNewControl();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
            this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
            this.button1.ForeColor = System.Drawing.Color.Cornsilk;
            this.button1.Location = new System.Drawing.Point(75, 353);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(115, 30);
            this.button1.TabIndex = 1;
            this.button1.Text = "Add Right";
            this.button1.UseVisualStyleBackColor = false;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
            this.button2.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
            this.button2.ForeColor = System.Drawing.Color.Cornsilk;
            this.button2.Location = new System.Drawing.Point(228, 353);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(115, 30);
            this.button2.TabIndex = 3;
            this.button2.Text = "Add Left";
            this.button2.UseVisualStyleBackColor = false;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // button3
            // 
            this.button3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
            this.button3.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
            this.button3.ForeColor = System.Drawing.Color.Cornsilk;
            this.button3.Location = new System.Drawing.Point(549, 353);
            this.button3.Name = "button3";
            this.button3.Size = new System.Drawing.Size(145, 30);
            this.button3.TabIndex = 5;
            this.button3.Text = "Remove By Index";
            this.button3.UseVisualStyleBackColor = false;
            this.button3.Click += new System.EventHandler(this.button3_Click);
            // 
            // button4
            // 
            this.button4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
            this.button4.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
            this.button4.ForeColor = System.Drawing.Color.Cornsilk;
            this.button4.Location = new System.Drawing.Point(381, 353);
            this.button4.Name = "button4";
            this.button4.Size = new System.Drawing.Size(130, 30);
            this.button4.TabIndex = 4;
            this.button4.Text = "Remove Index 0";
            this.button4.UseVisualStyleBackColor = false;
            this.button4.Click += new System.EventHandler(this.button4_Click);
            // 
            // messageNewControl1
            // 
            this.messageNewControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.messageNewControl1.AutoScroll = true;
            this.messageNewControl1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(200)))), ((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(255)))));
            this.messageNewControl1.BubbleIndent = 40;
            this.messageNewControl1.BubbleSpacing = 10;
            this.messageNewControl1.DrawArrow = true;
            this.messageNewControl1.Font = new System.Drawing.Font("Tahoma", 20F);
            this.messageNewControl1.LeftBubbleColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))));
            this.messageNewControl1.LeftBubbleTextColor = System.Drawing.Color.White;
            this.messageNewControl1.Location = new System.Drawing.Point(32, 27);
            this.messageNewControl1.Name = "messageNewControl1";
            this.messageNewControl1.RightBubbleColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(255)))), ((int)(((byte)(128)))), ((int)(((byte)(0)))));
            this.messageNewControl1.RightBubbleTextColor = System.Drawing.Color.FromArgb(((int)(((byte)(128)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(255)))));
            this.messageNewControl1.Size = new System.Drawing.Size(692, 277);
            this.messageNewControl1.TabIndex = 8;
            this.messageNewControl1.Text = "messageNewControl1";
            this.messageNewControl1.TruncatHistory = 0;
            // 
            // Form4
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.ControlLightLight;
            this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
            this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
            this.ClientSize = new System.Drawing.Size(770, 456);
            this.Controls.Add(this.messageNewControl1);
            this.Controls.Add(this.button3);
            this.Controls.Add(this.button4);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Name = "Form4";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "Form4";
            this.Load += new System.EventHandler(this.Form4_Load);
            this.ResumeLayout(false);

        }

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.Button button3;
        private System.Windows.Forms.Button button4;
        private MessageNewControl messageNewControl1;
    }
}


Result enter image description here