简体   繁体   中英

Using Excel and VBA, I want to autofill a calendar with start and end date

I've been trying to do this for a while but I feel my VBA skills just aren't up to scratch or I'm missing something super obvious. I've spent a few hours trying to fix this myself and haven't found anything efficient. It's all overly complicated and I can't code the VBA correctly.

What I was attempting to do was VLOOKUP the date and use that as the starting point, then use a function to fill all the days (missing weekends and other bank holidays) with the tag Cells contents so that is shown up on the schedule.

But I can't work out how to do this:

在此处输入图片说明

I would like to be able to change the start and end dates and have the calendar auto fill, seeing as this calendar is a year and a half long I'd like not to have to do it manually.

Thanks for any help anyone is willing to offer.

Right Thank you Tony for your brilliant answer, although it still left me with the problem I was more than able to adapt and create this code to solve my problem.

Sub fillCalendar()

Dim StartDate As Long
Dim finishDate As Long
Dim daystotal As Long
Dim counter As Integer
counter = 0

'start date is 02/01/2016 / 42371

StartDate = ActiveCell.Value
ActiveCell.Offset(0, 1).Activate
finishDate = ActiveCell.Value
daystotal = finishDate - StartDate
ActiveCell.Offset(0, 2).Activate
Selection.Copy

ActiveCell.Offset(0, StartDate - 42371).Select

While counter <= daystotal
If Worksheets("Schedule").Cells(3, ActiveCell.Column) <> "S" Then

    counter = counter + 1
    Worksheets("Schedule").Paste
    ActiveCell.Offset(0, 1).Activate
Else
    counter = counter + 1
    ActiveCell.Offset(0, 1).Activate
End If
Wend



End Sub

This very simple code will check each column to see if it is a weekday or not. If so it will paste the contents of the TAG cell and move along, adding one to the counter. When the counter meets the totaldays count, the while loop exits leaving a populated calendar. Thanks for everyone's help

This is not a direct answer to your question. However, it provides a macro which I believe you will find useful and which demonstrates the techniques you need for the macro you seek.

Before I could do anything with your question, I need some test data. I manually created this worksheet:

工作表的初始状态

I have reduced the size of columns A to F so I could get more on the screen without it being too small. My colours and formatting is probably not identical to yours but, as I will explain later, this does not matter.

The only different that does matter is the title for the month which I have changed from “January” to “January 2016”. I assume “January” is a string. On my worksheet this is the date “1 January 2016” formatted to display as “January 2016”. I could have formatted it as “January” but I wanted to make this change obvious since my macro depends on it.

Getting this January formatted consistently was enough bother. I did not want to have to do the same for February and March. I assume you find adding the headings for an extra month a chore. So I wrote a macro to add a new month to the right of any existing months.

Run the macro once and you get:

添加一个月后的工作表

Run it again and you get:

添加第二个月后的工作表

Getting my macro working took long enough so I have not thought about the macro you seek. However, I am sure my macro demonstrates all the techniques you need.

I only use one range because my macro mainly works with individual cells. However, it shows how to create a range, merge the cells within it and format the merged cell.

I said it did not matter that my formatting is slightly different from yours. My macro formats the new month by copying the formats from the previous month. The macro would be faster if I included Application.ScreenUpdating = False but this is a demonstration macro. Once you have finished studying it, delete the diagnostic statements and add Application.ScreenUpdating = False .

I use functions, such as DateSerial and DateDiff , to calculate values I need. When I was learning VBA, I opened VBA Help and worked down the lists of statements, methods and functions. If something looked useful to me, I would read the text carefully. If it did not look useful, I would read enough to know it was there in case my needs changed. For example, there are a number of exotic financial functions which were of no value to me but I knew there were there if my needs changed. There is a function that calculates the number of working days between two dates which may be useful to you.

Work down my macro. I do not explain the individual VBA statements because they are easy to look up once you know they exist. However, I explain the objective of each block of code. For your macro, you need to copy the value and colours from the Tag cell to appropriate cells to the right. My macro calculates the column number for particular dates and for the last Sunday of the previous month. My macro copies values and formats from one cell to another. These are the techniques you need. Come back with questions as necessary but I believe careful study of my macro will give you all the information you need.

Option Explicit

  ' Constants are a convenient way of defining important values:
  '  * If the value changes, one amendment here and the macro(s) that use the
  '    value are immediately updated.
  '  * It makes the code easier to read because "magic numbers" are replaced by
  '    meaningful names.
  Const ColDateStart As Long = 5        ' 5 = Column E
  Const ColDateEnd As Long = 6          ' 6 = Column F
  Const ColDateTag As Long = 8          ' 8 = Column H
  Const ColFirstDay As Long = 9         ' 9 = Column I
  Const RowMonth As Long = 1            ' Row for month names
  Const RowDow As Long = 2              ' Row for days of week as initial letters
  Const RowDom As Long = 3              ' Row for days of month as numbers
  Const RowDataFirst As Long = 4
  Const WshtName As String = "Calendar" ' Amend for your name for the worksheet
Sub AddExtraMonth()

  Dim BorderDayLeftColor As Long
  Dim BorderDayLeftLineStyle As Long
  Dim BorderDayLeftWeight As Long
  Dim BorderMonthRightColor As Long
  Dim BorderMonthRightLineStyle As Long
  Dim BorderMonthRightWeight As Long
  Dim ColCrnt As Long
  Dim ColEndNewMonth As Long
  Dim ColStartNewMonth As Long
  Dim ColLastRowMonth As Long
  Dim ColOffset As Long
  Dim ColSunday As Long
  Dim ColSource As Long
  Dim Dom As Long
  Dim DowMonthNewStart As Long
  Dim FontColor As Long
  Dim InteriorColor As Long
  Dim MonthCrntLast As Date
  Dim MonthNewEnd As Date
  Dim MonthNewStart As Date
  Dim NumDaysNewMonth As Long
  Dim NumberFormat As String
  Dim Pattern As Long
  Dim PatternColorIndex As Long
  Dim RngRowMonththNew As Range
  Dim RowCrnt As Long
  Dim RowLast As Long

  With Sheets(WshtName)

    ' Get useful column and row numbers
    ColLastRowMonth = .Cells(RowMonth, Columns.Count).End(xlToLeft).Column
    ColStartNewMonth = .Cells(RowDow, Columns.Count).End(xlToLeft).Column + 1
    RowLast = .Cells(Rows.Count, ColDateStart).End(xlUp).Row

    Debug.Print "ColLastRowMonth " & ColLastRowMonth
    Debug.Print "ColStartNewMonth " & ColStartNewMonth
    Debug.Print "RowLast " & RowLast

    ' Get last current month. Calculate first and last day of new month
    MonthCrntLast = .Cells(RowMonth, ColLastRowMonth)  ' Last value in row RowMonth is current last month
    MonthNewStart = DateSerial(Year(MonthCrntLast), Month(MonthCrntLast) + 1, 1)
    MonthNewEnd = DateSerial(Year(MonthNewStart), Month(MonthNewStart) + 1, 0)

    Debug.Print "MonthCrntLast " & Format(MonthCrntLast, "ddd d mmm yy")
    Debug.Print "MonthNewStart " & Format(MonthNewStart, "ddd d mmm yy")
    Debug.Print "MonthNewEnd " & Format(MonthNewEnd, "ddd d mmm yy")

    ' Calculate column of last sunday of current last month
    DowMonthNewStart = Weekday(MonthNewStart)
    ColSunday = ColStartNewMonth - DowMonthNewStart + 1

    Debug.Print "DowMonthNewStart " & DowMonthNewStart
    Debug.Print "ColSunday " & ColSunday & " = " & ColNumToCode(ColSunday)

    ' Calculate number of days in new month.
    ' Calcutate last column of new month
    NumDaysNewMonth = DateDiff("d", MonthNewStart, MonthNewEnd) + 1
    ColEndNewMonth = ColStartNewMonth + NumDaysNewMonth - 1

    Debug.Print "NumDaysNewMonth " & NumDaysNewMonth
    Debug.Print "ColStartNewMonth " & ColStartNewMonth & " = " & ColNumToCode(ColStartNewMonth)
    Debug.Print "ColEndNewMonth " & ColEndNewMonth & " = " & ColNumToCode(ColEndNewMonth)

    ' Calulate range for new month within row RowMonth
    Set RngRowMonththNew = .Range(.Cells(RowMonth, ColStartNewMonth), .Cells(RowMonth, ColEndNewMonth))

    Debug.Print "RngRowMonththNew " & Replace(RngRowMonththNew.Address, "$", "")

    ' Size new columns to match columns from previous month
    RngRowMonththNew.Columns.ColumnWidth = .Cells(RowMonth, ColStartNewMonth - 2).ColumnWidth

    ' Get formats from previous month
    With .Cells(RowMonth, ColLastRowMonth)
      FontColor = .Font.Color
      InteriorColor = .Interior.Color
      NumberFormat = .NumberFormat
    End With

    With .Cells(RowDow, ColStartNewMonth - 1)
      ' Note these value are not used until the end
      BorderMonthRightLineStyle = .Borders(xlEdgeRight).LineStyle
      BorderMonthRightWeight = .Borders(xlEdgeRight).Weight
      BorderMonthRightColor = .Borders(xlEdgeRight).Color
    End With

    ' Merge and format cells to contain month name
    RngRowMonththNew.Merge
    With .Cells(RowMonth, ColStartNewMonth)
      .Value = MonthNewStart
      .NumberFormat = NumberFormat
      .HorizontalAlignment = xlCenter
      .Font.Color = FontColor
      .Interior.Color = InteriorColor
    End With

    With .Cells(RowMonth, ColEndNewMonth)
      With .Borders(xlEdgeRight)
        .LineStyle = BorderMonthRightLineStyle
        .Weight = BorderMonthRightWeight
        .Color = BorderMonthRightColor
      End With
    End With

    ' Copy value and formats for new month within RowDow from cells in middle of previous month
    ColSource = ColStartNewMonth - 14
    ColOffset = 0
    For ColCrnt = ColStartNewMonth To ColEndNewMonth
      .Cells(RowDow, ColCrnt).Value = .Cells(RowDow, ColSource + ColOffset).Value
      .Cells(RowDow, ColCrnt).HorizontalAlignment = .Cells(RowDow, ColSource + ColOffset).HorizontalAlignment
      .Cells(RowDow, ColCrnt).Font.Color = .Cells(RowDow, ColSource + ColOffset).Font.Color
      .Cells(RowDow, ColCrnt).Interior.Color = .Cells(RowDow, ColSource + ColOffset).Interior.Color
      If ColCrnt > ColStartNewMonth Then
        ' Only set left border if not firsst day of month so month border untouched
        .Cells(RowDow, ColCrnt).Borders(xlEdgeLeft).LineStyle = .Cells(RowDow, ColSource + ColOffset).Borders(xlEdgeLeft).LineStyle
        .Cells(RowDow, ColCrnt).Borders(xlEdgeLeft).Weight = .Cells(RowDow, ColSource + ColOffset).Borders(xlEdgeLeft).Weight
        .Cells(RowDow, ColCrnt).Borders(xlEdgeLeft).Color = .Cells(RowDow, ColSource + ColOffset).Borders(xlEdgeLeft).Color
      End If
      .Cells(RowDow, ColCrnt).Borders(xlEdgeBottom).LineStyle = .Cells(RowDow, ColSource + ColOffset).Borders(xlEdgeBottom).LineStyle
      .Cells(RowDow, ColCrnt).Borders(xlEdgeBottom).Weight = .Cells(RowDow, ColSource + ColOffset).Borders(xlEdgeBottom).Weight
      .Cells(RowDow, ColCrnt).Borders(xlEdgeBottom).Color = .Cells(RowDow, ColSource + ColOffset).Borders(xlEdgeBottom).Color
      ColOffset = ColOffset + 1
      If ColOffset = 7 Then
        ColOffset = 0
      End If
    Next

    ' Set right border for month for row RowDow
    With .Cells(RowDow, ColEndNewMonth).Borders(xlEdgeRight)
      .LineStyle = BorderMonthRightLineStyle
      .Weight = BorderMonthRightWeight
      .Color = .Color
    End With

    ' Copy formats for new month within RowDom from cells in middle of previous month
    ' Set days of month
    ColSource = ColStartNewMonth - 14
    ColOffset = 0
    Dom = 1
    For ColCrnt = ColStartNewMonth To ColEndNewMonth
      .Cells(RowDom, ColCrnt).Value = Dom
      .Cells(RowDom, ColCrnt).HorizontalAlignment = .Cells(RowDom, ColSource + ColOffset).HorizontalAlignment
      .Cells(RowDom, ColCrnt).Font.Color = .Cells(RowDom, ColSource + ColOffset).Font.Color
      .Cells(RowDom, ColCrnt).Interior.Color = .Cells(RowDom, ColSource + ColOffset).Interior.Color
      If ColCrnt > ColStartNewMonth Then
        ' Only set left border if not firsst day of month so month border untouched
        .Cells(RowDom, ColCrnt).Borders(xlEdgeLeft).LineStyle = .Cells(RowDom, ColSource + ColOffset).Borders(xlEdgeLeft).LineStyle
        .Cells(RowDom, ColCrnt).Borders(xlEdgeLeft).Weight = .Cells(RowDom, ColSource + ColOffset).Borders(xlEdgeLeft).Weight
        .Cells(RowDom, ColCrnt).Borders(xlEdgeLeft).Color = .Cells(RowDom, ColSource + ColOffset).Borders(xlEdgeLeft).Color
      End If
      .Cells(RowDom, ColCrnt).Borders(xlEdgeBottom).LineStyle = .Cells(RowDom, ColSource + ColOffset).Borders(xlEdgeBottom).LineStyle
      .Cells(RowDom, ColCrnt).Borders(xlEdgeBottom).Weight = .Cells(RowDom, ColSource + ColOffset).Borders(xlEdgeBottom).Weight
      .Cells(RowDom, ColCrnt).Borders(xlEdgeBottom).Color = .Cells(RowDom, ColSource + ColOffset).Borders(xlEdgeBottom).Color
      ColOffset = ColOffset + 1
      If ColOffset = 7 Then
        ColOffset = 0
      End If
      Dom = Dom + 1
    Next

    ' Set right border for month for row RowDom
    With .Cells(RowDom, ColEndNewMonth).Borders(xlEdgeRight)
      .LineStyle = BorderMonthRightLineStyle
      .Weight = BorderMonthRightWeight
      .Color = .Color
    End With

    ' Get pattern for last Sunday.  Assume same pattern used for Saturdays
    With .Cells(RowDataFirst, ColSunday)
      InteriorColor = .Interior.Color
      Pattern = .Interior.Pattern
      PatternColorIndex = .Interior.PatternColorIndex
      ' Get borders for last Sunday. Assume left border used for all borders
      With .Borders(xlEdgeLeft)
        BorderDayLeftLineStyle = .LineStyle
        BorderDayLeftWeight = .Weight
        BorderDayLeftColor = .Color
      End With
    End With

    ColCrnt = ColSunday + 6         ' Column for first Saturday of new month
    Do While True
      ' Set pattern for Saturday and Sunday for every data row
      For RowCrnt = RowDataFirst To RowLast
        .Cells(RowCrnt, ColCrnt).Interior.Pattern = Pattern
        .Cells(RowCrnt, ColCrnt).Interior.PatternColorIndex = PatternColorIndex
        .Cells(RowCrnt, ColCrnt).Interior.Color = InteriorColor
      Next
      ColCrnt = ColCrnt + 1     ' Advance to Sunday
      If ColCrnt > ColEndNewMonth Then
        ' All Saturdays and Sundays of new month marked
        Exit Do
      End If
      For RowCrnt = RowDataFirst To RowLast
        .Cells(RowCrnt, ColCrnt).Interior.Pattern = Pattern
        .Cells(RowCrnt, ColCrnt).Interior.PatternColorIndex = PatternColorIndex
        .Cells(RowCrnt, ColCrnt).Interior.Color = InteriorColor
      Next
      ColCrnt = ColCrnt + 6     ' Advance to next Saturday
      If ColCrnt > ColEndNewMonth Then
        ' All Saturdays and Sundays of new month marked
        Exit Do
      End If
    Loop

    ' Set borders of data cells for new month
    For RowCrnt = RowDataFirst To RowLast
      For ColCrnt = ColStartNewMonth To ColEndNewMonth
        If ColCrnt > ColStartNewMonth Then
          ' Only set border if not first day of month so left border for month untouched
          With .Cells(RowCrnt, ColCrnt)
            With .Borders(xlEdgeLeft)
              .LineStyle = BorderDayLeftLineStyle
              .Weight = BorderDayLeftWeight
              .Color = BorderDayLeftColor
            End With
          End With
        End If
        With .Cells(RowCrnt, ColCrnt)
          With .Borders(xlEdgeBottom)
            .LineStyle = BorderDayLeftLineStyle
            .Weight = BorderDayLeftWeight
            .Color = BorderDayLeftColor
          End With
        End With
      Next
      ' Set right border for month
      With .Cells(RowCrnt, ColEndNewMonth)
        With .Borders(xlEdgeRight)
          .LineStyle = BorderMonthRightLineStyle
          .Weight = BorderMonthRightWeight
          .Color = BorderMonthRightColor
        End With
      End With
    Next

  End With

End Sub
Function ColNumToCode(ByVal ColNum As Long) As String

  Dim ColCode As String
  Dim PartNum As Long

  ' Last updated 3 Feb 12.  Adapted to handle three character codes.

  If ColNum = 0 Then
    ColNumToCode = "0"
  Else
    ColCode = ""
    Do While ColNum > 0
      PartNum = (ColNum - 1) Mod 26
      ColCode = Chr(65 + PartNum) & ColCode
      ColNum = (ColNum - PartNum - 1) \ 26
    Loop
  End If

  ColNumToCode = ColCode

End Function

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM