Calculating Age From DOB

I couldn’t believe there was no native way in .NET to do something alarmingly simple like calculate someone’s age from their date of birth. Of all the amazing functions and properties on DateTime objects, this seems surprisingly non-existent.

For such an easy task, it’s also surprisingly complex; so much so that Jeff Atwood, creator of StackOverflow, even asked that question himself here.

For all my (and the internet’s) failed attempts, read on. If you’re just looking for something to copy and paste in that will work, grab the following code

Public Function GetCurrentAge(ByVal dob As Date) As Integer
    Dim age As Integer
    age = Today.Year - dob.Year
    If (dob > Today.AddYears(-age)) Then age -= 1
    Return age
End Function

All of these methods, except the bottom one fail for various reasons:

Dim dob As Date = #5/14/1994#
Dim today As Date = #5/13/2013#
Dim age As Integer

age = DateDiff(DateInterval.Year, dob, today)
Console.WriteLine("DateDiff Year Age: {0}", age)
'19

age = today.Subtract(dob).TotalDays / 365.25
Console.WriteLine("Subtraction Age: {0}", age)
'19

age = Math.Floor(DateDiff(DateInterval.Month, dob, today) / 12)
Console.WriteLine("DateDiff Month Age: {0}", age)
'19

age = today.Year - dob.Year
If (dob > today.AddYears(-age)) Then age -= 1
Console.WriteLine("Year Part and Compare Age: {0}", age)
'18

Console.ReadKey()

Hope that helps!

Extending XmlReader Class: IsEndElement()

Introduction

You may find, as I did, the lack of symmetry in the XmlReader Class quite odd. If we look at the NodeType property, which exposes the XmlNodeType enumeration, we find an EndElement, but no StartElement (The Element member actually only identifies opening xml tag elements, but it’s not this shortage on which I’d like to elaborate). Only when we look at the methods available to the class do we see an IsStartElement() method for evaluation, but without a corollary method named something like: IsEndElement().

There are dozens of ways of getting around this deficiency, but I’m all for visually appealing code that leaves other coders with a easy and quick understanding of what you are attempting to do. Checking if the reader is on an opening tag with IsStartElement and then finding closing tags by evaluation the reader’s NodeType property might work correctly, but just looks wrong to me.

Extension Methods

What I’d like to do is create a method that looks and feels like the IsStartElement() function, but instead evaluates if the reader is currently on an EndElement node type. This is where extension methods come into play. Extension methods allow you to extend the functionality of a built in class with custom methods that act as if they were native to the class. I think they are best suited for when you are of the sincere belief that method deficit is a slight oversight of the framework designers, and if given the opportunity, they would happily accept your additional coding to improve the underlying code. Since all instances of this type or any derived type will have automatic access to any extension methods written on top of a particular class, you want to be careful with their implementation .

The first step is to open your project, right click on it, click add, then select module:

Add Module

Then give your module a name. I like to create a single Module for all extensions with a name like ExtensionMethods.

IsEndElement Code

First, you’ll need an imports(vb) or using(c#) statement with the extensions Namespace:

Imports System.Runtime.CompilerServices

Then, add the following code to your module:

<Extension()>  
Public Function IsEndElement(ByVal xmlReader As XmlReader, 
                             Optional ByVal name As String = "") As Boolean  
    If xmlReader.NodeType = XmlNodeType.EndElement Then  
        If name = "" Then  
            'node is end element, name value not set  
            Return True  
        Else  
            If xmlReader.Name = name Then  
                'node is end element, AND named the same as parameter  
                Return True  
            Else  
                'node is end element, but name is diff than parameter  
                Return False  
            End If  
        End If  
    Else  
        'node is not an end element  
        Return False  
    End If  
End Function  

And voilĂ ! Now you can call your code on native objects with full IntelliSense:

IntelliSense

Automatically Increment Minimum Required Version in a ClickOnce Application

Introduction

ClickOnce deployments make publishing .NET applications simple and easy. One nice feature is forcing the user to update to the latest available version. This can be very important on sensitive applications that require any potential fixes or patches to be immediately sent to the users.

One way to do this is to set the minimum required version on the publish page to the current version. ClickOnce then knows to automatically download the latest version whenever a user goes to run the application. The problem with this can be that you have to remember to increment the value every time you do a publish. This post will make life easier by automating the increase of the minimum required version every time you preform a publish.

We’ll do so by adding an MSBuild event to the project. This post assumes you don’t have any experience with MS build and will introduce you to the relevant concepts; if you’re already familiar with that, feel free to jump ahead to Step 6 and copy and paste the code into your .proj file.

Introduction to Project Editor

1) First, in solution explorer, right click on your project and select unload project

2) Once the project has become unavailable, right click again and select edit [project_name].csproj

  • Note: This unlocks full set of IntelliSense of all the MSBuild features

Introduction to MS Build

3) MS Build notation uses properties with key/value pairs to extract information. We’ll use the notation $(key) in the implementation section to reference the .proj elements in Step 4.

  • ex. Using the property name as an alias, you can use $(OutputPath) to obtain the value for the element <OutputPath>.\bin</OutputPath>. So in this case $(OutputPath) = .\bin

4) We’ll use the following properties generated for a ClickOnce deployment

<MinimumRequiredVersion>1.0.0.6</MinimumRequiredVersion>  
<ApplicationRevision>7</ApplicationRevision>  
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>  
  • The ApplicationVersion element specifies the build number. The last value of %2a is set by Visual Studio when the option to “Automatically increment revision with each publish” is checked on. In that case, it substitutes the %2a placeholder with whatever value is in the ApplicationRevision element
  • The MinimumRequiredVersion element is what we’d like to set with each publish so that it automatically increments as well.

5) MSBuild Tasks can be specified in the .proj file and invoked during a build event.

  • FormatVersion is a built-in task for .NET 4.0 and later that formats the ApplicationVersion and ApplicationRevision into a single version number

Implementation

6) Copy and Paste the following code into the opened project file as a child element to the root <Project> element:

<Target Name="AutoSetMinimumRequiredVersion"
        BeforeTargets="GenerateDeploymentManifest">
  <FormatVersion Version="$(ApplicationVersion)" 
                 Revision="$(ApplicationRevision)">
    <Output PropertyName="MinimumRequiredVersion" 
            TaskParameter="OutputVersion"  />
  </FormatVersion>
  <FormatVersion Version="$(ApplicationVersion)" 
                 Revision="$(ApplicationRevision)">
    <Output PropertyName="_DeploymentBuiltMinimumRequiredVersion"
            TaskParameter="OutputVersion"  />
  </FormatVersion>
</Target>
  • This code will take ApplicationVersion and ApplicationRevision as parameters in the Format Version task and will save the output by overwriting the MinimumRequiredVersion with the full publish version

7) Save and reload your project. Every ClickOnce deployment will now automatically update without a prompt with the ability to skip.

Making Sure Outlook Calendar Reminders Actually Pop-up in Windows 7

Introduction

In Windows 7, MS Outlook seems to be horribly bad at popping up calendar reminders when outlook doesn’t have focus. This can cause a series of problems from minor annoyances to missed meetings. As discussed in this StackExchange question, there are some relatively easy work arounds to this issue that I’ll elaborate on here. If you’ve never used Outlook’s VBA editor to extend it’s capabilities before, this article will take you through how to set up everything you’ll need to do, start to finish. If you’re already familiar with using VBA in Outlook, then you can just skip to Step 5 and paste in the code and you’ll be all set.

Step By Step

  • 1) Hit the Windows Key and type “certificate”
  • 2) Select Digital Certificate for VBA Projects and type in a name for your certificate and hit OK
  • 3) Open Outlook 2010 and Hit Alt + F11 to start the VBA editor
  • 4) In the Project Pane on the left, Expand Project1 > Microsoft Outlook Objects > and double click on ThisOutlookSession
  • 5) Paste in the following code exactly:
'ensures all reminder notices receive focus
Private Sub Application_Reminder(ByVal Item As Object)
    If TypeOf Item Is AppointmentItem Then
        Application.ActiveWindow.Activate
    End If
End Sub

Edit: Added New Better Code Below:

'Declare Functions From User32 Library  
Private Declare PtrSafe Function FindWindowA Lib "user32" _  
        (ByVal lpClassName As String, _  
         ByVal lpWindowName As String) As Long  
Private Declare PtrSafe Function SetWindowPos Lib "user32" _  
        (ByVal hwnd As Long, _  
         ByVal hWndInsertAfter As Long, _  
         ByVal X As Long, _  
         ByVal Y As Long, _  
         ByVal cx As Long, _  
         ByVal cy As Long, _  
         ByVal wFlags As Long) As Long  

'Declare Constants  
Private Const SWP_NOSIZE = &H1  
Private Const SWP_NOMOVE = &H2  
Private Const FLAGS As Long = SWP_NOMOVE Or SWP_NOSIZE  
Private Const HWND_TOPMOST = -1  

'Only show the message the first time  
Private messageAlreadyShown As Boolean  

'Finds Reminder Window and Brings to TopMost position  
Private Sub Application_Reminder(ByVal Item As Object)  
    On Error Resume Next  

    'show message box for first reminder  
    If Not messageAlreadyShown Then  
        MsgBox "First Reminder", vbSystemModal, ""  
        messageAlreadyShown = True  
    End If  

    'find reminder window  
    ReminderWindow = FindWindowA(vbNullString, "1 Reminder")  
    'bring reminder window to front  
    SetWindowPos ReminderWindow, HWND_TOPMOST, 0, 0, 0, 0, FLAGS  
End Sub 
  • 6) Sign the Macro by going to Tools > Digital Signature and clicking Choose
  • 7) Select the certificate you created earlier and hit OK
  • 8) Select OK again, hit Ctrl + S to save and exit the VBA window
  • 9) To Enable Macros, Go to File > Options and select Trust Center from the left window
  • 10) Run the Trust center by clicking the Trust Center Settings button on the right.
  • 11) From the Trust Center, select Macro Settings, and select “Notifications for digitally signed macros, all other macros disabled” and hit OK
  • 12) Exit Outlook - It will ask you if you want to save the project, click Yes
  • 13) Start Outlook - It will give you a security notice. Select “Trust all documents from this publisher” (You can first confirm that you are the publisher by selecting “Show Signature Details”)
  • 14) That’s it! You’re all set. You never have to touch any of that code again or miss another meeting (unintentionally)

UPDATE!

I’ve update the code to use ActiveWindow instead of ActiveExplorer, which returns nothing “if no explorer is active.” Thanks to CW for the impetus to update my code.