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

模拟时钟控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (21投票s)

2009年3月14日

LGPL3

4分钟阅读

viewsIcon

82094

downloadIcon

5095

带时区修改的模拟时钟控件。

Analog Clock Control

引言

这是我为 Code Project 社区撰写的第二篇文章。本文将解释如何制作一个模拟时钟控件,该控件模仿 Windows Vista 时钟侧边栏小工具。它模仿了加载和滴答效果,具有 timezone 属性,可用于维护时钟中显示的 timezone,并且还具有不错的设计器支持功能。

TheClock 是一个模拟时钟控件,它模仿 Windows Vista 侧边栏小工具的行为,尤其是在加载、滴答甚至时区方面。请注意,由于 timezone 功能将具有复杂的方法,我将单独撰写文章,您可以在我完成这篇文章后找到。

背景

随着 Windows Vista 的发布,大多数人都知道它的一个新特性是带有小工具的侧边栏。在所有这些小工具中,有一个控件引起了我的注意,那就是模拟时钟。它具有不错的功能和界面。在观察了它的工作方式之后,我决定基于该小工具制作自己的 VB 模拟时钟。因此,我撰写了这篇文章,与 CodeProject 成员分享,特别是那些已经向该网站贡献了优秀文章的成员,这些文章在我的工作中给了我很大的帮助。

Using the Code

该控件有一个名为 TheClock 的主要组件。该组件必须具备的主要目标是以一种奇特的方式滴答,就像 Windows Vista 小工具一样。它应该具有的第二个功能是 Timezone 功能。本文将解释主要组件本身。我将在另一篇文章中单独讨论 Timezone 功能。

初始化组件

我们需要做的第一件事是创建一个组件。 TheClock 将继承 control 对象,并将实现 ISupportInitialize,因为我们将在放置控件后添加一些初始值。

#Region "The Clock - Copyright (C) 2007 Michael Rawi"

'The Clock
'Windows Forms Component - NET Framework 2.0
'Copyright (C) 2007 Michael Rawi

'This library is free software; you can redistribute it and/or
'modify it under the terms of the GNU Lesser General Public
'License as published by the Free Software Foundation; either
'version 2.1 of the License, or (at your option) any later version.

'This library is distributed in the hope that it will be useful,
'but WITHOUT ANY WARRANTY; without even the implied warranty of
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
'Lesser General Public License for more details.

'Michael Rawi
#End Region
Imports System.DateTime

''' <summary>
''' An analog clock.
''' </summary>
''' <remarks>Created by Michael Rawi</remarks>
<Designer(GetType(TheClockDesigner)), _
Description("A modern analog clock."), _
ToolboxBitmap(GetType(TheClock), "TheClock.png")> _
Public Class TheClock
    Inherits Control
    Implements System.ComponentModel.ISupportInitialize

...
...
...

End Class

设置变量和属性

在制作控件之前,您应该知道开发控件需要哪些变量。您不必完全知道,因为您以后可以添加其他变量,但您至少应该定义您最需要的变量。对于此控件,我们还需要枚举。

...
...

#Region "Enumeration"
    Public Enum Styles
        Analog = 0
    End Enum

    Public Enum SecondTicks
        [Default] = 0
        Rapid = 1
        Original = 2
    End Enum

    Private Enum FadeStatus
        [In] = 0 'Slowly disappeared - 1.0F~0.5F in 0.5 second by 5 Frame
        Out = 1 'Slowly reappeared
    End Enum
#End Region

#Region "Component Variables"
#Region "Core Variables"
    Private WithEvents _tmrTime As Timer
    Private WithEvents _tmrTick As Timer
    Private WithEvents _tmrLoad As Timer

    Private _imgS As Image
    Private _imgM As Image
    Private _imgH As Image

    Private _angleS As Single
    Private _angleM As Single
    Private _angleH As Single

    Private _ComInfo As New Devices.ComputerInfo
#End Region
#Region "Private Properties"
    Private _secTick As SecondTicks = SecondTicks.Default
    Private _style As Styles
#End Region
#Region "Load Animation"
    Private _loadTimeframe As Single
    Private _loadHAngle As Single
    Private _loadMAngle As Single
    Private _loadSAngle As Single
    Private _loadMod As Integer
#End Region
#Region "Features"
    Private _ShowSecond As Boolean = True
    Private _AdvancedMode As Boolean = False

    Private _FadeFocus As Boolean = True
    Private _FadeStatus As FadeStatus = FadeStatus.Out
    Private _FadeAlpha As Single = 0.5F
    Private WithEvents _tmrFade As Timer

    Private _span As TimeSpan = New TimeSpan(0, 0, 0)
    Private _State As String
#End Region
#End Region

...
...

在下一步中,我们将创建一些属性值,以便更容易进行设计时。

...
...

#Region "Properties"
    ''' <summary>
    ''' Get or set the style of the clock.
    ''' </summary>
    ''' <value>Style</value>
    ''' <returns>Current Style</returns>
    ''' <remarks></remarks>
    <Description("Get or set the style of the Clock"), Category("Features"), _
    DefaultValue(GetType(Styles), "Analog")> _
    Public Property Style() As Styles
        Get
            Return _style
        End Get
        Set(ByVal value As Styles)
            _style = value
        End Set
    End Property

    ''' <summary>
    ''' Get or set the second tick's style.
    ''' </summary>
    ''' <value>SecondTick</value>
    ''' <returns>Current SecondTicks</returns>
    ''' <remarks></remarks>
    <Description("Get or set the second tick's style"), Category("Features"), _
    DefaultValue(GetType(SecondTicks), "Default")> _
    Public Property SecondTick() As SecondTicks
        Get
            Return _secTick
        End Get
        Set(ByVal value As SecondTicks)
            _secTick = value
            Select Case value
                Case SecondTicks.Rapid
                    _tmrTime.Interval = 100
                Case Else
                    _tmrTime.Interval = 1000
            End Select
        End Set
    End Property

    ''' <summary>
    ''' Get or set the second visibility.
    ''' </summary>
    ''' <value>Boolean</value>
    ''' <returns>Second visibility</returns>
    ''' <remarks></remarks>
    <Description("Get or set the second visibility"), Category("Features"), _
    DefaultValue(True)> _
    Public Property ShowSecond() As Boolean
        Get
            Return _ShowSecond
        End Get
        Set(ByVal value As Boolean)
            _ShowSecond = value
        End Set
    End Property

    ''' <summary>
    ''' Enable advanced mode. This feature is not ready yet
    ''' </summary>
    ''' <value>Boolean</value>
    ''' <returns>AdvancedMode</returns>
    ''' <remarks></remarks>
    <Browsable(False)> _
    Public Property AdvancedMode() As Boolean
        Get
            Return _AdvancedMode
        End Get
        Set(ByVal value As Boolean)
            _AdvancedMode = value
        End Set
    End Property

    ''' <summary>
    ''' Enable fade focus effect.
    ''' </summary>
    ''' <value>Boolean</value>
    ''' <returns>Current fadefocus effect</returns>
    ''' <remarks></remarks>
    <Description("Enable fade focus effect"), Category("Features"), _
    DefaultValue(True)> _
    Public Property EnableFadeFocus() As Boolean
        Get
            Return _FadeFocus
        End Get
        Set(ByVal value As Boolean)
            _FadeFocus = value
            Me.Refresh()
        End Set
    End Property

    ''' <summary>
    ''' Set clock time span against current time.
    ''' </summary>
    ''' <value>Timespan</value>
    ''' <returns>Current timespan</returns>
    ''' <remarks></remarks>
    <Description("Set clock time span against current time"), Category("Features"), _
    DefaultValue(GetType(TimeSpan), "00:00:00")> _
    Public Property ClockSpan() As TimeSpan
        Get
            Return _span
        End Get
        Set(ByVal value As TimeSpan)
            _span = value
        End Set
    End Property
#End Region

...
...

需要做的一件重要的事情是锁定控件的大小,因为我们将从背景图像中获得固定大小。这可以通过覆盖 OnSizeChanged 事件来完成。

使东西工作

在初始化步骤之后,现在我们将继续主要功能。 Draw Surface Area 区域包含绘制时针、分针和秒针所需的方法。为了创建所有效果,我们将为此控件设置 3 个计时器。第一个计时器将充当时间指示器。它每秒滴答一次,并告诉控件更新秒针的位置。第二个计时器将执行“弹跳”效果。现在,如果您正确观察 Windows 小工具,您会注意到,在秒针滴答之后,它会稍微弹回,就像真正的时钟一样。为了创建该效果,我们需要一个计时器在秒针滴答之后使其弹起。最后一个计时器将用于初始化过程。在控件加载后,它需要找到当前时间的位置,并以一种优雅的方式将指针放置在正确的位置。这就是我们需要实现 ISupportInitialize 来完成工作的原因。

...

Public Sub EndInit() Implements System.ComponentModel.ISupportInitialize.EndInit
    'Activate the clock
    If Not Me.DesignMode Then
        If ((Now.Hour + _span.Hours) Mod 12) * _
		30 > (Now.Minute + _span.Minutes) * 6 Then
            _loadMod = 1
        Else
            _loadMod = 3
        End If
        _loadTimeframe = (Now.Minute + _span.Minutes) * 6 + (Now.Second \ 10)
        _loadHAngle = (((Now.Hour + _span.Hours) Mod 12) * _
	    30 + ((Now.Minute + _span.Minutes) * 6 \ 12)) / (_loadTimeframe / 6)
        _loadMAngle = 6

        _tmrLoad = New Timer
        _tmrLoad.Interval = 50
        _angleH = 0
        _angleM = 0
        _angleS = 0
        Me.Refresh()
        _tmrLoad.Start()
    End If
End Sub

...

添加设计器支持

Designer Support

一个好的组件应该具有一些使开发时间更容易的功能。其中一个功能是设计器支持。这将使我们的组件更易于使用。

<System.Security.Permissions.PermissionSetAttribute_
	(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Public Class TheClockDesigner
    Inherits System.Windows.Forms.Design.ControlDesigner

    Private lists As DesignerActionListCollection

    Public Overrides ReadOnly Property ActionLists() _
            As DesignerActionListCollection
        Get
            If lists Is Nothing Then
                lists = New DesignerActionListCollection()
                lists.Add( _
                New TheClockActionList(Me.Component))
            End If
            Return lists
        End Get
    End Property
End Class

时区支持

Timezone support

Use own time

对于最后一个功能,我们将为组件添加 Timezone 支持。通过添加此功能,用户可以选择她/他需要的控件时间,而不是仅仅遵循 Windows 标准控件。此功能还可以与设计器支持集成,使其更易于使用。此外,它还将具有一些不错的“使用我自己的”功能,可以根据用户的输入调整时钟时间差。

关注点

在创建此控件时,仍然有一个问题困扰着我。由于某种奇怪的原因,我必须调整由 XP 或 Vista 操作系统确定的秒针位置。我目前在这些操作系统上测试了我的项目,发现如果我使用 XP 操作系统,我需要将秒针向左移动 1 像素。此代码可以在 DrawSecondHand 方法中找到。直到现在,知道 XP 坐标与 Vista 坐标略有不同仍然让我感到困惑。

    Private Sub DrawSecondHand(ByVal g As Graphics, ByVal Angle As Single)
        Dim container As Rectangle
        If Angle <> 180 Then
            container = New Rectangle(-6, -64, 13, 129)
        Else
            If _ComInfo.OSVersion.StartsWith("5.1") Then
                container = New Rectangle(-7, -65, 13, 129) 'This works on XP
            Else
                container = New Rectangle(-6, -64, 13, 129) 'This works on Vista
            End If
        End If

        g.TranslateTransform(64.0F, 65.0F)
        g.RotateTransform(Angle)
        If _FadeFocus Then
            Using ia As New Imaging.ImageAttributes
                Dim cm As New Imaging.ColorMatrix

                cm.Matrix33 = _FadeAlpha
                ia.SetColorMatrix(cm)
                g.DrawImage(_imgS, container, 0, 0, 13, 129, _
                    GraphicsUnit.Pixel, ia)
            End Using
        Else
            g.DrawImage(_imgS, container)
        End If
        'g.DrawImage(_imgS, container)
        g.ResetTransform()
    End Sub

历史

  • 版本 1.0 - 初始发布
© . All rights reserved.