TimeEdit 控件
TimeEdit、TimeCombo、TimeColumn,用于灵活的时间编辑
引言
类似控件
System.Windows.Forms.DateTimePicker
- gTimePicker
此控件的区别
功能对比
DateTimePicker | gTimePicker | TimeEdit | |
值更新 | 掩码完成时 失去焦点时 验证后 | 失去焦点时 验证后 | 按键后立即 |
文本和值是 始终相同 | 否 | 否 | 是 |
空值支持 | 否 | 是 | 是 |
可以只有时间值 例如 0001/01/01 01:00 | 否 | 是 | 是 |
有下拉时钟 | 否 | 是 | 在 TimeCombo 中 |
输入时间时 不需要使用 : 或箭头 | 否 | 否 | 是 |
按键后的值
DateTimePicker | gTimePicker | TimeEdit | |
显示的文本示例 | 00:00 | 00:00 | 00:00 |
按下 [6] 键后显示的文本 | 6:00 | 60:00 | 06:00 |
按下 [6] 键后的值 | 00:00 + 当前日期 | 00:00 | 06:00 |
失去焦点后的值 | 06:00 + 当前日期 | 06:00 | 06:00 |
我为何决定创建它
我需要一个具有以下功能的控件
Value
属性可以设置为仅时间值,例如 00:00 (0001/01/01 00:00),这是 DateTime 类型的最小值Value
属性可以是DBNull.Value
,这对于数据库绑定很有用- 编辑时间时,其值始终反映显示的文本
- 通过最少的输入进行简单的时间输入
- 可以使用鼠标滚轮或按下箭头键来增加或减少其值
与此控件相关的值属性
Value
(Object):DBNull
或WorkingDayDate
和Time
NullableDateTime
(Nullable(ofDateTime
)): Nothing 或WorkingDayDate
和Time
NullableTime
(Nullable(ofDateTime
)): Nothing 或Time
此应用程序的功能及其用途
您可以将此控件包含在您的应用程序中以获得以下功能
- 允许您的应用程序用户以 24 小时制或 12 小时制输入时间
- 保护您的应用程序用户免于输入无效时间
- 如果您的应用程序检查此控件的值,它将始终返回正确的值,即使在控件失去焦点之前,这对于在用户输入时获取值变化很有用
- 您可以将
TimeEdit
控件与WorkingDayDateControl
关联,以便在一个属性中返回日期和时间 - 它是一个简单的控件,继承自
TextBox
,易于理解,并且可以进一步开发为更复杂的控件 - 它是
DateTimePicker
的替代品,并且更易于使用
解决了什么问题
- 当用户更改
TimeEdit
控件的文本时,即使它没有失去焦点,其值也会立即更改。 TimeEdit
控件没有MinValue
,因此它可以指向仅时间值。- 它具有
WorkingDayEndTime
和WorkingDayDate
属性,当在午夜后持续的工作日中安排约会时很有用 - 它可以分配给
WorkingDayDateControl
以从中获取日期部分,并在单个Value
属性中返回日期和时间
关于 TimeCombo 和 TimeColumn
TimeCombo
是基于TimeEdit
的组合框TimeCombo
使用 Flexible ComboBox and EditingControl Library 和 gTimePicker 时钟TimeColumn
是基于TimeCombo
和 Flexible ComboBox and EditingControl Library 的DataGridViewColumn
全球化
即使您的本地文化与 en-US
不同,此控件的行为也始终如一。它始终使用 AM/PM 和 :,即使您的本地文化使用其他格式。
下一步是什么
- 添加下拉按钮以显示时钟以选择时间
- 构建一个约会网格来安排可以跨越午夜而无需中断排序的约会(按时间排序时,00:00 会排在 23:59 之后)
Using the Code
- 您可以在您的 Windows 应用程序中使用
TimeEdit
控件来提及时间 - 如果您构建一个用户控件,您可以使用
TimeConverter
和DateConverter
来处理您的控件属性。
演示应用程序
演示应用程序比较了 DateTimePicker
和 TimeEdit
控件。有两个 TimeEdit
控件,第一个控件的 WorkingDayEndTime
属性设置为 00:00
,第二个 TimeEdit
控件的 WorkingDayEndTime
设置为 02:00
。当在任何 TimeEdit
控件或 DateTimePicker
控件中键入时,控件的值将显示为日期。如果输入的时间小于 WorkingDayEndTime
,则第二个 TimeEdit
控件的日期将设置为 0001/01/02
(yyyy/MM/dd),这在按时间对约会安排进行排序时非常有用。
它还包含使用 TimeColumn
的 DataGridView
。
关注点
如何保持值和文本相同
为此,我们不将值存储在单独的变量中。我们使用显示的文本来获取值。当用户以编程方式设置值时,此控件将分析该值
- 如果新值是数字,则将其视为小时;6.5 将转换为 06:30
- 如果新值是
DateTime
,则WorkingDayDate
属性将设置为新值的日期,NullableTime
属性将设置为新值的时间部分 - 如果新值是空的
string
,则值为DBNull
- 如果新值是包含时间的
string
,则值将返回其值
Public Overridable Property Value As Object
Get
Try
If String.IsNullOrEmpty(MyBase.Text) Then
Return DBNull.Value
Else
Return Me.NullableDateAndTime.Value
End If
Catch ex As Exception
ErrMsg(ex)
Return DBNull.Value
End Try
End Get
Set(ByVal NewValue As Object)
Try
If IsNumeric(NewValue) Then
'Nothing = 00:00
'not include date
Me.NullableTime = (New Date).AddHours(CDbl(NewValue))
ElseIf IsDate(NewValue) Then
'Nothing = 00:00
'include date
Me.NullableDateAndTime = CDate(NewValue)
ElseIf IsDBNull(NewValue) OrElse NewValue Is Nothing Then
Me.Text = ""
ElseIf GetType(TimeSpan).IsAssignableFrom(NewValue.GetType) Then
'not include date
Me.NullableTime = New Date(CType(NewValue, TimeSpan).Ticks)
ElseIf GetType(String).IsAssignableFrom(NewValue.GetType) Then
If String.IsNullOrEmpty(CStr(NewValue)) Then
Me.Text = ""
Else
'don't include date
Me.NullableTime = ToDateTime(CStr(NewValue))
End If
Else
Me.Text = ""
End If
Catch ex As Exception
ErrMsg(ex)
End Try
End Set
End Property
验证时间输入并保护用户免于输入无效时间
- 重写
Sub OnKeyPress
- 如果用户在空的
TimeEdit
控件中按下某个键,它将自动分配默认值 - 构建
sText string
来反映用户输入的結果Dim sText = Me.Text Mid(sText, Me.SelectionStart + 1, 1) = e.KeyChar 'Replace the 2d char
- 使用正则表达式测试结果文本是否为有效的
Time
,24 小时制使用"^(?<Hour>([0-1]\d|2[0-3])):(?<Minute>[0-5]\d)$"
,12 小时制使用"^(?<Hour>(0?[1-9]|1[0-2])):(?<Minute>[0-5]\d) (?<AMPM>(A|P)M)$"
* 此正则表达式包含三个组:小时、分钟和 AM/PM
* 每个组都有其规则,例如 12 小时制中的小时是 0 后跟任意数字或 1 后跟 0-2 - 如果不匹配,则分析编辑
char
在小时或分钟中的位置,并分析其可能的修复方法,如下面的代码所示:Private Sub OnHourKeyPress(ByRef e As KeyPressEventArgs) Try Dim Do1 = False Select Case SelectionStart Case 0 'SelectionStart = 0 Select Case e.KeyChar Case "3"c To "9"c 'not allowed to be first char Do1 = True Case "2"c 'not allowed to be first char in 12 Hr mode If Not _Hr24 Then Do1 = True Else 'in 24 hr mode if the next char is > 3 then replace it with 3 If Me.Text()(1) > "3"c Then Mid(Me.Text, 2, 1) = "3" End If End If Case "1"c If Not _Hr24 Then 'in 12 hr mode if the next char is > 2 then replace it with 2 If Me.Text()(1) > "2"c Then Mid(Me.Text, 2, 1) = "2" End If End If End Select Case 1 'SelectionStart = 1 Select Case MyBase.Text(0) Case Is > "2"c 'in 12 hr mode not allowed to be in 2d char If Not Hr24 Then Do1 = True End If Case "2"c If Me.Hr24 Then Select Case e.KeyChar Case "4"c To "9"c 'in 24 hr mode not allowed to be in 2d char Do1 = True End Select Else Do1 = True End If Case "1"c If Not _Hr24 Then Select Case e.KeyChar Case "3"c To "9"c Do1 = True End Select End If End Select 'MyBase.Text(0) End Select If Do1 Then Me.Text = "0" & Me.Text.Substring(1) SelectionStart = 1 SelectionLength = 1 End If OnTimeKeyPress(e) Catch ex As Exception ErrMsg(ex) End Try End Sub
TimeEdit
控件继承自 TextBox
,但通过以下方式移除了部分属性
- 添加
Designer
属性<ToolboxItem(True), Designer(GetType(TimeEditDesigner)), _ DefaultBindingProperty("Value")> _ Public Class TimeEdit Inherits TextBox '....More Code.... <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), _ Browsable(False), Obsolete("", True)> _ Public Shadows Lines, Multiline, AutoCompleteMode, _ AutoCompleteSource, AutoCompleteCustomSource, WordWrap, _ PasswordChar, UseSystemPasswordChar As DBNull End Class
- 构建
TimeEditDesigner
类Imports System.Windows.Forms.Design Imports System.ComponentModel Public Class TimeEditDesigner Inherits ControlDesigner Protected Overrides Sub PreFilterProperties(ByVal properties As IDictionary) Try properties.Remove("PasswordChar") properties.Remove("UseSystemPasswordChar") properties.Remove("Text") properties.Remove("Lines") properties.Remove("Multiline") properties.Remove("AutoCompleteMode") properties.Remove("AutoCompleteSource") properties.Remove("AutoCompleteCustomSource") properties.Remove("WordWrap") MyBase.PreFilterProperties(properties) Catch ex As Exception ErrMsg(ex) End Try End Sub Public Overrides ReadOnly Property ActionLists As _ System.ComponentModel.Design.DesignerActionListCollection Get Return Nothing End Get End Property Public Overrides ReadOnly Property Verbs As _ System.ComponentModel.Design.DesignerVerbCollection Get Return Nothing End Get End Property End Class
此应用程序如何处理运行时错误
每个过程都包含以下图
Private Sub OnHourKeyPress(ByRef e As KeyPressEventArgs) 'procedure start
Try
'.... procedure code
Catch ex As Exception
ErrMsg(ex)
End Try
End Sub 'procedure end
向项目中添加了一个包含 ErrMsg
过程的模块,用于记录发生的所有运行时错误,文件位于“C:\Temp”。
错误使用如下方式打印到该文件
FileOpen(iFile, sLogFileName, OpenMode.Append, OpenAccess.Write)
Print(iFile, vbNewLine & vbNewLine & "Date: " _
& Now.ToString("yyyy/MM/dd HH:mm:ss") & vbNewLine)
Print(iFile, sMsg)
FileClose(iFile)
历史
- 初始版本
- 添加
TimeCombo
和TimeColumn