Imports System.ComponentModel.Design

''' <summary>
''' Scroll state values.
''' </summary>
Public Enum ScrollState
    Stopped = 0
    Running = 1
    Paused = 2
    NotInitialized = 3
End Enum

''' <summary>
''' It is derived from <see cref="T:System.Windows.Forms.Label" /> class and adds properties to scrill the text in the label.
''' </summary>
''' <remarks>
''' <para>Like it name says it is a scrolling label. You can set delay and speed <see cref="ScrollLabel.Delay" /> and <see cref="ScrollLabel.Speed" /> properties.</para>
''' <para>If you have set this label to scroll and your application goes in to long procedure that may take more than the delay time then this label might stop scrolling. It does not run in seperate thread.</para>
''' </remarks>
<ToolboxBitmap(GetType(Label)), Description("A scrolling text label."), Designer(GetType(ScrollLabelDesigner))> _
Public Class ScrollLabel
    Inherits System.Windows.Forms.Label

    Dim l_x As Single
    Dim l_y As Single
    Dim l_step As Integer = 2
    Dim l_grp As System.Drawing.Graphics
    Dim l_brush As Brush
    Dim l_size As System.Drawing.SizeF
    Dim l_drawFormat As StringFormat
    Dim l_text As String
    Dim l_scroll_state As ScrollState
    Dim l_rightleft As RightToLeft
    Dim l_continuous As Boolean

#Region " Component Designer generated code "

    ''' <summary>
    ''' Initializes a new instance of the <see cref="ScrollLabel" /> class.
    ''' </summary>
    ''' <param name="Container">The container.</param>
    Public Sub New(ByVal Container As System.ComponentModel.IContainer)
        MyClass.New()

        'Required for Windows.Forms Class Composition Designer support
        Container.Add(Me)
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the <see cref="ScrollLabel" /> class.
    ''' </summary>
    Public Sub New()
        MyBase.New()

        'This call is required by the Component Designer.
        InitializeComponent()
        SetStyle( _
         ControlStyles.AllPaintingInWmPaint Or _
         ControlStyles.DoubleBuffer Or _
         ControlStyles.UserPaint Or _
         ControlStyles.ResizeRedraw Or _
         ControlStyles.SupportsTransparentBackColor, _
         True _
         )

        'Add any initialization after the InitializeComponent() call
        l_x = 0
        l_y = -1
        l_grp = Nothing
        l_brush = New System.Drawing.SolidBrush(MyBase.ForeColor)
        l_rightleft = MyBase.RightToLeft
        l_continuous = False
        l_scroll_state = ScrollState.NotInitialized
        Me.l_scroll_state = ScrollState.Stopped
        Me.AutoSize = False
    End Sub

    'Component overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            Me.tmr_delay.Enabled = False
            If l_grp IsNot Nothing Then l_grp.Dispose()
            If components IsNot Nothing Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Component Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Component Designer
    'It can be modified using the Component Designer.
    'Do not modify it using the code editor.
    Friend WithEvents tmr_delay As System.Timers.Timer
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.tmr_delay = New System.Timers.Timer
        CType(Me.tmr_delay, System.ComponentModel.ISupportInitialize).BeginInit()
        '
        'tmr_delay
        '
        Me.tmr_delay.SynchronizingObject = Me
        '
        'ScrollLabel
        '
        Me.Text = "Scroll Text"
        CType(Me.tmr_delay, System.ComponentModel.ISupportInitialize).EndInit()

    End Sub

#End Region

#Region " Class Properties "

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Delay between scrolling
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get or Set scrolling time delay.
    ''' </summary>
    ''' <value>Time interval in Integer</value>
    ''' <remarks>
    ''' This controls how fast it will scroll.
    ''' </remarks>
    ''' <seealso cref="ScrollLabel.Speed" />
    <Description("Specifies number of milliseconds between scroll."), Category("Scroll")> _
    Public Property Delay() As Double
        Get
            Return tmr_delay.Interval
        End Get
        Set(ByVal Value As Double)
            If Value < 1 Then Value = 500
            tmr_delay.Interval = Value
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' How many steps in one scroll
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get ot Set scrolling steps.
    ''' </summary>
    ''' <value>Steps in Integer</value>
    ''' <remarks>
    ''' This controlls how many steps it will take in each scrolls.
    ''' </remarks>
    ''' <seealso cref="ScrollLabel.Delay" />
    <Description("Specifies number of pixels to move text when it scrolls."), Category("Scroll")> _
    Public Property Speed() As Integer
        Get
            Return l_step
        End Get
        Set(ByVal Value As Integer)
            If Value < 1 Then Value = 1
            l_step = Value
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets a value indicating whether this <see cref="ScrollLabel" /> scrolls text as continuous text or not.
    ''' </summary>
    ''' <value><c>true</c> if ppy_continuous; otherwise, <c>false</c>.</value>
    <Description("Specifies whether to scroll text conituous fashion ot not."), Category("Scroll"), DefaultValue(False)> _
    Public Property Continuous() As Boolean
        Get
            Return Me.l_continuous
        End Get
        Set(ByVal value As Boolean)
            Me.l_continuous = value
            Me.set_x()
            Me.Refresh()
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Is it scrolling?
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get or Set scrolling status.
    ''' </summary>
    ''' <value>Boolean value indicating scroling status.</value>
    <Description("Indicates whether the text is scrolling or not."), Category("Scroll")> _
    Public Property IsScrolling() As Boolean
        Get
            Return Me.tmr_delay.Enabled
        End Get
        Set(ByVal Value As Boolean)
            Me.tmr_delay.Enabled = Value
            Me.set_x()
            If Value Then
                Me.l_scroll_state = ScrollState.Running
            Else
                Me.l_scroll_state = ScrollState.Stopped
            End If
            Me.draw()
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' The exact scroll state.
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get or Set blinking state.
    ''' </summary>
    ''' <value>Enumareted value indicating scroling state.</value>
    <Description("Indicates current scrolling state."), Category("Scroll")> _
    Public ReadOnly Property ScrollState() As ScrollState
        Get
            Return Me.l_scroll_state
        End Get
    End Property

    <Browsable(False), DesignOnly(True), EditorBrowsable(EditorBrowsableState.Never)> _
    Public Overloads Property AutoSize() As Boolean
        Get
            Return False
        End Get
        Private Set(ByVal value As Boolean)
            MyBase.AutoSize = value
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Font of this lable
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' It's same as all other label. But overloaded for internal purpose.
    ''' </summary>
    ''' <value></value>
    ''' <returns>The <see cref="T:System.Drawing.Font"></see> to apply to the text displayed by the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultFont"></see> property.</returns>
    ''' <PermissionSet><IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" /><IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /></PermissionSet>
    Public Overrides Property Font() As Font
        Get
            Return MyBase.Font
        End Get
        Set(ByVal Value As Font)
            MyBase.Font = Value
            If l_grp IsNot Nothing Then
                l_grp.Dispose()
                l_grp = Nothing
            End If
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Font of this lable
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' It's same as all other label. But overloaded for internal purpose.
    ''' </summary>
    ''' <value></value>
    ''' <returns>The foreground <see cref="T:System.Drawing.Color"></see> of the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultForeColor"></see> property.</returns>
    ''' <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /></PermissionSet>
    Public Overrides Property ForeColor() As Color
        Get
            Return MyBase.ForeColor
        End Get
        Set(ByVal Value As Color)
            l_brush.Dispose()
            MyBase.ForeColor = Value
            l_brush = New System.Drawing.SolidBrush(MyBase.ForeColor)
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Right-to-left string.
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' For Right-to-left languages. It's not working right now.
    ''' </summary>
    ''' <value></value>
    ''' <returns>One of the <see cref="T:System.Windows.Forms.RightToLeft"></see> values. The default is <see cref="F:System.Windows.Forms.RightToLeft.Inherit"></see>.</returns>
    ''' <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">The assigned value is not one of the <see cref="T:System.Windows.Forms.RightToLeft"></see> values. </exception>
    ''' <PermissionSet><IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" /><IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /></PermissionSet>
    Public Overrides Property RightToLeft() As RightToLeft
        Get
            Return Me.l_rightleft
        End Get
        Set(ByVal Value As RightToLeft)
            Me.l_rightleft = Value
            MyBase.RightToLeft = Value
            Me.set_x()
            Select Case Me.RtL(Me)
                Case -1
                    l_drawFormat = New StringFormat(StringFormatFlags.NoClip Or StringFormatFlags.DirectionRightToLeft)
                Case 1
                    l_drawFormat = New StringFormat(StringFormatFlags.NoClip)
            End Select
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Text to scroll.
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Text to scroll on label
    ''' </summary>
    ''' <value>String</value>
    ''' <remarks>
    ''' Keep in mind that, this controll scrolls only text not the image.
    ''' </remarks>
    <Editor(GetType(System.ComponentModel.Design.MultilineStringEditor), GetType(System.Drawing.Design.UITypeEditor))> _
    Public Overloads Property Text() As String
        Get
            Return Me.l_text
        End Get
        Set(ByVal Value As String)
            Me.l_text = Value
            MyBase.Text = Value
            Me.set_x()
            If l_grp IsNot Nothing Then
                l_grp.Dispose()
                l_grp = Nothing
            End If
            If Me.l_scroll_state <> ScrollState.NotInitialized Then Me.draw()
        End Set
    End Property
#End Region

#Region " Class Methods "

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Start scrolling
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Start scrolling.
    ''' </summary>
    ''' <remarks>
    ''' You can set <see cref="ScrollLabel.IsScrolling" /> property to <b>true</b> or call this methods both works same.
    ''' </remarks>
    ''' <seelaso cref="ScrollLabel.[Stop]" />
    ''' <seelaso cref="ScrollLabel.Pause" />
    Public Sub Start()
        Me.IsScrolling = True
    End Sub

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Stop scrolling
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Stop scrolling.
    ''' </summary>
    ''' <remarks>
    ''' You can set <see cref="ScrollLabel.IsScrolling" /> property to <b>false</b> or call this methods both works same.
    ''' </remarks>
    ''' <seelaso cref="ScrollLabel.Start" />
    ''' <seelaso cref="ScrollLabel.Pause" />
    Public Sub [Stop]()
        Me.IsScrolling = False
    End Sub

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Pause scrolling
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Pause scrolling.
    ''' </summary>
    ''' <remarks>
    ''' You can set <see cref="ScrollLabel.ScrollState" /> property to <b>Pause</b> or call this methods both works same.
    ''' </remarks>
    ''' <seelaso cref="ScrollLabel.Start" />
    ''' <seelaso cref="ScrollLabel.[Stop]" />
    Public Sub Pause()
        Me.tmr_delay.Enabled = False
        Me.l_scroll_state = ScrollState.Paused
        Me.draw()
    End Sub

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Returns Right-to-Left setting
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    Private Function RtL(ByRef O As Control) As Integer
        Select Case O.RightToLeft
            Case Windows.Forms.RightToLeft.Inherit
                Return RtL(O.Parent)
            Case RightToLeft.Yes
                Return -1
            Case RightToLeft.No
                Return 1
        End Select
    End Function

    Private Sub set_x()
        Dim l_r As Single

        l_y = -1
        If Me.l_grp Is Nothing Then
            Try
                l_grp = Me.CreateGraphics
                l_size = l_grp.MeasureString(Me.Text, MyBase.Font)
            Catch ex As Exception
                If l_grp Is Nothing Then
                    If RtL(Me) = -1 Then
                        l_x = Me.Width
                    Else
                        l_x = 0
                    End If
                End If
                Exit Sub
            End Try
        End If
        l_r = 1
        If Me.RtL(Me) = -1 Then
            If Me.ScrollState = Win.ScrollState.NotInitialized Or Me.ScrollState = Win.ScrollState.Stopped Then
                Select Case Me.TextAlign
                    Case ContentAlignment.BottomLeft, ContentAlignment.MiddleLeft, ContentAlignment.TopLeft
                        l_x = Me.Width - 2
                        l_r = -1
                    Case ContentAlignment.BottomCenter, ContentAlignment.MiddleCenter, ContentAlignment.TopCenter
                        l_x = CSng((Me.Width / 2) + (l_size.Width / 2))
                    Case ContentAlignment.BottomRight, ContentAlignment.MiddleRight, ContentAlignment.TopRight
                        l_x = 3 + l_size.Width
                End Select
            Else
                Select Case Me.TextAlign
                    Case ContentAlignment.BottomLeft, ContentAlignment.MiddleLeft, ContentAlignment.TopLeft
                        l_r = -1
                End Select
            End If
            If Me.l_continuous And l_r > 0 Then
                l_r = l_x
                Do While l_r < Me.Width
                    l_r += l_size.Width
                Loop
                l_x = l_r
            End If
        Else
            If Me.ScrollState = Win.ScrollState.NotInitialized Or Me.ScrollState = Win.ScrollState.Stopped Then
                Select Case Me.TextAlign
                    Case ContentAlignment.BottomLeft, ContentAlignment.MiddleLeft, ContentAlignment.TopLeft
                        l_x = 3
                        l_r = -1
                    Case ContentAlignment.BottomCenter, ContentAlignment.MiddleCenter, ContentAlignment.TopCenter
                        l_x = CSng((Me.Width - l_size.Width) / 2)
                    Case ContentAlignment.BottomRight, ContentAlignment.MiddleRight, ContentAlignment.TopRight
                        l_x = Me.Width - l_size.Width - 3
                End Select
            Else
                Select Case Me.TextAlign
                    Case ContentAlignment.BottomLeft, ContentAlignment.MiddleLeft, ContentAlignment.TopLeft
                        l_r = -1
                End Select
            End If
            If Me.l_continuous And l_r > 0 Then
                l_r = l_x
                Do While l_r > 0
                    l_r -= l_size.Width
                Loop
                l_x = l_r
            End If
        End If
    End Sub

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Draw the text on label
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    Private Sub draw()
        Dim l_r As Single

        Select Case Me.l_scroll_state
            Case ScrollState.Stopped
                MyBase.Text = Me.Text
                Exit Sub
            Case Else
                MyBase.Text = ""
        End Select
        Dim l_rl As Integer = RtL(Me)

        If Me.l_grp Is Nothing Then
            Try
                l_grp = Me.CreateGraphics
                l_size = l_grp.MeasureString(Me.Text, MyBase.Font)
            Catch ex As Exception
                If l_grp Is Nothing Then Exit Sub
            End Try
        End If
        If l_y < 0 Then
            l_y = MyBase.Font.GetHeight(l_grp)
            Select Case Me.TextAlign
                Case ContentAlignment.BottomCenter, ContentAlignment.BottomLeft, ContentAlignment.BottomRight
                    l_y = Me.Height - l_y - 3
                Case ContentAlignment.MiddleCenter, ContentAlignment.MiddleLeft, ContentAlignment.MiddleRight
                    If Me.RtL(Me) = -1 Then
                        l_y = ((Me.Height - l_y) / 2)
                    Else
                        l_y = ((Me.Height - l_y) / 2) - 1
                    End If
                Case ContentAlignment.TopCenter, ContentAlignment.TopLeft, ContentAlignment.TopRight
                    l_y = 0
            End Select
        End If
        'TODO: RightToLeft has bug.
        MyBase.RightToLeft = Windows.Forms.RightToLeft.No
        l_grp.Clear(Me.BackColor)
        MyBase.RightToLeft = Me.l_rightleft
        l_grp.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
        l_grp.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
        l_grp.DrawString(Me.Text, MyBase.Font, l_brush, l_x, l_y, l_drawFormat)

        Select Case l_rl
            Case -1
                If Me.l_continuous Then
                    l_r = l_x - l_size.Width
                    Do While l_r > 0
                        l_grp.DrawString(Me.Text, MyBase.Font, l_brush, l_r, l_y, l_drawFormat)
                        l_r -= l_size.Width
                    Loop
                    If l_x - l_size.Width >= (Me.Width - Me.Speed) Then
                        l_x += l_size.Width * l_rl
                    End If
                ElseIf Me.l_size.Width < (Me.Width / 2) Then
                    l_grp.DrawString(Me.Text, MyBase.Font, l_brush, l_x + (Me.Width * l_rl), l_y, l_drawFormat)
                    If l_x - l_size.Width >= (Me.Width - Me.Speed) Then
                        l_x += Me.Width * l_rl
                    End If
                Else
                    If (l_x - l_size.Width) > (Me.Width / 2) Then
                        l_grp.DrawString(Me.Text, MyBase.Font, l_brush, l_x + CSng((l_size.Width + (Me.Width / 2)) * l_rl), l_y, l_drawFormat)
                    End If
                    If l_x - l_size.Width >= (Me.Width - Me.Speed) Then
                        l_x += CSng(l_size.Width + (Me.Width / 2)) * l_rl
                    End If
                End If
            Case 1
                If Me.l_continuous Then
                    l_r = l_x + l_size.Width
                    Do While l_r < Me.Width
                        l_grp.DrawString(Me.Text, MyBase.Font, l_brush, l_r, l_y, l_drawFormat)
                        l_r += l_size.Width
                    Loop
                    If l_x + l_size.Width <= Me.Speed Then
                        l_x += l_size.Width * l_rl
                    End If
                ElseIf Me.l_size.Width < (Me.Width / 2) Then
                    l_grp.DrawString(Me.Text, MyBase.Font, l_brush, l_x + (Me.Width * l_rl), l_y, l_drawFormat)
                    If l_x + l_size.Width <= Me.Speed Then
                        l_x += Me.Width * l_rl
                    End If
                Else
                    If (l_size.Width + l_x) < (Me.Width / 2) Then
                        l_grp.DrawString(Me.Text, MyBase.Font, l_brush, l_x + CSng((l_size.Width + (Me.Width / 2)) * l_rl), l_y, l_drawFormat)
                    End If
                    If l_x + l_size.Width <= Me.Speed Then
                        l_x += CSng(l_size.Width + (Me.Width / 2)) * l_rl
                    End If
                End If
        End Select

    End Sub

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Time to scroll
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    Private Sub tmr_delay_Elapsed(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Handles tmr_delay.Elapsed
        Me.tmr_delay.Enabled = False
        l_x -= (Me.Speed * RtL(Me))
        Me.draw()
        Me.tmr_delay.Enabled = True
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        If Me.ScrollState = Win.ScrollState.Paused Then Me.draw()
    End Sub
#End Region

End Class

Friend Class ScrollLabelDesigner
    Inherits System.Windows.Forms.Design.ControlDesigner

    Private lists As DesignerActionListCollection

    Public Sub New()
        MyBase.New()
    End Sub

    'Use pull model to populate smart tag menu.
    Public Overrides ReadOnly Property ActionLists() _
    As DesignerActionListCollection
        Get
            If lists Is Nothing Then
                lists = New DesignerActionListCollection()
                lists.Add( _
                New ScrollLabelActionList(Me.Component))
            End If
            Return lists
        End Get
    End Property

End Class

'///////////////////////////////////////////////////////////////
'DesignerActionList-derived class defines smart tag entries and
' resultant actions.
'///////////////////////////////////////////////////////////////
Friend Class ScrollLabelActionList
    Inherits System.ComponentModel.Design.DesignerActionList

    Private l_uct As ScrollLabel

    Private designerActionUISvc As DesignerActionUIService = Nothing

    'The constructor associates the control 
    'with the smart tag list.
    Public Sub New(ByVal component As IComponent)

        MyBase.New(component)
        Me.l_uct = DirectCast(component, ScrollLabel)

        ' Cache a reference to DesignerActionUIService, so the
        ' DesigneractionList can be refreshed.
        Me.designerActionUISvc = _
        CType(GetService(GetType(DesignerActionUIService)), _
        DesignerActionUIService)

    End Sub

    'Helper method to retrieve control properties. Use of 
    ' GetProperties enables undo and menu updates to work properly.
    Private Function GetPropertyByName(ByVal propName As String) _
    As PropertyDescriptor
        Dim prop As PropertyDescriptor
        prop = TypeDescriptor.GetProperties(l_uct)(propName)
        If prop Is Nothing Then
            Throw New ArgumentException( _
            "Matching ScrollLabel property not found!", propName)
        Else
            Return prop
        End If
    End Function

    'Properties that are targets of DesignerActionPropertyItem entries.
    Public Property ForeColor() As System.Drawing.Color
        Get
            Return l_uct.ForeColor
        End Get
        Set(ByVal value As System.Drawing.Color)
            GetPropertyByName("ForeColor").SetValue(l_uct, value)
        End Set
    End Property

    <Editor(GetType(System.ComponentModel.Design.MultilineStringEditor), GetType(System.Drawing.Design.UITypeEditor))> _
    Public Property Text() As String
        Get
            Return l_uct.Text
        End Get
        Set(ByVal value As String)
            GetPropertyByName("Text").SetValue(l_uct, value)
        End Set
    End Property

    Public Property TextAlign() As System.Drawing.ContentAlignment
        Get
            Return l_uct.TextAlign
        End Get
        Set(ByVal value As System.Drawing.ContentAlignment)
            GetPropertyByName("TextAlign").SetValue(l_uct, value)
        End Set
    End Property

    Public Property Continuous() As Boolean
        Get
            Return l_uct.Continuous
        End Get
        Set(ByVal value As Boolean)
            GetPropertyByName("Continuous").SetValue(l_uct, value)
        End Set
    End Property

    Public Property Delay() As Double
        Get
            Return l_uct.Delay
        End Get
        Set(ByVal value As Double)
            GetPropertyByName("Delay").SetValue(l_uct, value)
        End Set
    End Property

    Public Property IsScrolling() As Boolean
        Get
            Return l_uct.IsScrolling
        End Get
        Set(ByVal value As Boolean)
            GetPropertyByName("IsScrolling").SetValue(l_uct, value)
        End Set
    End Property

    Public Property Speed() As Integer
        Get
            Return l_uct.Speed
        End Get
        Set(ByVal value As Integer)
            GetPropertyByName("Speed").SetValue(l_uct, value)
        End Set
    End Property

    'Implementation of this virtual method creates smart tag  
    ' items, associates their targets, and collects into list.
    Public Overrides Function GetSortedActionItems() As DesignerActionItemCollection
        Dim items As New DesignerActionItemCollection()

        'Define static section header entries.
        items.Add(New DesignerActionHeaderItem("Scroll"))
        items.Add(New DesignerActionHeaderItem("Appearance"))

        items.Add(New DesignerActionPropertyItem( _
         "ForeColor", "Text Color", "Appearance", "Forground color."))

        items.Add(New DesignerActionPropertyItem( _
         "Text", "Text", "Appearance", "Text to display."))

        items.Add(New DesignerActionPropertyItem( _
         "TextAlign", "Text Align", "Appearance", "Text alignment."))

        items.Add(New DesignerActionPropertyItem( _
         "Continuous", "Scroll Continuously", "Scroll", "Do you want to scroll text continuously?"))

        items.Add(New DesignerActionPropertyItem( _
         "IsScrolling", "Is Scrolling", "Scroll", "Is it scrolling right now."))

        items.Add(New DesignerActionPropertyItem( _
         "Delay", "Delay (in millisecond)", "Scroll", "Delay between scroll."))

        items.Add(New DesignerActionPropertyItem( _
         "Speed", "Speed (in pixels)", "Scroll", "How many pixel to scroll in each scroll."))

        Return items
    End Function

End Class
