Control snapping on resize in C# scrollable container

2.2k views Asked by At

I have a set of controls which I am stacking vertically inside a scrollable control.

Each control contains text (like message bubbles on an iPhone), which the bubble resizes based on the height of the text.

The problem I face, is when I resize the parent so it is smaller, the bubbles start to overlap, and when I resize so the bubbles are one-line, there is too much space in between each bubble.

What I would like to do, is to have each bubble snap the top of the bubble to 10pts off the bubble above it, the fastest way possible without any flicker (as there is presently no flicker on resize)

I have thought about embedding each control into another parent (eg, a grid control row), but then each bubble added would be responsible for resizing the parent of itself, and then anchors would no longer work for their top, left, and right positioning.

How can this be done ? (sorry, the details of the question are above as it can't really be worded into a simple one liner question due to the complexity and specifics)

Thanks in advance :)

AS REQUESTED, SCREENSHOTS and CODE

This is the view normally enter image description here After Resizing, then scrolling down to controls that weren't in visible segment enter image description here And resizing back, then scrolling back up enter image description here

Now the good stuff..... CODE.....

Here is the code for my custom control:

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;

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;
    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 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 ) {
        this.Invalidate();
        foreach ( Message m in messages ) {
            Messages.Remove( m );
        }
        RedrawControls();
    }

    public void Add( string Message, BubblePositionEnum Position ) {
        Message b = new Message(Position);

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

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

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

    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;
        SuspendLayout();
        foreach ( Message m in this.Controls ) {
            if ( count>0 ) {
                m.Top=last.Top+last.Height+_BubbleSpacing+AutoScrollPosition.Y;
                if ( VerticalScroll.Visible ) {
                    m.Width=new_width-( _BubbleIndent+_BubbleSpacing+SystemInformation.VerticalScrollBarWidth );
                } else {
                    m.Width=new_width-( _BubbleIndent+_BubbleSpacing );
                }
            }
            last=m;
            count++;
        }
        ResumeLayout();
        Invalidate();
    }

    public class Message : Control {
        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;
        }

        protected override void OnResize( System.EventArgs e ) {
            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();

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

        protected override void OnPaint( PaintEventArgs e ) {
            base.OnPaint( e );
            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 );

            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-25, Height-5 ) );
            } else {
                _G.DrawString( Text, Font, new SolidBrush( ForeColor ), new Rectangle( 5, 4, Width-25, 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();
        }
    }
}

And for my manifest :

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!-- UAC Manifest Options
            If you want to change the Windows User Account Control level replace the 
            requestedExecutionLevel node with one of the following.

        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />

            Specifying requestedExecutionLevel node will disable file and registry virtualization.
            If you want to utilize File and Registry Virtualization for backward 
            compatibility then delete the requestedExecutionLevel node.
        -->
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- A list of all Windows versions that this application is designed to work with. 
      Windows will automatically select the most compatible environment.-->

      <!-- If your application is designed to work with Windows Vista, uncomment the following supportedOS node-->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS>

      <!-- If your application is designed to work with Windows 7, uncomment the following supportedOS node-->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>

      <!-- If your application is designed to work with Windows 8, uncomment the following supportedOS node-->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS>

      <!-- If your application is designed to work with Windows 8.1, uncomment the following supportedOS node-->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>

    </application>
  </compatibility>

  <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
  <!-- <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
        />
    </dependentAssembly>
  </dependency>-->

  <asmv1:application>
    <asmv1:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv1:windowsSettings>
  </asmv1:application>
</asmv1:assembly>

And for the form itself to demo the control on

    int x = 0;
    while ( x<20 ) {
        messageControl1.Add( "Testing", MessageControl.BubblePositionEnum.Right );
        messageControl1.Add( "Testing", MessageControl.BubblePositionEnum.Right );
        messageControl1.Add( "Testing", MessageControl.BubblePositionEnum.Left );
        x++;
    }

The form is set to scale to DPI (which is correct, so change that when you edit to test, and use my manifest as that is DPI scaling, not Font scaling).

3

There are 3 answers

5
Juan Carlos Oropeza On

In this case I'm creating a grid to represent a labyrinth.

My form have a Panel control, then I create buttons inside calculating Top, Left values. For me button size is fixed, you could use your parent value.

I name the buttons grid0102 for row = "01"and col="02"
Then select Location, Size and Text properties.
And finally BackColor for walls

int buttonSize = 20;

Panel myPanel = (Panel)this.Controls["panelArea"];
string[] myGrid = getGrid(0);

for (int row = 0; row < r; row++)
{
     char[] rowChar = myGrid[row].ToCharArray();

     for (int col = 0; col < c; col++)
     {
        Button newButton = new Button();
        newButton.Name = "grid" + row.ToString("D3") + col.ToString("D3");
        newButton.Location = new Point { X = buttonSize * col, Y = buttonSize * row };
        newButton.Size = new Size { Width = buttonSize, Height = buttonSize };
        newButton.Text = rowChar[col].ToString();

        if (rowChar[col] == '%') newButton.BackColor = Color.Green;

        myPanel.Controls.Add(newButton);
        Debug.WriteLine(newButton.Location);
     }
  }

NOTE ADDED

But if the problem is handling resize, just encampsule that code in a function and call it when Resize event occurs.

    private void panelArea_Resize(object sender, EventArgs e)
    {
         UI_Resize();
    }

enter image description here

13
Juan Carlos Oropeza On

I think found it.

Just add this line in the Redraw function to realize only 6 object where being updated

Debug.WriteLine(m.Name + "-" + m.Top + "-" + m.Width);

-10-234
-58-217
-106-217
-154-217
-202-217
-250-217

first bug
And this line in the test method fix the creation process

messageControl1.SuspendLayout(); //add
while (x < 20)
{
    messageControl1.Add("Testing", MessageControl.BubblePositionEnum.Right);
    messageControl1.Add("Testing", MessageControl.BubblePositionEnum.Right);
    messageControl1.Add("Testing", MessageControl.BubblePositionEnum.Left);
    x++;
}
messageControl1.ResumeLayout(); //add
messageControl1.Invalidate();   //add

As you can see the scroll is at the end. enter image description here

second bug
Looks like hide elements have diferent size, you can see the debug result.
So I just save firt element Height and assign to everyone.

Debug.WriteLine("------------------------------------------------");
int firstHeight = 0;
foreach (Message m in this.Controls)
{
    if (count > 0)
    {
        Debug.WriteLine(m.Height);
        m.Height = firstHeight;
        m.Top = last.Top + firstHeight + _BubbleSpacing + AutoScrollPosition.Y;
        if (VerticalScroll.Visible)
        {
           m.Width = new_width - (_BubbleIndent + _BubbleSpacing + SystemInformation.VerticalScrollBarWidth);
        }
        else
        {
            m.Width = new_width - (_BubbleIndent + _BubbleSpacing);
        }
   }
   else
   {
      firstHeight = m.Height;
   }

   Debug.WriteLine(m.Name + "-" + m.Top + "-" + m.Width);
   last = m;
   count++;
}
2
Idle_Mind On

Here's what I'm talking about doing it via the FlowLayoutPanel. Take a close look at all of the code as I've made significant changes throughout. I recommend pasting this over a blank project to play with it.

The form when it initially loads:

Initial Form

The form after being resized smaller:

Resized Smaller

Here's the form now scrolled to the bottom to show that the FlowLayoutPanel has taken care of re-arranging everything for me:

Scrolled to the bottom

The re-worked code:

public partial class Form1 : Form
{

    public Form1()
    {
        this.InitializeComponent();
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
        this.UpdateStyles();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        for (int x = 0; x < 20; x++)
        {
            messageControl1.Add(x.ToString("00") + ": Testing testing testing ...", MessageControl.BubblePositionEnum.Right);
            messageControl1.Add(x.ToString("00") + ": Testing with variable length strings.  This one is longer!", MessageControl.BubblePositionEnum.Right);
            messageControl1.Add(x.ToString("00") + ": Testing is fun.", MessageControl.BubblePositionEnum.Left);
        }
    }

}

public class MessageControl : FlowLayoutPanel 
{

    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;
    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 MessageControl()
    {
        this.Messages = new List<Message>();
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
        this.UpdateStyles();
        this.BackColor = Color.Orange;
        this.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
        this.AutoScroll = true;
    }

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

    public void Remove(Message[] messages)
    {
        this.SuspendLayout();
        foreach (Message m in messages)
        {
            Messages.Remove(m);
            this.Controls.Remove(m);
        }
        this.ResumeLayout();
        this.Invalidate();
        this.Refresh();
    }

    public void Add(string Message, BubblePositionEnum Position)
    {
        Message b = new Message(this, Message, Position);
        b.DrawBubbleArrow = _DrawArrow;
        b.Width = this.ClientSize.Width;
        Messages.Add(b);
        this.Controls.Add(b);
    }

    protected override void OnLayout(LayoutEventArgs levent)
    {
        this.ResizeMessages();
        base.OnLayout(levent);
    }

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

    private void ResizeMessages()
    {
        this.SuspendLayout();
        foreach (Message m in this.Messages)
        {
            m.Width = this.ClientSize.Width - 9;
        }
        this.ResumeLayout();
    }

    public class Message : Control
    {

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

        private Message() { }

        public Message(MessageControl mc, string Message, BubblePositionEnum Position)
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true);
            this.UpdateStyles();

            this._mc = mc;
            this._BubblePosition = Position;
            this.Text = Message;
            this.BubbleColor = (Position == BubblePositionEnum.Right ? mc.RightBubbleColor : mc.LeftBubbleColor);
            this.BackColor = this.BubbleColor;
            this.ForeColor = (Position == BubblePositionEnum.Right ? mc.RightBubbleTextColor : mc.LeftBubbleTextColor);
            this.Font = new Font("Segoe UI", 10);

            this.Size = new Size(152, 38);
            this.Anchor = AnchorStyles.Left | AnchorStyles.Right;
        }

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

            Shape = new GraphicsPath();
            if (BubblePosition == BubblePositionEnum.Left)
            {
                Shape.AddArc(9, 0, 10, 10, 180, 90);
                Shape.AddArc(Width - 10 - this._mc.BubbleIndent, 0, 10, 10, -90, 90);
                Shape.AddArc(Width - 10 - this._mc.BubbleIndent, Height - 11, 10, 10, 0, 90);
                Shape.AddArc(9, Height - 11, 10, 10, 90, 90);
            }
            else
            {
                Shape.AddArc(this._mc._BubbleIndent, 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(this._mc._BubbleIndent, Height - 11, 10, 10, 90, 90);
            }

            if (_DrawBubbleArrow == true)
            {
                Point[] p;
                if (_BubblePosition == BubblePositionEnum.Left)
                {
                    p = new Point[] {
                        new Point(9, 9),
                        new Point(0, 15),
                        new Point(9, 20)
                    };                      
                }
                else
                {
                    p = new Point[] {
                        new Point(Width - 8, 9),
                        new Point(Width, 15),
                        new Point(Width - 8, 20)
                    };
                }
                Shape.AddPolygon(p);
            }

            Shape.CloseAllFigures();
            this.Region = new Region(Shape);

            this.Invalidate();
            this.Refresh();
        }

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

            var G = e.Graphics;
            int RenderWidth = this.Width - 10 - this._mc.BubbleIndent;
            SizeF s = G.MeasureString(Text, Font, RenderWidth);
            this.Height = (int)(Math.Floor(s.Height) + 10);

            G.SmoothingMode = SmoothingMode.HighQuality;
            G.PixelOffsetMode = PixelOffsetMode.HighQuality;
            G.InterpolationMode = InterpolationMode.HighQualityBicubic;

            // Draw the string specified in 'Text' property
            using (SolidBrush brush = new SolidBrush(this.ForeColor))
            {
                if (_BubblePosition == BubblePositionEnum.Left)
                {
                    G.DrawString(Text, Font, brush, new Rectangle(13, 4, RenderWidth, Height - 5));
                }
                else
                {
                    G.DrawString(Text, Font, brush, new Rectangle(this._mc.BubbleIndent + 5, 4, RenderWidth, Height - 5));
                }
            }
        }

    }

}