Subscribe to Code For Excel And Outlook Blog and never miss an entry!
Subscribe to this feed All Posts (RSS) | Subscribe to this feed All Comments (RSS)

Using Excel VBA to set up Task Reminders in Outlook
November 2, 2008 – 10:41 pm by JP

    I know I have trouble remembering when different projects or tasks are due. Here's one way to remember: Set up a list of tasks and due dates in Excel, then use VBA to set up reminders in Outlook.

    It's perfect for things like birthdays, holidays, anniversaries, etc, anytime you want a reminder prior to the actual event, instead of the usual task reminders which seem to pop up right when something is due.

    This code will silently create a task reminder in Outlook a set number of days before the date you specify. I say "silently," because this code does not trigger the Outlook object model guard -- it creates tasks without any popups whatsoever!

    Paste this code into a standard module in Excel:

VBA:
  1. Dim bWeStartedOutlook As Boolean
  2.  
  3. Function AddToTasks(strDate As String, strText As String, DaysOut As Integer) As Boolean
  4. ' Adds a task reminder to Outlook Tasks a specific number of days before the date specified
  5. ' Returns TRUE if successful
  6. ' Will not trigger OMG because no protected properties are accessed
  7. ' by Jimmy Pena, http://www.codeforexcelandoutlook.com, 10/30/2008
  8. '
  9. ' Usage:
  10. ' =AddToTasks("12/31/2008", "Something to remember", 30)
  11. ' or:
  12. ' =AddToTasks(A1, A2, A3)
  13. ' where A1 contains valid date, A2 contains task information, A3 contains number of days before A1 date to trigger task reminder
  14. '
  15. ' can also be used in VBA :
  16. 'If AddToTasks("12/31/2008", "Christmas shopping", 30) Then
  17. '  MsgBox "ok!"
  18. 'End If
  19.  
  20. Dim intDaysBack As Integer
  21. Dim dteDate As Date
  22. Dim olApp As Outlook.Application
  23. Dim objTask As Outlook.TaskItem
  24.  
  25. ' make sure all fields were filled in
  26. If (Not IsDate(strDate)) Or (strText = "") Or (DaysOut <= 0) Then
  27.   AddToTasks = False
  28.   GoTo ExitProc
  29. End If
  30.  
  31. ' We want the task reminder a certain number of days BEFORE the due date
  32. ' ex: if DaysOut = 120, then we want the due date to be -120 before the date specified
  33. ' we need to pass -120 to the NextBusinessDay function, so to go from 120 to -120,
  34. ' we subtract double the number (240) from the number provided (120).
  35. ' 120 - (120 * 2); 120 - 240 = -120
  36.  
  37. intDaysBack = DaysOut - (DaysOut * 2)
  38.  
  39. dteDate = NextBusinessDay(CDate(strDate), intDaysBack)
  40.  
  41. On Error Resume Next
  42.   Set olApp = GetOutlookApp
  43. On Error GoTo 0
  44.  
  45. If Not olApp Is Nothing Then
  46.   Set objTask = olApp.CreateItem(3)  ' task item
  47.  
  48.   With objTask
  49.     .StartDate = dteDate
  50.     .Subject = strText & ", due on: " & strDate
  51.     .ReminderSet = True
  52.     .Save
  53.   End With
  54.  
  55. Else
  56.   AddToTasks = False
  57.   GoTo ExitProc
  58. End If
  59.  
  60. ' if we got this far, it must have worked
  61. AddToTasks = True
  62.  
  63. ExitProc:
  64. If bWeStartedOutlook Then
  65.   olApp.Quit
  66. End If
  67. Set olApp = Nothing
  68. Set objTask = Nothing
  69. End Function
  70.  
  71. Function NextBusinessDay(dteDate As Date, intAhead As Integer) As Date
  72. ' from Sue Mosher's book "Microsoft Outlook Programming"
  73. Dim dteNextDate As Date
  74.  
  75. dteNextDate = DateAdd("d", intAhead, dteDate)
  76.  
  77. Select Case Weekday(dteNextDate)
  78.  
  79. ' if Sunday, add 1 day to make it next business day (Monday)
  80.     Case 1
  81.         dteNextDate = dteNextDate + 1
  82. ' if Saturday, add 2 days to make it next business day (Monday)
  83.     Case 7
  84.         dteNextDate = dteNextDate + 2
  85. End Select
  86. NextBusinessDay = dteNextDate
  87.  
  88. End Function
  89.  
  90. Function GetOutlookApp() As Object
  91.  
  92. On Error Resume Next
  93.   Set GetOutlookApp = GetObject(, "Outlook.Application")
  94.   If Err.Number <> 0 Then
  95.     Set GetOutlookApp = CreateObject("Outlook.Application")
  96.     bWeStartedOutlook = True
  97.   End If
  98. On Error GoTo 0
  99.  
  100. End Function

addtotasks-300x125

    After you get TRUE or FALSE in the target cell, you'll want to delete the function, so it doesn't keep trying (and succeeding) to add task reminders over and over every time the worksheet recalculates.

--JP

Share and Enjoy:
  • StumbleUpon
  • Technorati
  • Digg
  • Google
  • del.icio.us
  • MisterWong

Check if Workbook or Worksheet is Protected
November 2, 2008 – 3:13 pm by JP

Protected Sheet

    I was looking for a function to check if a worksheet is protected. I googled the appropriate phrase ("check if worksheet is protected") and followed the first link: an Excel function posted at vbaexpress.com: VBA Express : Excel - Check if Worksheet is protected.

    Here is a copy of the code:

VBA:
  1. Private Function SheetProtected(TargetSheet As Worksheet) As Boolean
  2.      'Function purpose:  To evaluate if a worksheet is protected
  3.      
  4.     If TargetSheet.ProtectContents = True Then
  5.         SheetProtected = True
  6.     Else
  7.         SheetProtected = False
  8.     End If
  9.      
  10. End Function

    Works well, but what about when you want to check if a whole workbook is protected? You need a second function. And what if you want to check both at the same time? Or a Range? Looks like it's time to roll your own!

    This function takes an Excel.Workbook, Excel.Worksheet, or Excel.Range Object as an argument, and returns TRUE if any one is protected. Let me explain.

    If you pass a Worksheet Object, it will simply check if the worksheet is protected. If you pass a Workbook Object, it will check if the workbook is protected, but will also check each of the worksheets for protection.

    If you pass a Range Object, it checks for two conditions:

  • If the cell is locked and the worksheet is protected (both are required for a cell to be uneditable), then it's protected
  • If the workbook is protected (by recursively calling itself to check if the Workbook object is protected)
VBA:
  1. Function IsProtected(objXL As Object) As Boolean
  2. Dim wksht As Excel.Worksheet
  3. Dim cell As Excel.Range
  4. Select Case TypeName(objXL)
  5.   Case "Worksheet"
  6.     If objXL.ProtectContents Then
  7.       ' still doesn't mean you can't edit the worksheet!
  8.       IsProtected = True
  9.       Exit Function
  10.     End If
  11.   Case "Workbook"
  12.     If objXL.ProtectStructure Then
  13.       IsProtected = True
  14.       Exit Function
  15.     End If
  16.     For Each wksht In objXL.Worksheets
  17.       If wksht.ProtectContents Then
  18.         ' still doesn't mean you can't edit the worksheet!
  19.         IsProtected = True
  20.         Exit Function
  21.       End If
  22.     Next wksht
  23.   Case "Range"
  24.     If objXL.Cells.Count = 1 Then
  25.       If (objXL.Locked And objXL.Parent.ProtectContents) Or (IsProtected(objXL.Parent.Parent)) Then
  26.         IsProtected = True
  27.         Exit Function
  28.       End If
  29.     Else
  30.       For Each cell In objXL
  31.         If (cell.Locked And cell.Parent.ProtectContents) Or (IsProtected(cell.Parent.Parent)) Then
  32.           IsProtected = True
  33.           Exit Function
  34.         End If
  35.       Next cell
  36.     End If
  37.  
  38. End Select
  39. End Function

    Usage:

VBA:
  1. Dim wkb As Excel.Workbook
  2. Set wkb = ActiveWorkbook
  3. If IsProtected(wkb) Then
  4.   Msgbox "It's protected!"
  5. End If
  6. ' or
  7. Dim wksht As Excel.Worksheet
  8. Set wksht = ActiveSheet
  9. If IsProtected(wksht) Then
  10.   Msgbox "It's protected!"
  11. End If
  12. ' or
  13. Dim rng as Excel.Range
  14. Set rng = Range("A3")
  15. If IsProtected(rng) Then
  16.   Msgbox "It's protected!"
  17. End If

    The function should return FALSE for any other type of Object you pass to it, so be sure to only pass Workbook or Worksheet or Range Objects to it.

    I got a bit lost writing this function, because of the numerous conditions for worksheet protection, and just settled with what I wrote above.

    For example, just because a Worksheet Object is protected, doesn't mean you can't edit cells on the worksheet; if the cells are unlocked, you can still update cells on a protected worksheet. One way to check for that is by checking the Locked property of the Cells Object: If Cells.Locked = True, then all the cells on the worksheet are locked (IsProtected = True), if Cells.Locked = False, none of them are (IsProtected = False ?), and if there is a mix of locked and unlocked cells, Cells.Locked = Null (which is not a boolean value!)

    And if you pass a Range object, does it matter if the workbook structure is protected? It depends on your specific needs.

    And should Cells.Locked = False return IsProtected = False? It depends on the purpose of the function. If you just want a basic check for protection, it may be enough just to check if the worksheet is protected. But if you have specific needs, you might want to know not only if the worksheet is protected, but also if the cells are locked. Here's an updated version of the above function that does that.

VBA:
  1. Function IsProtected(objXL As Object) As Boolean
  2. Dim wksht As Excel.Worksheet
  3. Dim cell As Excel.Range
  4.  
  5. Select Case TypeName(objXL)
  6.   Case "Worksheet"
  7.     If objXL.ProtectContents Then
  8.       ' still doesn't mean you can't edit the worksheet!
  9.       Select Case Cells.Locked
  10.         Case True ' all cells are locked AND worksheet is protected
  11.           IsProtected = True
  12.           Exit Function
  13.       End Select
  14.     End If
  15.   Case "Workbook"
  16.     If objXL.ProtectStructure Then
  17.       IsProtected = True
  18.       Exit Function
  19.     End If
  20.     For Each wksht In objXL.Worksheets
  21.       If wksht.ProtectContents Then
  22.         ' still doesn't mean you can't edit the worksheet!
  23.         Select Case Cells.Locked
  24.           Case True ' all cells are locked AND worksheet is protected
  25.             IsProtected = True
  26.             Exit Function
  27.         End Select
  28.       End If
  29.     Next wksht
  30.   Case "Range"
  31.     If objXL.Cells.Count = 1 Then
  32.       If (objXL.Locked And objXL.Parent.ProtectContents) Or (IsProtected(objXL.Parent.Parent)) Then
  33.         IsProtected = True
  34.         Exit Function
  35.       End If
  36.     Else
  37.       For Each cell In objXL
  38.         If (cell.Locked And cell.Parent.ProtectContents) Or (IsProtected(cell.Parent.Parent)) Then
  39.           IsProtected = True
  40.           Exit Function
  41.         End If
  42.       Next cell
  43.     End If
  44. End Select
  45. End Function

Enjoy,
JP

Share and Enjoy:
  • StumbleUpon
  • Technorati
  • Digg
  • Google
  • del.icio.us
  • MisterWong

Check your generated emails for valid recipients
October 30, 2008 – 11:58 am by JP

    I had a need for a function that checks if a recipient is valid. This could be useful in Excel to check if a name that someone input into a userform could resolve to a valid recipient. Hence the function name "ValidRecipient". :)

    The code creates a throwaway email and tries to resolve the name. Returns TRUE if the string passed to it resolves to a valid Outlook recipient in your addressbook. It uses a second function to grab a reference to Outlook, so the code is late-bound and can be used in any project. It can be used on the worksheet as a UDF, or (ideally) in your VBA code.

    Beware the Outlook (OMG) object model guard, which will be triggered when you access the Recipients collection.

VBA:
  1. Function ValidRecipient(strRecip As String) As Boolean
  2. Dim olApp As Object
  3. Dim olMsg As Object
  4. Dim olRecips As Object
  5. Dim olCurrentRecip As Object
  6.  
  7. On Error Resume Next
  8. Set olApp = GetOutlookApp
  9. On Error GoTo 0
  10.  
  11. If Not olApp Is Nothing Then
  12.   Set olMsg = olApp.CreateItem(0)
  13.   Set olRecips = olMsg.Recipients
  14.   Set olCurrentRecip = olRecips.Add(strRecip)
  15.  
  16.   If olCurrentRecip.Resolve Then
  17.     ValidRecipient = True
  18.     GoTo ExitProc
  19.   End If
  20.  
  21. ' or:
  22. ' olMsg.To = strRecip
  23. ' If olMsg.Recipients.ResolveAll Then
  24. ' ValidRecipient = True
  25. ' GoTo ExitProc
  26. ' End If
  27.  
  28. End If
  29.  
  30. ExitProc:
  31. olMsg.Close (1)
  32. Set olCurrentRecip = Nothing
  33. Set olRecips = Nothing
  34. Set olMsg = Nothing
  35. Set olApp = Nothing
  36. End Function

VBA:
  1. Function GetOutlookApp() As Object
  2. ' returns a reference to Outlook to the calling sub
  3. On Error Resume Next
  4.   Set GetOutlookApp = GetObject(, "Outlook.Application")
  5.  
  6. If GetOutlookApp Is Nothing Then
  7.   Set GetOutlookApp = CreateObject("Outlook.Application")
  8.   Exit Function
  9. End If
  10. On Error Goto 0
  11. End Function

Usage:

VBA:
  1. If ValidRecipient("Jimmy Pena") Then
  2.  MsgBox "OK!"
  3. End If

or:

=ValidRecipient("Jimmy Pena")

Share and Enjoy:
  • StumbleUpon
  • Technorati
  • Digg
  • Google
  • del.icio.us
  • MisterWong

Monitoring a range for changes
October 28, 2008 – 2:35 pm by JP

    A co-worker of mine has a workbook with an inventory list in column D. He wants a way to highlight low inventory quantities, when the inventory for any particular item falls below a certain amount. At first I thought about conditional formatting, for example:

condform-300x93

    But he'd rather have a popup messagebox. I guess the more intrusive, the more likely it won't be ignored. So I came up with this code. If any cell in column D is changed, and the amount is below 50, the messagebox is shown.

VBA:
  1. Private Sub Worksheet_Change(ByVal Target As Range)
  2. Dim rng As Excel.Range
  3.  
  4. On Error Resume Next
  5. Set rng = Intersect(Range("D:D"), Range(Target.Address))
  6. On Error GoTo 0
  7.  
  8. If Not rng Is Nothing Then
  9.   If (Target.Value <50) And (Target.Value <> "") Then
  10.     MsgBox "Below 50, please reorder now.", vbInformation
  11.   End If
  12. End If
  13.  
  14. End Sub

Enjoy,
JP

Share and Enjoy:
  • StumbleUpon
  • Technorati
  • Digg
  • Google
  • del.icio.us
  • MisterWong

HTML Link Building Using Excel
October 26, 2008 – 7:22 pm by JP

    While building a link list for my blogroll page (see Blogs I Read), I needed a way to create the list of links. The prospect of building all of the links by hand is pretty exhausting (yes, even the thought), so why not leverage Excel's capabilities?

    First I took a list of the links and put them in a worksheet in column A. Then I put the site name in column B. Here is the formula to create the link:


="<a rel=""nofollow"" href="""&A2&""" title="""&B2&""""&">"&B2&"</a>"

Yes, I nofollow external links (except in comments).

This technique could be very useful, if you have a list of websites and want to turn them into HTML links, just paste in the above formula and adjust the cell references. I created a sample with the sites from that page and the formula filled down to create the links.

Link Building Worksheet sample

FYI - I finally upgraded Wordpress to 2.6.3, there's a fantastic plugin I found that does it automatically. If you aren't running the latest version, this plugin makes it very easy.

--JP

Share and Enjoy:
  • StumbleUpon
  • Technorati
  • Digg
  • Google
  • del.icio.us
  • MisterWong