Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

A simple NewsTicker - Two implementations

4.00/5 (8 votes)
23 Apr 2014CPOL3 min read 37.6K   1K  
Implementing a simple News Ticker custom control for Winforms .NET to reduce flicker as much as possible.

Introduction

When looking around the internet for a clue to implementing a simple scrolling news ticker style panel for an application I have in production, the samples I found all pointed to the use of Graphics.Drawstring, which when turned into a control flickered annoyingly no matter what I tried. The code I will present below in my first ever submission (please be gentle!) provides two ways that I found to overcome this. Please feel free to add/amend and otherwise improve on my examples!

Also, I should add that there are a lot of omissions in the class, like extra properties that could be implemented and there is virtually no error trapping... I will leave that up to your individual requirements.

Implementation 1 - Text Rendering

Having started to code a control against the sample ideas I found and having already implemented double buffering to little effect, I took a look at an alternative way to actually draw the text to the control's surface and came across the TextRenderer class

The key area of the first implementation is to use a TextRenderer object instead of the aforementioned Graphics.Drawstring call. In the snippet below, Gr is a graphics object defined for the UserControl itself, _news is the actual string to present in the control and the variables X and Y are the starting width and height offset of the string.

VB
Gr.Clear(Me.BackColor)
TextRenderer.DrawText(Gr, _news, Me.Font, New Point(CInt(X), CInt(Y)), Me.ForeColor)

The text is moved across the panel by manipulating the value of X in response to a timer Tick event until it is found to be less than zero minus the length of the string itself (ie: it has disappeared off the left side of the control) when it is reset to the starting point, the right side of the control.

Altering the values of the timer's interval and a step value _speed allows the user to fine-tune the control for least flicker.

The two central methods in the control respond to the initial setup during MyBase.Load and the timer tick response

VB
Private Sub SetupWindow()
  Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
  Gr = Me.CreateGraphics()
  X = Me.Width
  Me.BorderStyle = Windows.Forms.BorderStyle.Fixed3D
  Me.Dock = DockStyle.Bottom
  _strSize = Gr.MeasureString(_news, Me.Font)
  If (Me.Height < _strSize.Height + 4) Then Me.Height = CInt(_strSize.Height) + 4
  Timer.Interval = 25
  Timer.Start()
End Sub

Private Sub UpdateDisplay()
  Dim tHeight As Double = _strSize.Height
  Select Case Me.TextPosition
   Case TextPlacement.Top : Y = 0
    Case TextPlacement.Middle : Y = CSng((Me.Height / 2) - (tHeight / 2))
    Case TextPlacement.Bottom : Y = CSng(Me.Height - tHeight)
  End Select
  Gr.Clear(Me.BackColor)
  TextRenderer.DrawText(Gr, _news, Me.Font, New Point(CInt(X), CInt(Y)), Me.ForeColor)
  If X <= (0 - _strSize.Width) Then
    X = Me.Width
  Else
    X = CSng(X - _speed)
  End If
End Sub

Implementation 2 - Repositioning a Label

While I was looking at ways to reduce the flicker in the above code, I considered the possibility of not redrawing the text directly at all but just moving it, so I added a label to my UserControl, set Autosize = True and set its text property to the string to be shown (_news). By manipulating the same X and Y variables as above, having first set them to the label's width and height offset it was possible to implement the same effect of a scrolling text with (to my mind) even less flickering evident than that noticeable in the first implementation

VB
lbl.Location = New Point(CInt(X), CInt(Y))

The full listing for implementation two is reproduced here (Note: I am omitting the label insertion in the designer partial class, although this should be placed on the UserControl and named lbl for this code to operate correctly)

VB
Public Class NewsTicker 
Inherits System.Windows.Forms.UserControl

Enum TextPlacement
 Top
 Middle
 Bottom
End Enum


Dim X As Single
Dim Y As Single
Dim Gr As Graphics
Dim _news As String = "There is no news to display at this time"
Dim _strSize As SizeF
Private _tpos As TextPlacement = TextPlacement.Top
Private _speed As Double = 1.0

Private Sub NewsTicker_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    SetupWindow()
End Sub
Private Sub NewsTicker_Resize(sender As Object, e As System.EventArgs) Handles Me.Resize
    SetupWindow()
End Sub
Private Sub Timer_Tick(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Timer.Tick
    UpdateDisplay()
End Sub
    Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
    lbl.Height = Me.Height - 4
    lbl.Text = _news
    lbl.AutoSize = True
    lbl.Location = New Point(Me.Width, 0)
    Me.Dock = DockStyle.Bottom
    Me.BorderStyle = Windows.Forms.BorderStyle.Fixed3D
    X = Me.Width
    If (Me.Height < lbl.Height + 4) Then Me.Height = CInt(lbl.Height) + 4
    Timer.Interval = 25
    Timer.Start()
End Sub

Private Sub UpdateDisplay()
    Dim tHeight As Double = lbl.Height
    Select Case Me.TextPosition
        Case TextPlacement.Top : Y = 0
        Case TextPlacement.Middle : Y = CSng((Me.Height / 2) - (tHeight / 2))
        Case TextPlacement.Bottom : Y = CSng(Me.Height - tHeight)
    End Select
    lbl.Location = New Point(CInt(X), CInt(Y))
    If X <= (0 - lbl.Width) Then
        X = Me.Width
    Else
        X = CSng(X - _speed)
    End If
End Sub

Public Property News As String
    Get
        Return _news
    End Get
    Set(value As String)
        _news = value
        SetupWindow()
    End Set
End Property
Public Property TextPosition As TextPlacement
    Get
        Return _tpos
   End Get
   Set(value As TextPlacement)
        _tpos = value
   End Set
   End Property

Public Property Speed As Double
    Get
        Return _speed
    End Get
    Set(value As Double)
        If value < 0.01 Or value > 100 Then
            Throw New ArgumentOutOfRangeException("Speed", _
              "Cannot be less than 0.01 or greater than 100")
        Else
            _speed = value
            SetupWindow()
        End If
    End Set
End Property
End Class

I will upload the full project source as a .ZIP when I can work out why I keep getting an 'Unexpected error' during the upload procedure? - .ZIP now uploaded thanks to the guys at CP!

History

  • v1.0 - Initial release: 27 December 2013.
  • v1.01 - .ZIP upload issue resolved.

License

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