我正在构建一个函数,当您选择一个单元格时,输出是从选择到同一列上最后一个填充单元格行的单元格范围。
这是完美的代码。
+??P?b??0
?rGNh?o(5*?<????x????b??7;1?ƻ?H?L1?>?O~&?O??E??Ӎ?5c?62?? ~?????ܙKv=?E???U?d?
问题: 我想更新此代码,以便在用于日期时,用户可以使用三个选项进行过滤:YTD(年初至今),ALL(所有时间 - 即获取所有数据),一年(即2015 / 2014/2013等) 。)
我的最终目标是让用户选择日期范围列中的单元格并输入 YTD 或 ALL 或指定年份(即 2014 < / em>)并使用他的过滤器获得范围。
示例:用户写''Get the cell range from selection to last cell
Function CellRange(CellA As Range)
CellRange = CellA.Address + ":" + CellA.End(xlDown).Address
End Function
,=cellrange(A2,2014)
会产生$A$2:$A$23
,如果用户更改为=cellrange(A2,2014)
,则会产生$A$24:$A$40
在图像上。
我尝试了各种循环或计数,但我觉得很失落,因为我的尝试显然没有任何意义。
我正在寻求一些帮助:指导或问题的解决方案最好,因为我想在解决这个问题之后建立它(因此我在VBA上这样做)。
答案 0 :(得分:1)
我已经编写了一些代码,我认为这些代码可以捕捉到您尝试做的事情。我将以几点为前言。 (1)如果#Value
不是CellA
值,则代码会抛出Date
错误(我认为这是出于不言自明的原因)。 (2)如果公式中的年份条目与CellA
中的年份不匹配,则它也会抛出#Value
。我不确定你是否想要退回这种类型的治疗方法,但我个人认为,如果用户指向CellA
,2014年为一年,那么对于用户而言,这将是非常令人困惑的。重新寻找2013年的日期。如果你想要改变,请告诉我。
看看代码,给它一些测试用例,并告诉我是否需要修改其他任何内容。
基于新信息编辑: 我没有像往常那样花时间测试这段代码,但看看它是否适合你。
Function cellrange(cellA As Range, vFilter As Variant) As String
Dim rStart As Range
Dim rEnd As Range
Dim bFinished As Boolean
Dim dToday As Date
Dim nOffset As Integer
'Throw an error if cell is not a date cell
If Not IsDate(cellA) Then
cellrange = CVErr(xlErrValue)
End If
If IsNumeric(vFilter) Then
If vFilter = Year(cellA) Then
'Below code if there is a year entered as vFilter
Set rStart = cellA
bFinished = False
'Loop to find start of year range
Do
If IsDate(rStart.Offset(-1)) Then
If Year(rStart.Offset(-1)) = vFilter Then
Set rStart = rStart.Offset(-1)
Else
bFinished = True
End If
Else
bFinished = True
End If
Loop While bFinished = False
'Loop to find end of year range
Set rEnd = cellA
bFinished = False
Do
If IsDate(rEnd.Offset(1)) Then
If Year(rEnd.Offset(1)) = vFilter Then
Set rEnd = rEnd.Offset(1)
Else
bFinished = True
End If
Else
bFinished = True
End If
Loop While bFinished = False
cellrange = rStart.Address & ":" & rEnd.Address
Else
If Year(cellA) > vFilter Then
nOffset = -1
Else
nOffset = 1
End If
Set rEnd = cellA
bFinished = False
Do
If IsDate(rEnd.Offset(nOffset)) Then
If Year(rEnd.Offset(nOffset)) <> vFilter Then
Set rEnd = rEnd.Offset(nOffset)
Else
Set rEnd = rEnd.Offset(nOffset)
bFinished = True
End If
Else
bFinished = True
End If
Loop While bFinished = False
Set rStart = rEnd
bFinished = False
Do
If IsDate(rStart.Offset(nOffset)) Then
If Year(rStart.Offset(nOffset)) = Year(rStart) Then
Set rStart = rStart.Offset(nOffset)
Else
bFinished = True
End If
Else
bFinished = True
End If
Loop While bFinished = False
If nOffset = -1 Then
cellrange = rStart.Address & ":" & rEnd.Address
Else
cellrange = rEnd.Address & ":" & rStart.Address
End If
End If
Else
If vFilter = "YTD" Then
'Below code if there is 'YTD' entered as vFilter
Set rStart = cellA
bFinished = False
dToday = Date
'Loop to find start of year range
Do
If IsDate(rStart.Offset(-1)) Then
If Year(rStart.Offset(-1)) = Year(rStart) Then
Set rStart = rStart.Offset(-1)
Else
bFinished = True
End If
Else
bFinished = True
End If
Loop While bFinished = False
'Loop to find end of year range
Set rEnd = cellA
bFinished = False
Do
If rEnd > dToday Then
nOffset = -1
If IsDate(rEnd.Offset(nOffset)) Then
If Year(rEnd.Offset(nOffset)) = Year(rEnd) And rEnd.Offset(nOffset) >= dToday Then
Set rEnd = rEnd.Offset(nOffset)
Else
bFinished = True
End If
Else
bFinished = True
End If
Else
nOffset = 1
If IsDate(rEnd.Offset(nOffset)) Then
If Year(rEnd.Offset(nOffset)) = Year(rEnd) And rEnd.Offset(nOffset) <= dToday Then
Set rEnd = rEnd.Offset(nOffset)
Else
bFinished = True
End If
Else
bFinished = True
End If
End If
Loop While bFinished = False
cellrange = rStart.Address & ":" & rEnd.Address
Else
'Below returns the 'ALL' case
Set rStart = cellA
bFinished = False
'Loop to find start of year range
Do
If IsDate(rStart.Offset(-1)) Then
Set rStart = rStart.Offset(-1)
Else
bFinished = True
End If
Loop While bFinished = False
'Loop to find end of year range
Set rEnd = cellA
bFinished = False
Do
If IsDate(rEnd.Offset(1)) Then
Set rEnd = rEnd.Offset(1)
Else
bFinished = True
End If
Loop While bFinished = False
cellrange = rStart.Address & ":" & rEnd.Address
End If
End If
End Function
较旧的预编辑代码
Function cellrange(cellA As Range, vFilter As Variant) As String
Dim rStart As Range
Dim rEnd As Range
Dim bFinished As Boolean
Dim dToday As Date
Dim nOffset As Integer
'Throw an error if cell is not a date cell
If Not IsDate(cellA) Then
cellrange = CVErr(xlErrValue)
End If
'Throw an error if the cell year does not match the value being searched
If IsNumeric(vFilter) And vFilter <> Year(cellA) Then
cellrange = CVErr(xlErrValue)
End If
If IsNumeric(vFilter) Then
'Below code if there is a year entered as vFilter
Set rStart = cellA
bFinished = False
'Loop to find start of year range
Do
If IsDate(rStart.Offset(-1)) Then
If Year(rStart.Offset(-1)) = vFilter Then
Set rStart = rStart.Offset(-1)
Else
bFinished = True
End If
Else
bFinished = True
End If
Loop While bFinished = False
'Loop to find end of year range
Set rEnd = cellA
bFinished = False
Do
If IsDate(rEnd.Offset(1)) Then
If Year(rEnd.Offset(1)) = vFilter Then
Set rEnd = rEnd.Offset(1)
Else
bFinished = True
End If
Else
bFinished = True
End If
Loop While bFinished = False
cellrange = rStart.Address & ":" & rEnd.Address
Else
If vFilter = "YTD" Then
'Below code if there is 'YTD' entered as vFilter
Set rStart = cellA
bFinished = False
dToday = Date
'Loop to find start of year range
Do
If IsDate(rStart.Offset(-1)) Then
If Year(rStart.Offset(-1)) = Year(rStart) Then
Set rStart = rStart.Offset(-1)
Else
bFinished = True
End If
Else
bFinished = True
End If
Loop While bFinished = False
'Loop to find end of year range
Set rEnd = cellA
bFinished = False
Do
If rEnd > dToday Then
nOffset = -1
If IsDate(rEnd.Offset(nOffset)) Then
If Year(rEnd.Offset(nOffset)) = Year(rEnd) And rEnd.Offset(nOffset) >= dToday Then
Set rEnd = rEnd.Offset(nOffset)
Else
bFinished = True
End If
Else
bFinished = True
End If
Else
nOffset = 1
If IsDate(rEnd.Offset(nOffset)) Then
If Year(rEnd.Offset(nOffset)) = Year(rEnd) And rEnd.Offset(nOffset) <= dToday Then
Set rEnd = rEnd.Offset(nOffset)
Else
bFinished = True
End If
Else
bFinished = True
End If
End If
' If IsDate(rEnd.Offset(nOffset)) Then
' If Year(rEnd.Offset(nOffset)) = Year(rEnd) And rEnd.Offset(nOffset) < dToday Then
' Set rEnd = rEnd.Offset(nOffset)
' Else
' bFinished = True
' End If
' Else
' bFinished = True
' End If
Loop While bFinished = False
cellrange = rStart.Address & ":" & rEnd.Address
Else
'Below returns the 'ALL' case
Set rStart = cellA
bFinished = False
'Loop to find start of year range
Do
If IsDate(rStart.Offset(-1)) Then
Set rStart = rStart.Offset(-1)
Else
bFinished = True
End If
Loop While bFinished = False
'Loop to find end of year range
Set rEnd = cellA
bFinished = False
Do
If IsDate(rEnd.Offset(1)) Then
Set rEnd = rEnd.Offset(1)
Else
bFinished = True
End If
Loop While bFinished = False
cellrange = rStart.Address & ":" & rEnd.Address
End If
End If
End Function
答案 1 :(得分:1)
稍微紧凑的功能...
要在电子表格中使用它,枚举值不会起作用;例如。使用&#39; = CellRange(C3,1)&#39;
Public Enum xlDateAction
xlYearToDate = 1
xlCurrentYear = 2
xlAll = 3
End Enum
Public Function CellRange(SrcCell As Range, DtRange As xlDateAction) As String
Application.ScreenUpdating = False
If Not IsDate(SrcCell.Value) Then Exit Function
Dim CellDate As Date: CellDate = SrcCell.Value
Dim EndCell As Range
Set EndCell = Columns(SrcCell.Column).Find(What:="", After:=[SrcCell]).Offset(-1, 0)
Dim StartCell As Range: Set StartCell = SrcCell
Do Until StartCell.Row = 1 Or Not IsDate(StartCell.Value)
Set StartCell = StartCell.Offset(-1, 0)
Loop
If Not IsDate(StartCell.Value) Then Set StartCell = StartCell.Offset(1, 0)
If DtRange <> xlAll Then
Dim SrcYear As Long: SrcYear = Year(CDate(SrcCell.Value))
Do Until StartCell.Address = SrcCell.Address Or Year(CDate(StartCell.Value)) = SrcYear
If Year(CDate(StartCell.Value)) < SrcYear Then Set StartCell = StartCell.Offset(1, 0)
Loop
If DtRange = xlCurrentYear Then
Do Until EndCell.Address = SrcCell.Address Or Year(CDate(EndCell.Value)) = SrcYear
If Year(CDate(EndCell.Value)) > SrcYear Then Set EndCell = EndCell.Offset(-1, 0)
Loop
Else
Set EndCell = SrcCell
End If
End If
CellRange = Range(StartCell, EndCell).Address
Application.ScreenUpdating = True
End Function
******* 更新 *******
添加了一年覆盖功能,我认为现在应该进行你想要的范围选择...(还调整了枚举,因为这对我来说更有意义)
Public Enum xlDateAction
xlCurrentYear = 1
xlYearToDate = 2
xlAll = 3
End Enum
Public Function CellRange(SrcCell As Range, DtRange As xlDateAction, _
Optional YearOverride As Long = 0) As String
Application.ScreenUpdating = False
If Not IsDate(SrcCell.Value) Then Exit Function
If YearOverride = Year(CDate(SrcCell.Value)) Then YearOverride = 0
Dim TargetYear As Long: TargetYear = YearOverride
Dim StartCell As Range: Set StartCell = SrcCell
Dim EndCell As Range
Set EndCell = Columns(SrcCell.Column).Find(What:="", After:=[SrcCell]).Offset(-1, 0)
Do Until StartCell.Row = 1 Or Not IsDate(StartCell.Value)
Set StartCell = StartCell.Offset(-1, 0)
Loop
If Not IsDate(StartCell.Value) Then Set StartCell = StartCell.Offset(1, 0)
If TargetYear = 0 Then TargetYear = Year(CDate(SrcCell.Value))
If DtRange <> xlAll Then
Do Until StartCell.Address = EndCell.Address Or Year(CDate(StartCell.Value)) >= TargetYear
If Year(CDate(StartCell.Value)) < TargetYear Then Set StartCell = StartCell.Offset(1, 0)
Loop
If DtRange = xlYearToDate And Year(CDate(StartCell.Value)) >= TargetYear And _
TargetYear > Year(CDate(SrcCell.Value)) Then Set StartCell = StartCell.Offset(-1, 0)
If DtRange = xlCurrentYear Then
Do Until EndCell.Address = StartCell.Address Or Year(CDate(EndCell.Value)) <= TargetYear
If Year(CDate(EndCell.Value)) > TargetYear Then Set EndCell = EndCell.Offset(-1, 0)
Loop
' If target year doesn't exist in dates
If Year(CDate(EndCell.Value)) <> TargetYear Then Exit Function
Else
Set EndCell = SrcCell
End If
End If
CellRange = Range(StartCell, EndCell).Address
Application.ScreenUpdating = True
End Function
答案 2 :(得分:1)
这是一个更短的解决方案,适用于所有三种情况,并且不需要数据工作表处于活动状态:
Public Function cellrange(rDates As Range, vFilter As Variant) As String
Dim i As Long, ndx1 As Long, ndx2 As Long, r As Range, vA As Variant, bErr As Boolean, bAll As Boolean
bErr = True
If IsDate(rDates) Then
With rDates.EntireColumn
i = rDates.Parent.Evaluate("count(" & .Address & ")")
Set r = .Cells(1 - i + rDates.Parent.Evaluate("index(" & .Address & ",match(9.9E+307," & .Address & "))").Row).Resize(i, 1)
End With
vA = r.Value
Select Case LCase(vFilter)
Case "all": bErr = 0: bAll = 1
Case "ytd"
For i = 1 To UBound(vA)
If ndx1 = 0 And Year(vA(i, 1)) = Year(Date) Then ndx1 = i
If vA(i, 1) <= Date Then ndx2 = i
Next
Case Else 'year
vFilter = Val(vFilter)
If vFilter Then
For i = 1 To UBound(vA)
If ndx1 = 0 And Year(vA(i, 1)) = vFilter Then ndx1 = i
If ndx1 And Year(vA(i, 1)) = vFilter Then ndx2 = i
Next
End If
End Select
If Not bAll Then If ndx1 > 0 And ndx2 > 0 Then Set r = r.Range(r.Parent.Cells(ndx1, 1), r.Parent.Cells(ndx2, 1)): bErr = False
If Not bErr Then cellrange = r.Address Else cellrange = CVErr(xlErrValue)
End If
End Function
答案 3 :(得分:0)