Click here to Skip to main content
15,885,757 members
Articles / Programming Languages / C#

A .NET Wizard control

Rate me:
Please Sign up or sign in to vote.
4.86/5 (89 votes)
24 Apr 2003CPOL7 min read 660.4K   8.7K   216  
A .NET Wizard control for the VS.IDE and client apps
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Windows.Forms;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Text;
using System.Text;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;

using UtilityLibrary.Win32;


namespace UtilityLibrary.Notification
{
  [ ProvideProperty( "ToolTip", typeof( Control ) ),
  ToolboxBitmap( typeof( UtilityLibrary.Notification.ToolTipEx ), "UtilityLibrary.Notification.ToolTipEx.bmp" ), 
  ToolboxItem( true ) ]
  public class ToolTipEx : Component, IExtenderProvider
  {
    #region Class constants
    private const int     DEFAULT_DELAY = 500;
    private const int     RESHOW_RATIO  = 100;
    private const int     AUTOPOP_RATIO = 5000;    

    enum TToolAction
    {
      INSERT,
      REMOVE,
      UPDATE
    };
    #endregion

    #region Class members
    private IDictionary m_parents  = new Hashtable();
    private IDictionary m_controls = new Hashtable();
    
    private bool  m_bActive           = true;
    private bool  m_bShowAlways       = false;
    private int   m_iReshowDelay      = RESHOW_RATIO;
    private int   m_iInitialDelay     = DEFAULT_DELAY;
    private int   m_iAutoPopDelay     = AUTOPOP_RATIO;
    private int   m_iAutomaticDelay   = 500;

    private ToolTipWrapper  m_tooltip;
    private Regex           m_regex = new Regex( @"([^<]*)<(.*)>(.*)</\2>" );
    private StringFormat    m_format;
    #endregion

    #region Class events
    [ Category( "Property Changed" ) ]
    public event EventHandler ActiveChanged;
    [ Category( "Property Changed" ) ]
    public event EventHandler AutomaticDelayChanged;
    [ Category( "Property Changed" ) ]
    public event EventHandler AutoPopDelayChanged;
    [ Category( "Property Changed" ) ]
    public event EventHandler InitialDelayChanged;
    [ Category( "Property Changed" ) ]
    public event EventHandler ReshowDelayChanged;
    [ Category( "Property Changed" ) ]
    public event EventHandler ShowAlwaysChanged;
    [ Category( "Actions" ) ]
    public event EventHandler OnShow;
    [ Category( "Actions" ) ]
    public event EventHandler OnHide;
    #endregion

    #region Class Properties
    /// <summary>
    /// Gets or sets a value indicating whether the ToolTip is currently active
    /// </summary>
    [ Category( "Behaviour" ), 
    DefaultValue( true ), 
    Description( "Gets or sets a value indicating whether the ToolTip is currently active" ) ]
    public bool Active
    {
      get
      {
        return m_bActive;
      }
      set
      {
        if( value != m_bActive )
        {
          m_bActive = value;
          OnActiveChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the automatic delay for the ToolTip.
    /// </summary>
    [ Category( "Behaviour" ), 
    DefaultValue( 500 ),
    RefreshProperties( RefreshProperties.All ),
    Description( "Gets or sets the automatic delay for the ToolTip." ) ]
    public int AutomaticDelay
    {
      get
      {
        return m_iAutomaticDelay;
      }
      set
      {
        if( value != m_iAutomaticDelay )
        {
          m_iAutomaticDelay = value;
          OnAutomaticDelayChanged();
        }
      }
    }
    
    /// <summary>
    /// Gets or sets the period of time the ToolTip remains visible if 
    /// the mouse pointer is stationary within a control with specified 
    /// ToolTip text.
    /// </summary>
    [ Category( "Behaviour" ), 
    DefaultValue( 5000 ), 
    Description( "Gets or sets the period of time the ToolTip remains visible if the mouse pointer is stationary within a control with specified ToolTip text." ) ]
    public int AutoPopDelay
    {
      get
      {
        return m_iAutoPopDelay;
      }
      set
      {
        if( value != m_iAutoPopDelay )
        {
          m_iAutoPopDelay = value;
          OnAutoPopDelayChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the time that passes before the ToolTip appears.
    /// </summary>
    [ Category( "Behaviour" ), 
    DefaultValue( 500 ), 
    Description( "Gets or sets the time that passes before the ToolTip appears" ) ]
    public int InitialDelay
    {
      get
      {
        return m_iInitialDelay;
      }
      set
      {
        if( value != m_iInitialDelay )
        {
          m_iInitialDelay = value;
          OnInitialDelayChanged();
        }
      }
    }

    /// <summary>
    /// Gets or sets the length of time that must transpire before subsequent 
    /// ToolTip windows appear as the mouse pointer moves from one control 
    /// to another.
    /// </summary>
    [ Category( "Behaviour" ), 
    DefaultValue( 100 ), 
    Description( "Gets or sets the length of time that must transpire before subsequent ToolTip windows appear as the mouse pointer moves from one control to another." ) ]
    public int ReshowDelay
    {
      get
      {
        return m_iReshowDelay;
      }
      set
      {
        if( value != m_iReshowDelay )
        {
          m_iReshowDelay = value;
          OnReshowDelayChanged();
        }

      }
    }

    /// <summary>
    /// Gets or sets a value indicating whether a ToolTip window is displayed 
    /// even when its parent control is not active.
    /// </summary>
    [ Category( "Behaviour" ), 
    DefaultValue( false ), 
    Description( "Gets or sets a value indicating whether a ToolTip window is displayed even when its parent control is not active" ) ]
    public bool ShowAlways
    {
      get
      {
        return m_bShowAlways;
      }
      set
      {
        if( value != m_bShowAlways )
        {
          m_bShowAlways = value;
          OnShowAlwaysChanged();
        }

      }
    }
    #endregion

    #region Internal Classes
    internal class SubclassRedirector : NativeWindow
    {
      #region Class members
      private ToolTipEx m_parent;
      #endregion

      #region Class Overrides
      public SubclassRedirector( ToolTipEx parent )
      {
        m_parent = parent;
      }

      protected override void WndProc(ref System.Windows.Forms.Message m)
      {
        base.WndProc( ref m );
        m_parent.WndProc( ref m );
      }
      #endregion
    }

    internal class ToolTipWrapper : Control
    {
      #region Class constants
      private const string  TOOLTIPS_CLASS = "tooltips_class32";
      #endregion
      
      #region Class members
      private ToolTipEx m_parent;
      #endregion

      #region Class constructor and overrided methods
      public ToolTipWrapper( ToolTipEx parent )
      {
        ControlStyles styleTrue = ControlStyles.AllPaintingInWmPaint |
          ControlStyles.DoubleBuffer |
          ControlStyles.EnableNotifyMessage |
          ControlStyles.UserPaint;

        SetStyle( styleTrue, true );
        SetStyle( ControlStyles.Selectable, false );

        m_parent = parent;
      }

      /// <summary>
      /// Params which used in creation of tooltip
      /// </summary>
      protected override System.Windows.Forms.CreateParams CreateParams
      {
        get
        {
          CreateParams param = base.CreateParams;

          param.ClassName = TOOLTIPS_CLASS;
          param.ExStyle = (int)WindowExStyles.WS_EX_TOPMOST;

          // param.Style = -2147483645;
          unchecked 
          {
            param.Style = (int)WindowStyles.WS_POPUP | 
              (int)ToolTipStyles.TTS_NOPREFIX | 
              (int)ToolTipStyles.TTS_ALWAYSTIP;
          }
 
          return param;
        }
      }

      /// <summary>
      /// Initialize Common control and tooltip window
      /// </summary>
      protected override void CreateHandle()
      {
        InitializeCommonControls();
        base.CreateHandle();

        WindowsAPI.SetWindowPos( this.Handle, new IntPtr( (int)SetWindowPosZOrder.HWND_TOPMOST ),
          0, 0, 0, 0, (uint)SetWindowPosFlags.SWP_NOMOVE | 
          (uint)SetWindowPosFlags.SWP_NOSIZE | 
          (uint)SetWindowPosFlags.SWP_NOACTIVATE );
      }
      /// <summary>
      /// Initialize Windows common controls before try to use them
      /// </summary>
      /// <returns>TRUE - if successful, FALSE - fail</returns>
      private bool InitializeCommonControls()
      {
        INITCOMMONCONTROLSEX icex = new INITCOMMONCONTROLSEX();
        icex.dwSize = Marshal.SizeOf( typeof( INITCOMMONCONTROLSEX ) );
        icex.dwICC = (int)CommonControlInitFlags.ICC_WIN95_CLASSES;
      
        bool bStatus = WindowsAPI.InitCommonControlsEx( icex );

        return bStatus;
      }

      /// <summary>
      /// Redirect painting to Component
      /// </summary>
      /// <param name="e"></param>
      protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
      {
        m_parent.OnPaint( this.Text, e );
      }

      /// <summary>
      /// Redirect painting to Component
      /// </summary>
      /// <param name="pevent"></param>
      protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs pevent)
      {
        m_parent.OnPaintBackground( this.Text, pevent );
      }
      #endregion
    }

    internal protected class ToolTipStringFormatter : ICloneable
    {
      private ToolTipEx m_parent;

      public FontFamily family;
      public FontStyle  style;
      public float      size;
      public Color      color;

      private ToolTipStringFormatter(){}
      public  ToolTipStringFormatter( ToolTipEx parent )
      {
        m_parent = parent;
        Reset();
      }

      
      public void   Reset()
      {
        family= m_parent.m_tooltip.Font.FontFamily;
        style = m_parent.m_tooltip.Font.Style;
        size  = m_parent.m_tooltip.Font.Size;
        color = SystemColors.InfoText;
      }

      public object Clone()
      {
        return this.MemberwiseClone();
      }

      public void   CopyFrom( ToolTipStringFormatter obj )
      {
        if( obj == this ) return;

        family = obj.family; 
        style  = obj.style;  
        size   = obj.size;   
        color  = obj.color;  
      }


      public static implicit operator Color( ToolTipStringFormatter obj )
      {
        return obj.color;
      }

      public static implicit operator Font( ToolTipStringFormatter obj )
      {
        return new Font( obj.family, obj.size, obj.style );
      }

    }
    #endregion

    #region Class Initialize/Finilize methods
    public ToolTipEx()
    {
      m_format = new StringFormat();
      m_format.Alignment      = StringAlignment.Near;
      m_format.LineAlignment  = StringAlignment.Near;
      m_format.Trimming       = StringTrimming.None;
      m_format.FormatFlags    = StringFormatFlags.LineLimit;
      m_format.HotkeyPrefix   = HotkeyPrefix.Hide;

      m_tooltip = new ToolTipWrapper( this );
      SetTimerSettings( ToolTipsDelays.TTDT_AUTOMATIC, AutomaticDelay );
    }

    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        m_tooltip.Dispose();
        m_controls.Clear();

        foreach( SubclassRedirector tool in m_parents.Values )
        {
          tool.ReleaseHandle();
        }
      }
      
      base.Dispose( disposing );
    }
    #endregion

    #region ToolTip Property Provider methods
    public string GetToolTip( Control ctrl )
    {
      return (string)m_controls[ ctrl ];
    }

    public void   SetToolTip( Control ctrl, string value )
    {
      bool bNew = ( m_controls.Contains( ctrl ) == false );
      m_controls[ ctrl ] = value;

      if( ctrl.IsHandleCreated == false )
      {
        if( value == null || value.Length == 0 )
        { 
          m_controls.Remove( ctrl );
          return;
        }

        ctrl.HandleCreated += new EventHandler( OnControlHandleCreated );
      }
      else
      {
        TooltipAction( ctrl, value, 
          ( value == null || value.Length == 0 ) ? TToolAction.REMOVE : 
          ( ( bNew == true ) ? TToolAction.INSERT : TToolAction.UPDATE ) );
      }
    }

    public bool   CanExtend( object extendee )
    {
      // if extendee is Control class child then we can provide our
      // services for it
      return ( extendee is Control );
    }

    
    private void OnControlHandleCreated( object sender, EventArgs e )
    {
      Control ctrl = ( Control )sender;
      string  text = (string)m_controls[ sender ];

      // detach event
      ctrl.HandleCreated -= new EventHandler( OnControlHandleCreated );

      TooltipAction( ctrl, text, 
        ( text == null || text.Length == 0 ) ? TToolAction.REMOVE : TToolAction.INSERT );
    }

    private void TooltipAction( Control ctrl, string text, TToolAction action )
    {
      if( ctrl.Parent != null )
      {
        AttachToParent( ctrl.Parent.Handle );

        Rectangle clientRect = new Rectangle( ctrl.Location, ctrl.Size );

        TOOLINFO info = new TOOLINFO();
        info.cbSize   = (uint)Marshal.SizeOf( typeof( TOOLINFO ) );
        info.uFlags   = (uint)ToolTipFlags.TTF_IDISHWND | (uint)ToolTipFlags.TTF_SUBCLASS;
        info.hwnd     = ctrl.Parent.Handle;
        info.uId      = ctrl.Handle;
        info.lpszText = text; //( text != null ) ? Marshal.StringToHGlobalAuto( text ) : IntPtr.Zero;
        info.rect     = (RECT)clientRect;

        int msg = 0;

        switch( action )
        {
          case TToolAction.INSERT: 
            msg = (int)ToolTipMsg.TTM_ADDTOOLW; 
            Trace.WriteLine( "Add", "TOOL ACTION" );
            break;
          
          case TToolAction.UPDATE: 
            msg = (int)ToolTipMsg.TTM_UPDATETIPTEXTW; 
            Trace.WriteLine( "Update Text", "TOOL ACTION" );
            break;
          
          case TToolAction.REMOVE: 
            msg = (int)ToolTipMsg.TTM_DELTOOLW; 
            Trace.WriteLine( "Add", "TOOL ACTION" );
            break;
        }

        int result = WindowsAPI.SendMessage( m_tooltip.Handle, msg, 0, ref info );

        if( result == 0 )
        {
          Trace.WriteLine( "Tool Action status FAIL!!!", "Notification" );
        }
      }
    }

    private void AttachToParent( IntPtr parentHandle )
    {
      if( m_parents.Contains( parentHandle ) == false )
      {
        // create subclass utility class
        SubclassRedirector tool = new SubclassRedirector( this );
        
        // subclass parentHandle window
        tool.AssignHandle( parentHandle );

        // store in list parents
        m_parents[ parentHandle ] = tool;
      }
    }
    private void SetTimerSettings( ToolTipsDelays flag, int time )
    {
      WindowsAPI.SendMessage( m_tooltip.Handle, 
        (int)ToolTipMsg.TTM_SETDELAYTIME, (int)flag, time );
    }
    #endregion

    #region Class event raisers
    private void RaiseActiveChangedEvent()
    {
      if( ActiveChanged != null )
      {
        ActiveChanged( this, EventArgs.Empty );
      }
    }
    
    private void RaiseAutomaticDelayChangedEvent()
    {
      if( AutomaticDelayChanged != null )
      {
        AutomaticDelayChanged( this, EventArgs.Empty );
      }
    }
    private void RaiseAutoPopDelayChangedEvent()
    {
      if( AutoPopDelayChanged != null )
      {
        AutoPopDelayChanged( this, EventArgs.Empty );
      }
    }
    private void RaiseInitialDelayChangedEvent()
    {
      if( InitialDelayChanged != null )
      {
        InitialDelayChanged( this, EventArgs.Empty );
      }
    }
    private void RaiseReshowDelayChangedEvent()
    {
      if( ReshowDelayChanged != null )
      {
        ReshowDelayChanged( this, EventArgs.Empty );
      }
    }
    private void RaiseShowAlwaysChangedEvent()
    {
      if( ShowAlwaysChanged != null )
      {
        ShowAlwaysChanged( this, EventArgs.Empty );
      }
    }

    private void RaiseOnShowEvent()
    {
      if( OnShow != null )
      {
        OnShow( this, EventArgs.Empty );
      }
    }

    private void RaiseOnHideEvent()
    {
      if( OnHide != null )
      {
        OnHide( this, EventArgs.Empty );
      }
    }
    #endregion

    #region Class virtual methods
    protected virtual void OnActiveChanged()
    {
      RaiseActiveChangedEvent();
    }

    protected virtual void OnAutomaticDelayChanged()
    {
      AutoPopDelay = AutomaticDelay * 10;
      m_iInitialDelay = AutomaticDelay;
      m_iReshowDelay  = (int)(AutomaticDelay / 5);

      SetTimerSettings( ToolTipsDelays.TTDT_AUTOMATIC, AutomaticDelay );

      RaiseAutomaticDelayChangedEvent();
    }

    protected virtual void OnAutoPopDelayChanged()
    {
      SetTimerSettings( ToolTipsDelays.TTDT_AUTOPOP, AutoPopDelay );

      RaiseAutoPopDelayChangedEvent();
    }

    protected virtual void OnInitialDelayChanged()
    {
      SetTimerSettings( ToolTipsDelays.TTDT_INITIAL, InitialDelay );

      RaiseInitialDelayChangedEvent();
    }

    protected virtual void OnReshowDelayChanged()
    {
      SetTimerSettings( ToolTipsDelays.TTDT_RESHOW, ReshowDelay );

      RaiseReshowDelayChangedEvent();
    }

    protected virtual void OnShowAlwaysChanged()
    {
      RaiseShowAlwaysChangedEvent();
    }
    #endregion

    #region Class worker methods
    /// <summary>
    /// Function try detect is input string has formatting or not
    /// </summary>
    /// <param name="text">String for check</param>
    /// <returns>TRUE - if formatting detected</returns>
    protected bool IsStringWithFormat( string text )
    {
      MatchCollection coll = m_regex.Matches( text );
      return ( coll.Count > 0 );
    }

    /// <summary>
    /// Our WndProc which raise to user OnShow and OnHide events
    /// </summary>
    /// <param name="m"></param>
    protected virtual void WndProc( ref System.Windows.Forms.Message m )
    {
      if( m.Msg == (int)Msg.WM_NOTIFY )
      {
        NMHDR note = (NMHDR)m.GetLParam(typeof(NMHDR));

        if( note.code == (int)ToolTipNotifyMsg.TTN_SHOW )
        {
          m.Result = IntPtr.Zero;
          RaiseOnShowEvent();
        }
        else if( note.code == (int)ToolTipNotifyMsg.TTN_POP )
        {
          RaiseOnHideEvent();
        }
      }
    }

    /// <summary>
    /// Paint text and background of tooltip
    /// </summary>
    /// <param name="text"></param>
    /// <param name="e"></param>
    protected virtual void OnPaint( string text, System.Windows.Forms.PaintEventArgs e )
    {
      OnPaintBackground( text, e );

      Rectangle rc = m_tooltip.ClientRectangle;
      Graphics  g = e.Graphics;
      string[]  multi = text.Split( '\n' );
      Point     pnt = new Point( 0 );

      foreach( string txt in multi )
      {
        DrawToolTipString( g, pnt, txt );
        pnt.Y += MeasureToolTipString( g, txt ).Height;
      }
    }

    /// <summary>
    /// Paint background of tooltip
    /// </summary>
    /// <param name="text"></param>
    /// <param name="pevent"></param>
    protected virtual void OnPaintBackground( string text, System.Windows.Forms.PaintEventArgs pevent )
    {
      Size size = MeasureToolTip( text );

      if( m_tooltip.Width != size.Width || m_tooltip.Height != size.Height )
      {
        WindowsAPI.SetWindowPos( m_tooltip.Handle, 
          new IntPtr( (int)SetWindowPosZOrder.HWND_TOPMOST ),
          0, 0, size.Width, size.Height, (uint)SetWindowPosFlags.SWP_NOMOVE | 
          (uint)SetWindowPosFlags.SWP_NOACTIVATE );

        return;
      }

      Graphics g = pevent.Graphics;
      Rectangle rc = m_tooltip.ClientRectangle;

      g.FillRectangle( SystemBrushes.Info, rc );
    }

    /// <summary>
    /// Function detect formatting and accordingly to it change formatting 
    /// class states
    /// </summary>
    /// <param name="frmt">formatting class</param>
    /// <param name="id">string Identificator of formatter</param>
    protected virtual void DetectFormatting( ref ToolTipStringFormatter frmt, string id )
    {
      if( string.Compare( id, "b", true ) == 0 ) // bold
      {
        frmt.style |= FontStyle.Bold;
      }
      else if( string.Compare( id, "i", true ) == 0 ) // italic
      {
        frmt.style |= FontStyle.Italic;
      }
      else if( string.Compare( id, "u", true ) == 0 ) // underline
      {
        frmt.style |= FontStyle.Underline;
      }
      else // try to detect is it a color name
      {
        try
        {
          Color clr = Color.FromName( id );
          frmt.color = clr;
        }
        catch{}
      }
    }

    /// <summary>
    /// Calculate size which needed to display tooltip
    /// </summary>
    /// <param name="text"></param>
    /// <returns></returns>
    protected virtual Size MeasureToolTip( string text )
    {
      string[] multi = text.Split( '\n' );

      int      maxWidth = 0;
      int      totalHeight = 0;

      Bitmap bmp = new Bitmap( 1, 1 );
      Graphics g = Graphics.FromImage( bmp );
      Size sizeTmp;

      foreach( string txt in multi )
      {
        sizeTmp = MeasureToolTipString( g, txt );
        maxWidth = Math.Max( maxWidth, sizeTmp.Width );
        totalHeight += sizeTmp.Height;
      }

      g.Dispose();
      bmp.Dispose();

      return new Size( maxWidth, totalHeight + 4 );
    }

    /// <summary>
    /// Measure one line of tooltip text calcuating formatting
    /// </summary>
    /// <param name="g"></param>
    /// <param name="txt"></param>
    /// <returns></returns>
    protected virtual Size MeasureToolTipString( Graphics g, string txt )
    {
      Size sizeText;
      int maxHeight = 0;
      int totalWidht = 0;

      string  tempString = txt;
      Stack   stack = new Stack();

      ToolTipStringFormatter frmtString = new ToolTipStringFormatter( this );
      
      stack.Push( frmtString.Clone() );
      
      do
      {
        Match   mt = m_regex.Match( tempString );
        
        if( mt.Success == true )
        {
          string pretext  = mt.Groups[1].Value;
          string formatId = mt.Groups[2].Value;
          string text     = mt.Groups[3].Value;

          tempString = tempString.Remove( 0, mt.Groups[0].Value.Length );
          frmtString.CopyFrom( (ToolTipStringFormatter)stack.Peek() );

          sizeText = MeasureStringByFormat( g, frmtString, pretext );
          maxHeight = Math.Max( maxHeight, sizeText.Height );
          totalWidht += sizeText.Width;

          DetectFormatting( ref frmtString, formatId );

          if( IsStringWithFormat( text ) )
          {
            stack.Push( frmtString.Clone() );
            tempString = text + "</stack>" + tempString;
            continue;
          }
          else
          {
            int pos = text.IndexOf( "</stack>" );
            int left = pos + "</stack>".Length;
            
            if( pos != -1 )
            {
              tempString = text.Substring( left, text.Length - left ) + tempString;
              text = text.Substring( 0, pos );
            }

            sizeText = MeasureStringByFormat( g, frmtString, text );
            maxHeight = Math.Max( maxHeight, sizeText.Height );
            totalWidht += sizeText.Width;
            
            if( pos != -1 ) stack.Pop();
          }
        }
        else
        {
          int pos = 0;
          
          while( -1 != ( pos = tempString.IndexOf( "</stack>" ) ) )
          {
            int left = pos + "</stack>".Length;
            string text = tempString.Substring( 0, pos );
            
            tempString = tempString.Substring( left, tempString.Length - left );

            sizeText = MeasureStringByFormat( g, (ToolTipStringFormatter)stack.Peek(), text );
            maxHeight = Math.Max( maxHeight, sizeText.Height );
            totalWidht += sizeText.Width;

            stack.Pop();
          }
          
          sizeText = MeasureStringByFormat( g, (ToolTipStringFormatter)stack.Peek(), tempString );
          maxHeight = Math.Max( maxHeight, sizeText.Height );
          totalWidht += sizeText.Width;
          tempString = "";
        }
        
        if( tempString.Length == 0 ) break;        
      }
      while( true );

      stack.Clear();      
      
      return new Size( totalWidht, maxHeight );
    }

    /// <summary>
    /// Measure size of tooltip string accorfing to formatting
    /// </summary>
    /// <param name="g"></param>
    /// <param name="frmt"></param>
    /// <param name="txt"></param>
    /// <returns></returns>
    protected virtual Size MeasureStringByFormat( Graphics g, ToolTipStringFormatter frmt, string txt )
    {
      SizeF size = g.MeasureString( txt, (Font)frmt, 0, m_format );
      return new Size( (int)( size.Width + 0.5 ), (int)( size.Height + 0.5 ) );
    }

    /// <summary>
    /// Draw one line of tooltip text with formatting
    /// </summary>
    /// <param name="g"></param>
    /// <param name="pntStart"></param>
    /// <param name="txt"></param>
    protected virtual void DrawToolTipString( Graphics g, Point pntStart, string txt )
    {
      string  tempString = txt;
      Stack   stack = new Stack();

      ToolTipStringFormatter frmtString = new ToolTipStringFormatter( this );
      
      stack.Push( frmtString.Clone() );
      
      do
      {
        Match   mt = m_regex.Match( tempString );
        
        if( mt.Success == true )
        {
          string pretext  = mt.Groups[1].Value;
          string formatId = mt.Groups[2].Value;
          string text     = mt.Groups[3].Value;

          tempString = tempString.Remove( 0, mt.Groups[0].Value.Length );
          frmtString.CopyFrom( (ToolTipStringFormatter)stack.Peek() );

          DrawStringByFormat( g, ref pntStart, frmtString, pretext );
          DetectFormatting( ref frmtString, formatId );

          if( IsStringWithFormat( text ) )
          {
            stack.Push( frmtString.Clone() );
            tempString = text + "</stack>" + tempString;
            continue;
          }
          else
          {
            int pos = text.IndexOf( "</stack>" );
            int left = pos + "</stack>".Length;
            
            if( pos != -1 )
            {
              tempString = text.Substring( left, text.Length - left ) + tempString;
              text = text.Substring( 0, pos );
            }

            DrawStringByFormat( g, ref pntStart, frmtString, text );
            
            if( pos != -1 ) stack.Pop();
          }
        }
        else
        {
          int pos = 0;
          
          while( -1 != ( pos = tempString.IndexOf( "</stack>" ) ) )
          {
            int left = pos + "</stack>".Length;
            string text = tempString.Substring( 0, pos );
            
            tempString = tempString.Substring( left, tempString.Length - left );

            DrawStringByFormat( g, ref pntStart, (ToolTipStringFormatter)stack.Peek(), text );
            stack.Pop();
          }
          
          DrawStringByFormat( g, ref pntStart, (ToolTipStringFormatter)stack.Peek(), tempString );
          tempString = "";
        }
        
        if( tempString.Length == 0 ) break;        
      }
      while( true );

      stack.Clear();
    }

    /// <summary>
    /// Draw string accordingly to formatting
    /// </summary>
    /// <param name="g"></param>
    /// <param name="start"></param>
    /// <param name="frmt"></param>
    /// <param name="text"></param>
    protected virtual void DrawStringByFormat( Graphics g, ref Point start, 
      ToolTipStringFormatter frmt, string text )
    {
      Font  newFont = (Font)frmt;
      Color clr = (Color)frmt;

      SizeF size = g.MeasureString( text, newFont, 0, m_format );
      g.DrawString( text, newFont, new SolidBrush( clr ), start );
      start.X += ( int )( size.Width + 0.5 );
    }
    #endregion
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO ArtfulBits Inc.
Ukraine Ukraine
Name:Kucherenko Oleksandr

Born:September 20, 1979

Platforms: Win32, Linux; - well known and MS-DOS; Win16; OS/2 - old time not touched;

Hardware: IBM PC

Programming Languages: Assembler (for Intel 80386); Borland C/C++; Borland Pascal; Object Pascal; Borland C++Builder; Delphi; Perl; Java; Visual C++; Visual J++; UML; XML/XSL; C#; VB.NET; T-SQL; PL/SQL; and etc.

Development Environments: MS Visual Studio 2001-2008; MS Visual C++; Borland Delphi; Borland C++Builder; C/C++ any; Rational Rose; GDPro; Together and etc.

Libraries: STL, ATL, WTL, MFC, NuMega Driver Works, VCL; .NET 1.0, 1.1, 2.0, 3.5; and etc.

Technologies: Client/Server; COM; DirectX; DirectX Media; BDE; HTML/DHTML; ActiveX; Java Servlets; DCOM; COM+; ADO; CORBA; .NET; Windows Forms; GDI/GDI+; and etc.

Application Skills: Databases - design and maintain, support, programming; GUI Design; System Programming, Security; Business Software Development. Win/Web Services development and etc.

Comments and Discussions