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

    Dim g_on As Boolean
    Dim g_text As String
    Dim g_blink_icon As Boolean
    Dim g_image As Image = Nothing
    Dim g_disposed As Boolean = False

#Region " Component Designer generated code "

    ''' <summary>
    ''' Initializes a new instance of the <see cref="BlinkLabel" /> 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="BlinkLabel" /> class.
    ''' </summary>
    Public Sub New()
        MyBase.New()

        'This call is required by the Component Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
        g_on = True
        g_blink_icon = False
        Me.AutoSize = False
    End Sub

    'Component overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        g_disposed = True
        If disposing Then
            Me.tmr_delay.Enabled = False
            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.Interval = 500
        Me.tmr_delay.SynchronizingObject = Me
        '
        'BlinkLabel
        '
        Me.Text = "Blinking Text"
        CType(Me.tmr_delay, System.ComponentModel.ISupportInitialize).EndInit()

    End Sub

#End Region

#Region " Class Properties "

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Delay between blick.
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get or Set blinking time delay.
    ''' </summary>
    ''' <value>Time interval in Integer</value>
    ''' <remarks>
    ''' If value shold be greater than or equal to 1. If it is not then it will set to the default value of 500.
    ''' </remarks>
    <Description("Specifies number of milliseconds between blink."), Category("Blink")> _
    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

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Is it blinking?
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get or Set blinking status.
    ''' </summary>
    ''' <value>Boolean value indicating blinking status.</value>
    ''' <remarks>
    ''' </remarks>
    <Description("Indicates whether the text is blinking or not."), Category("Blink")> _
    Public Property IsBlinking() As Boolean
        Get
            Return Me.tmr_delay.Enabled
        End Get
        Set(ByVal Value As Boolean)
            Me.tmr_delay.Enabled = Value
            g_on = True
            Me.draw()
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Blink image and text or text only?
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get or Set to blink image along the text or not.
    ''' </summary>
    ''' <value>Boolean value.</value>
    ''' <remarks>
    ''' By default only text will blink. Set this proerpty to <b>true</b> if you want to blink image with text too.
    ''' </remarks>
    <Description("Indicates whether to blink image along with text or not."), Category("Blink")> _
    Public Property BlinkImage() As Boolean
        Get
            Return Me.g_blink_icon
        End Get
        Set(ByVal Value As Boolean)
            Me.g_blink_icon = Value
            If Not Value Then
                MyBase.Image = Me.g_image
            End If
            Me.draw()
        End Set
    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

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Overloads the image property so we can controll it.
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get or Set the Image to display on this label.
    ''' </summary>
    ''' <value>The image object.</value>
    ''' <remarks>
    ''' You set to blink this image or not by using <see cref="BlinkLabel.BlinkImage" /> property. By default it will not blink.
    ''' </remarks>
    ''' <seelaso cref="BlinkLabel.BlinkImage" />
    Public Overloads Property Image() As Image
        Get
            Return Me.g_image
        End Get
        Set(ByVal Value As Image)
            MyBase.Image = Value
            Me.g_image = Value
            Me.draw()
        End Set
    End Property

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Overloads the text property so we can controll it.
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Get or Set the text to blink on this label.
    ''' </summary>
    ''' <value>The text to blink.</value>
    <Editor(GetType(System.ComponentModel.Design.MultilineStringEditor), GetType(System.Drawing.Design.UITypeEditor))> _
    Public Overloads Property Text() As String
        Get
            Return Me.g_text
        End Get
        Set(ByVal Value As String)
            MyBase.Text = Value
            Me.g_text = Value
            g_on = True
        End Set
    End Property

#End Region

#Region " Class Methods "

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Start blinking
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ''' <summary>
    ''' Start blinking.
    ''' </summary>
    ''' <remarks>
    ''' You can set <see cref="BlinkLabel.IsBlinking" /> property to <b>true</b> or call this methods both works same.
    ''' </remarks>
    ''' <seelaso cref="BlinkLabel.IsBlinking" />
    Public Sub Start()
        Me.IsBlinking = True
    End Sub

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

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Draw image and text on label  'tmr_delay
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    Private Sub draw()
        If g_disposed Then Exit Sub
        'Debug.Write("Ticks Thread: " & System.Threading.Thread.CurrentThread.GetHashCode)
        If Me.InvokeRequired Then
            Me.BeginInvoke(New update_me(AddressOf update_text))
        Else
            update_text()
        End If
    End Sub

    Private Delegate Sub update_me()

    ''' <summary>
    ''' Update_texts this instance.
    ''' </summary>
    Private Sub update_text()
        Try
            'Debug.WriteLine("Update Thread: " & System.Threading.Thread.CurrentThread.GetHashCode)
            If g_on Or (Not Me.IsBlinking) Then
                If Me.g_blink_icon Then MyBase.Image = Me.g_image
                MyBase.Text = Me.Text
            Else
                If Me.g_blink_icon Then MyBase.Image = Nothing
                MyBase.Text = ""
            End If
            g_on = Not g_on
        Catch
        End Try
        'If Not MyBase.FindForm Is Nothing Then MyBase.FindForm.Refresh()
    End Sub

    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    ' Time to blink
    '覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧覧
    Private Sub tmr_delay_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles tmr_delay.Elapsed
        Me.draw()
    End Sub

#End Region

End Class

Friend Class BlinkLabelDesigner
    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 BlinkLabelActionList(Me.Component))
            End If
            Return lists
        End Get
    End Property

End Class

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

    Private l_uct As BlinkLabel

    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, BlinkLabel)

        ' 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 BlinkLabel 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 BlinkImage() As Boolean
        Get
            Return l_uct.BlinkImage
        End Get
        Set(ByVal value As Boolean)
            GetPropertyByName("BlinkImage").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 IsBlinking() As Boolean
        Get
            Return l_uct.IsBlinking
        End Get
        Set(ByVal value As Boolean)
            GetPropertyByName("IsBlinking").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("Blink"))
        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( _
         "BlinkImage", "Blink Image", "Blink", "Do you want to blink image with the text too?"))

        items.Add(New DesignerActionPropertyItem( _
         "IsBlinking", "Is Blinking", "Blink", "Is it blinking right now."))

        items.Add(New DesignerActionPropertyItem( _
         "Delay", "Delay (in millisecond)", "Blink", "Delay between blinking."))

        Return items
    End Function

End Class
