65.9K
CodeProject 正在变化。 阅读更多。
Home

一个简单的 NewsTicker - 两种实现方式

2013年12月29日

CPOL

3分钟阅读

viewsIcon

38807

downloadIcon

1080

为 Winforms .NET 实现一个简单的自定义新闻滚动条控件,以最大限度地减少闪烁。

引言

在网上寻找实现一个简单的滚动式新闻滚动条面板的线索时,我发现的示例都指向使用`Graphics.Drawstring`,无论我尝试什么,将其转换为控件后都会令人讨厌地闪烁。我在我的第一次提交(请温柔!)中将要展示的代码提供了两种我发现可以克服这个问题的方法。请随意添加/修改并改进我的示例!

另外,我应该补充一点,这个类有很多遗漏,例如可以实现的额外属性,而且几乎没有错误处理……我将把它留给你们的个人需求。

实现 1 - 文本渲染

在开始根据我找到的示例想法编写控件代码,并且已经实现了双缓冲但效果不佳之后,我查看了另一种实际将文本绘制到控件表面的方法,并发现了`TextRenderer`类。

第一个实现的关键部分是使用`TextRenderer`对象而不是前面提到的`Graphics.Drawstring`调用。在下面的代码片段中,`Gr`是为 UserControl 本身定义的图形对象,`_news`是要在控件中呈现的实际字符串,变量`X`和`Y`是字符串的起始宽度和高度偏移量。

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

通过响应计时器`Tick`事件来操作`X`的值来移动面板上的文本,直到发现它小于零减去字符串本身的长度(即:它已消失在控件的左侧)时,将其重置为起始点,即控件的右侧。

更改计时器间隔和步长值`_speed`的值,允许用户微调控件以获得最少的闪烁。

控件中的两个核心方法响应`MyBase.Load`期间的初始设置和计时器滴答响应。

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

实现 2 - 重新定位标签

在我寻找减少上述代码中闪烁的方法时,我考虑了根本不直接重绘文本,而只是移动它的可能性,所以我向我的 UserControl 添加了一个标签,设置`Autosize = True`并将它的文本属性设置为要显示的字符串(`_news`)。通过操作与上面相同的`X`和`Y`变量,首先将它们设置为标签的宽度和高度偏移量,可以实现相同的滚动文本效果,在我看来,闪烁比第一个实现中明显的闪烁还要少。

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

这里复制了第二个实现的完整列表(注意:我省略了设计器局部类中的标签插入,尽管这应该放在 UserControl 上并命名为`lbl`才能使此代码正常运行)。

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

我将在弄清楚为什么我在上传过程中一直收到“意外错误”时上传完整的项目源代码作为 .ZIP 文件?——感谢 CP 的各位,.ZIP 文件现已上传!

历史

  • v1.0 - 初始发布:2013 年 12 月 27 日。
  • v1.01 - 已解决 .ZIP 上传问题。
© . All rights reserved.