我正在寻找一种方法来自动将VBA文本框中的日期格式化为MM / DD / YYYY格式,我希望它在用户输入时格式化。例如,一旦用户输入在第二个数字中,程序将自动输入“/”。现在,我使用以下代码完成了这项工作(以及第二次破折号):
Private Sub txtBoxBDayHim_Change()
If txtBoxBDayHim.TextLength = 2 or txtBoxBDayHim.TextLength = 5 then
txtBoxBDayHim.Text = txtBoxBDayHim.Text + "/"
End Sub
现在,这在输入时效果很好。但是,当尝试删除时,它仍然以破折号输入,因此用户无法删除其中一个短划线(删除破折号会导致长度为2或5,然后再次运行该子,添加另一个破折号)。有关更好的方法的任何建议?
答案 0 :(得分:60)
我从不建议使用文本框或输入框来接受日期。很多事情都可能出错。我甚至不建议使用Calendar Control或日期选择器,因为你需要注册mscal.ocx或mscomct2.ocx,这是非常痛苦的,因为它们不是可自由分发的文件。
这是我推荐的。您可以使用此自定义日历接受来自用户的日期
<强>赞成强>:
<强> CONS 强>:
嗯......嗯......想不出任何......如何使用
Userform1.frm
和Userform1.frx
Userform1.frm
,如下图所示。导入表单
运行它
您可以在任何程序中调用它。例如
Sub Sample()
UserForm1.Show
End Sub
屏幕拍摄动作
注意:您可能还想查看Taking Calendar to new level
答案 1 :(得分:31)
这与Siddharth Rout的答案概念相同。但是我想要一个可以完全定制的日期选择器,以便外观和感觉可以适应它所用的任何项目。
You can click this link下载我想出的自定义日期选择器。以下是该表格的一些截图。
要使用日期选择器,只需将CalendarForm.frm文件导入VBA项目即可。上面的每个日历都可以通过一个函数调用获得。结果只取决于您使用的参数(所有参数都是可选的),因此您可以根据需要自定义它。
例如,左侧最基本的日历可以通过以下代码行获得:
MyDateVariable = CalendarForm.GetDate
这就是它的全部内容。从那里,您只需包含您想要获得所需日历的任何参数。下面的函数调用将生成右侧的绿色日历:
MyDateVariable = CalendarForm.GetDate( _
SelectedDate:=Date, _
DateFontSize:=11, _
TodayButton:=True, _
BackgroundColor:=RGB(242, 248, 238), _
HeaderColor:=RGB(84, 130, 53), _
HeaderFontColor:=RGB(255, 255, 255), _
SubHeaderColor:=RGB(226, 239, 218), _
SubHeaderFontColor:=RGB(55, 86, 35), _
DateColor:=RGB(242, 248, 238), _
DateFontColor:=RGB(55, 86, 35), _
SaturdayFontColor:=RGB(55, 86, 35), _
SundayFontColor:=RGB(55, 86, 35), _
TrailingMonthFontColor:=RGB(106, 163, 67), _
DateHoverColor:=RGB(198, 224, 180), _
DateSelectedColor:=RGB(169, 208, 142), _
TodayFontColor:=RGB(255, 0, 0), _
DateSpecialEffect:=fmSpecialEffectRaised)
这是它包含的一些功能的一小部分。所有选项都在userform模块中完整记录:
答案 2 :(得分:11)
添加内容以跟踪长度,并允许您“检查”用户是在添加还是减去文本。这是目前未经测试的,但类似于此的东西应该有用(特别是如果你有一个用户形式)。
'add this to your userform or make it a static variable if it is not part of a userform
private oldLength as integer
Private Sub txtBoxBDayHim_Change()
if ( oldlength > txboxbdayhim.textlength ) then
oldlength =txtBoxBDayHim.textlength
exit sub
end if
If txtBoxBDayHim.TextLength = 2 or txtBoxBDayHim.TextLength = 5 then
txtBoxBDayHim.Text = txtBoxBDayHim.Text + "/"
end if
oldlength =txtBoxBDayHim.textlength
End Sub
答案 3 :(得分:5)
我也是这样或那样偶然发现同样的困境,为什么Excel VBA没有Date Picker
。感谢Sid,他为我们所有人创造了一些令人敬畏的工作。
尽管如此,我来到了需要创造自己的地步。我在这里发帖,因为很多人我肯定会登陆这个帖子并从中获益。
我所做的事情非常简单,就像Sid所做的那样,除了我不使用临时工作表。我认为计算非常简单直接,因此无需将其转储到其他地方。这是日历的最终输出:
如何设置
Label
控件并按顺序命名,并从左到右,从上到下排列(此标签包含灰色的25
,直至上面的灰色5
。将Label
控件的名称更改为 Label_01 , Label_02 ,依此类推。将所有42个标签Tag
属性设置为dts
。Label
控件(这将包含 Su,Mo,Tu ...... )Label
控件,一个用于水平线(高度设置为1),另一个用于月和年显示。命名用于显示月份和年份的Label
Label_MthYr Image
控件,一个用于包含左侧图标以滚动上个月,另一个用于下个月滚动(我更喜欢简单的左右箭头图标)。将其命名为Image_Left
和Image_Right
布局应该或多或少是这样的(我将创造力留给任何会使用它的人。)
声明:
我们需要在最顶层声明一个变量来保持当前选定的月份。
Option Explicit
Private curMonth As Date
私人程序和功能:
Private Function FirstCalSun(ref_date As Date) As Date
'/* returns the first Calendar sunday */
FirstCalSun = DateSerial(Year(ref_date), _
Month(ref_date), 1) - (Weekday(ref_date) - 1)
End Function
Private Sub Build_Calendar(first_sunday As Date)
'/* This builds the calendar and adds formatting to it */
Dim lDate As MSForms.Label
Dim i As Integer, a_date As Date
For i = 1 To 42
a_date = first_sunday + (i - 1)
Set lDate = Me.Controls("Label_" & Format(i, "00"))
lDate.Caption = Day(a_date)
If Month(a_date) <> Month(curMonth) Then
lDate.ForeColor = &H80000011
Else
If Weekday(a_date) = 1 Then
lDate.ForeColor = &HC0&
Else
lDate.ForeColor = &H80000012
End If
End If
Next
End Sub
Private Sub select_label(msForm_C As MSForms.Control)
'/* Capture the selected date */
Dim i As Integer, sel_date As Date
i = Split(msForm_C.Name, "_")(1) - 1
sel_date = FirstCalSun(curMonth) + i
'/* Transfer the date where you want it to go */
MsgBox sel_date
End Sub
图像事件:
Private Sub Image_Left_Click()
If Month(curMonth) = 1 Then
curMonth = DateSerial(Year(curMonth) - 1, 12, 1)
Else
curMonth = DateSerial(Year(curMonth), Month(curMonth) - 1, 1)
End If
With Me
.Label_MthYr.Caption = Format(curMonth, "mmmm, yyyy")
Build_Calendar FirstCalSun(curMonth)
End With
End Sub
Private Sub Image_Right_Click()
If Month(curMonth) = 12 Then
curMonth = DateSerial(Year(curMonth) + 1, 1, 1)
Else
curMonth = DateSerial(Year(curMonth), Month(curMonth) + 1, 1)
End If
With Me
.Label_MthYr.Caption = Format(curMonth, "mmmm, yyyy")
Build_Calendar FirstCalSun(curMonth)
End With
End Sub
我添加了这个以使其看起来像用户点击标签,并且也应该在Image_Right
控件上完成。
Private Sub Image_Left_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal X As Single, ByVal Y As Single)
Me.Image_Left.BorderStyle = fmBorderStyleSingle
End Sub
Private Sub Image_Left_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal X As Single, ByVal Y As Single)
Me.Image_Left.BorderStyle = fmBorderStyleNone
End Sub
标签事件:
所有42个标签(Label_01
到Lable_42
)的所有内容都应该完成提示:< / strong>构建前10个,只需使用查找和替换剩余的。
Private Sub Label_01_Click()
select_label Me.Label_01
End Sub
这是将鼠标悬停在日期上并单击效果。
Private Sub Label_01_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal X As Single, ByVal Y As Single)
Me.Label_01.BorderStyle = fmBorderStyleSingle
End Sub
Private Sub Label_01_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal X As Single, ByVal Y As Single)
Me.Label_01.BackColor = &H8000000B
End Sub
Private Sub Label_01_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal X As Single, ByVal Y As Single)
Me.Label_01.BorderStyle = fmBorderStyleNone
End Sub
UserForm事件:
Private Sub UserForm_Initialize()
'/* This is to initialize everything */
With Me
curMonth = DateSerial(Year(Date), Month(Date), 1)
.Label_MthYr = Format(curMonth, "mmmm, yyyy")
Build_Calendar FirstCalSun(curMonth)
End With
End Sub
再次,只是为了悬停日期效果。
Private Sub UserForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, _
ByVal X As Single, ByVal Y As Single)
With Me
Dim ctl As MSForms.Control, lb As MSForms.Label
For Each ctl In .Controls
If ctl.Tag = "dts" Then
Set lb = ctl: lb.BackColor = &H80000005
End If
Next
End With
End Sub
就是这样。这是原始的,你可以添加自己的扭曲。
我已经使用了一段时间了,我没有任何问题(性能和功能明智)。尚无Error Handling
但我可以轻松管理。实际上,没有效果,代码太短了。
您可以在select_label
程序中管理日期的去向。 HTH。
答案 4 :(得分:2)
为了好玩,我接受了Siddharth关于单独文本框的建议并做了组合框。如果有人感兴趣,添加一个名为cboDay,cboMonth和cboYear的三个组合框的用户表单,并从左到右排列。然后将下面的代码粘贴到UserForm的代码模块中。所需的组合框属性在UserFormInitialization中设置,因此不需要额外的准备工作。
棘手的部分是因为年或月的变化而变得无效的日子。此代码只会在发生这种情况时将其重置为01并突出显示cboDay。
我有一段时间没有这样的编码。希望有一天某人会感兴趣。如果不是很有趣!
Dim Initializing As Boolean
Private Sub UserForm_Initialize()
Dim i As Long
Dim ctl As MSForms.Control
Dim cbo As MSForms.ComboBox
Initializing = True
With Me
With .cboMonth
' .AddItem "month"
For i = 1 To 12
.AddItem Format(i, "00")
Next i
.Tag = "DateControl"
End With
With .cboDay
' .AddItem "day"
For i = 1 To 31
.AddItem Format(i, "00")
Next i
.Tag = "DateControl"
End With
With .cboYear
' .AddItem "year"
For i = Year(Now()) To Year(Now()) + 12
.AddItem i
Next i
.Tag = "DateControl"
End With
DoEvents
For Each ctl In Me.Controls
If ctl.Tag = "DateControl" Then
Set cbo = ctl
With cbo
.ListIndex = 0
.MatchRequired = True
.MatchEntry = fmMatchEntryComplete
.Style = fmStyleDropDownList
End With
End If
Next ctl
End With
Initializing = False
End Sub
Private Sub cboDay_Change()
If Not Initializing Then
If Not IsValidDate Then
ResetMonth
End If
End If
End Sub
Private Sub cboMonth_Change()
If Not Initializing Then
ResetDayList
If Not IsValidDate Then
ResetMonth
End If
End If
End Sub
Private Sub cboYear_Change()
If Not Initializing Then
ResetDayList
If Not IsValidDate Then
ResetMonth
End If
End If
End Sub
Function IsValidDate() As Boolean
With Me
IsValidDate = IsDate(.cboMonth & "/" & .cboDay & "/" & .cboYear)
End With
End Function
Sub ResetDayList()
Dim i As Long
Dim StartDay As String
With Me.cboDay
StartDay = .Text
For i = 31 To 29 Step -1
On Error Resume Next
.RemoveItem i - 1
On Error GoTo 0
Next i
For i = 29 To 31
If IsDate(Me.cboMonth & "/" & i & "/" & Me.cboYear) Then
.AddItem Format(i, "0")
End If
Next i
On Error Resume Next
.Text = StartDay
If Err.Number <> 0 Then
.SetFocus
.ListIndex = 0
End If
End With
End Sub
Sub ResetMonth()
Me.cboDay.ListIndex = 0
End Sub
答案 5 :(得分:2)
为了快速解决方案,我通常会这样做。
这种方法允许用户在文本框中以他们喜欢的任何格式输入日期,最后在完成编辑后以mm / dd / yyyy格式格式化。所以它非常灵活:
Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
If TextBox1.Text <> "" Then
If IsDate(TextBox1.Text) Then
TextBox1.Text = Format(TextBox1.Text, "mm/dd/yyyy")
Else
MsgBox "Please enter a valid date!"
Cancel = True
End If
End If
End Sub
然而,我认为Sid开发的是一种更好的方法 - 完全成熟的日期选择器控制。
答案 6 :(得分:2)
您也可以在文本框中使用输入掩码。如果您将掩码设置为##/##/####
,它将始终在您键入时进行格式化,除了检查输入的内容是否为真实日期之外,您不需要进行任何编码。
这只是一些简单的行
txtUserName.SetFocus
If IsDate(txtUserName.text) Then
Debug.Print Format(CDate(txtUserName.text), "MM/DD/YYYY")
Else
Debug.Print "Not a real date"
End If
答案 7 :(得分:1)
虽然我同意以下答案中提到的内容,但建议对于Userform来说这是一个非常糟糕的设计,除非包含大量的错误检查...
要完成您需要做的事情,通过对您的代码进行最少的更改,有两种方法。
使用 KeyUp()事件代替文本框的Change事件。这是一个例子:
Private Sub TextBox2_KeyUp(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
Dim TextStr As String
TextStr = TextBox2.Text
If KeyCode <> 8 Then ' i.e. not a backspace
If (Len(TextStr) = 2 Or Len(TextStr) = 5) Then
TextStr = TextStr & "/"
End If
End If
TextBox2.Text = TextStr
End Sub
或者,如果您需要使用更改()事件,请使用以下代码。这改变了行为,因此用户不断输入数字,如
12072003
而他输入的结果显示为
12/07/2003
但是&#39; /&#39;字符只出现在DD的第一个字符,即07的0之后。不理想,但仍会处理退格。
Private Sub TextBox1_Change()
Dim TextStr As String
TextStr = TextBox1.Text
If (Len(TextStr) = 3 And Mid(TextStr, 3, 1) <> "/") Then
TextStr = Left(TextStr, 2) & "/" & Right(TextStr, 1)
ElseIf (Len(TextStr) = 6 And Mid(TextStr, 6, 1) <> "/") Then
TextStr = Left(TextStr, 5) & "/" & Right(TextStr, 1)
End If
TextBox1.Text = TextStr
End Sub
答案 8 :(得分:1)
Private Sub txtBoxBDayHim_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
If KeyAscii >= 48 And KeyAscii <= 57 Or KeyAscii = 8 Then 'only numbers and backspace
If KeyAscii = 8 Then 'if backspace, ignores + "/"
Else
If txtBoxBDayHim.TextLength = 10 Then 'limit textbox to 10 characters
KeyAscii = 0
Else
If txtBoxBDayHim.TextLength = 2 Or txtBoxBDayHim.TextLength = 5 Then 'adds / automatically
txtBoxBDayHim.Text = txtBoxBDayHim.Text + "/"
End If
End If
End If
Else
KeyAscii = 0
End If
End Sub
这对我有用。 :)
你的代码给了我很多帮助。谢谢!
我是巴西人,我的英语很差,对任何错误都感到抱歉。