All posts in Extensions

It’s been a while since I have had anything to say and that’s mostly because of how busy I’ve been recently. In that time though, I have posted a number of my frameworks on NuGet located here. And to complement those frameworks, I have started to write the SandCastle documentation so that users can understand how my methods work. So, with all that in mind, I now have plently of material to blog about.

Today, we are going to talk briefly about scheduling. Or, more importantly, how we can write code that will provide a set of dates to complement a variety of date ranges or number of desired occurrences within a given time frame. To best understand this we should look at pulling a set of dates by days, weeks, months, and years. So, you will see a lot of similar patterns.

Before I get into it, let me talk about the difference between evaluating dates By Schedule vs By Occurrence.

 
By Schedule is concerned with: a start date, and an end date. This is because we are looking for the number of occurrences that happen between 2 static dates. Ex. Run this script every Tuesday until 2/20/2020.

By Occurrence is a little different. Instead it cares about start date, number of occurrences, and a maximum number of years to allow as an upper range. This is because here, we are looking for the dates that result from a specified number of occurrences. Ex. run this script up to 60 times within the next 5 years.

 
Assessing Dates By Days

To schedule something in terms of days, the most important variable to consider is the number of days between each instance. Essentially, we just need to enumerate through the dates by the number of days until we hit the end date or the number of occurrences depending if we query by schedule or by occurrence.

The source code would look something like this:
 


/// <summary>
/// Gets a list of valid dates related to days within a scheduled range.
/// </summary>
/// <param name="startDate">The start date.</param>
/// <param name="endDate">The end date.</param>
/// <param name="numberOfDays">The number of days to evaluate.</param>
/// <returns></returns>
public static DateTime[] GetValidDaysBySchedule(
    DateTime startDate, DateTime endDate, int numberOfDays)
{
	// List to return
	var list = new List<DateTime>();

	// Set first date to evaluate to the start date
	var currentDate = startDate;

	// Must have a valid start and end date, and the end date must be later in time than the 
	// start date.
	if (startDate == default(DateTime) || endDate == default(DateTime) ||
		!(endDate >= startDate)) return list.ToArray();

	// Process
	// 1) Do not exceed the end date, and
	while (endDate.Subtract(currentDate).TotalDays >= 0)
	{
		// Add date
		list.Add(currentDate);

		// Get next date
		currentDate = currentDate.AddDays(numberOfDays);
	}
	return list.ToArray();
}

/// <summary>
/// Gets a list of valid dates related to days within a number of occurrences.
/// </summary>
/// <param name="startDate">The start date.</param>
/// <param name="numberOfDays">The number of days.</param>
/// <param name="numberOfOccurrences">The number of occurrences. The default is 1.</param>
/// <param name="maximumYearsAllowed">The maximum years allowed. The default is 10.</param>
/// <returns></returns>
public static DateTime[] GetValidDaysByOccurrence(
    DateTime startDate, int numberOfDays, int numberOfOccurrences = 1, int maximumYearsAllowed = 1)
{
	// List to return
	var list = new List<DateTime>();

	// Set first date to evaluate to the start date
	var currentDate = startDate;

	// Must have a valid start and end date, and the occurrence count must not be exceeded.
	if (startDate == default(DateTime) || list.Count >= numberOfOccurrences)
		return list.ToArray();

	// Process
	// 1) Do not exceed occurrence count, and 
	// 2) do not exceed the maximum allowed years in a date range.
	var maxDate = startDate.AddYears(maximumYearsAllowed);
	while (list.Count < numberOfOccurrences && maxDate >= currentDate)
	{
		// Add date
		list.Add(currentDate);

		// Get next date
		currentDate = currentDate.AddDays(numberOfDays);
	}
	return list.ToArray();
}

 

An example of how to use this in a console application:


// Main
static void Main(string[] args)
{
	var startDate = new DateTime(2018, 2, 17);
	var endDate = startDate.AddMonths(4);

	// Occurs every 10 days up to the end date
	Console.WriteLine("By date range:");
	var dates = ScheduleHelper.GetValidDaysBySchedule(startDate, endDate, 10);
	foreach (var date in dates)
	{
		Console.WriteLine($"{date:MM/dd/yyyy, dddd}");
	}

	// Spacer
	Console.WriteLine("");

	// Occurs every 10 days, up to 10 times, not to exceed 1 year
	Console.WriteLine("By # of occurrences:");
	var dates2 = ScheduleHelper.GetValidDaysByOccurrence(startDate, 10, 10, 1);
	foreach (var date in dates2)
	{
		Console.WriteLine($"{date:MM/dd/yyyy, dddd}");
	}

	// Hold
	Console.Read();
}

/* Result:
 *
 * By date range:
 * 02/17/2018, Saturday
 * 02/27/2018, Tuesday
 * 03/09/2018, Friday
 * 03/19/2018, Monday
 * 03/29/2018, Thursday
 * 04/08/2018, Sunday
 * 04/18/2018, Wednesday
 * 04/28/2018, Saturday
 * 05/08/2018, Tuesday
 * 05/18/2018, Friday
 * 05/28/2018, Monday
 * 06/07/2018, Thursday
 * 06/17/2018, Sunday
 * 
 * By # of occurrences:
 * 02/17/2018, Saturday
 * 02/27/2018, Tuesday
 * 03/09/2018, Friday
 * 03/19/2018, Monday
 * 03/29/2018, Thursday
 * 04/08/2018, Sunday
 * 04/18/2018, Wednesday
 * 04/28/2018, Saturday
 * 05/08/2018, Tuesday
 * 05/18/2018, Friday
 */

 
Assessing Dates By Weeks

To schedule something in terms of weeks, we not only need to look at the number of weeks between each occurrence, we also need to specify which days of the week are relevant. Performing evaluations of weeks are in my opinion the most complex of the 4 types.

The source code would look something like this:
 


/// <summary>
/// Days of the week
/// </summary>
public enum DaysOfTheWeek
{
	/// <summary>
	/// Sunday
	/// </summary>
	Sunday = 0,

	/// <summary>
	/// Monday
	/// </summary>
	Monday = 1,

	/// <summary>
	/// Tuesday
	/// </summary>
	Tuesday = 2,

	/// <summary>
	/// Wednesday
	/// </summary>
	Wednesday = 3,

	/// <summary>
	/// Thursday
	/// </summary>
	Thursday = 4,

	/// <summary>
	/// Friday
	/// </summary>
	Friday = 5,

	/// <summary>
	/// Saturday
	/// </summary>
	Saturday = 6
}

/// <summary>
/// Gets a list of valid dates related to weeks within a scheduled range.
/// </summary>
/// <param name="startDate">The start date.</param>
/// <param name="endDate">The end date.</param>
/// <param name="numberOfWeeks">The number of weeks to skip per block of selected days.</param>
/// <param name="selectedDays">The selected days of the week the event occurs.</param>
/// <returns></returns>
public static DateTime[] GetValidWeeksBySchedule(
	DateTime startDate, DateTime endDate, int numberOfWeeks, DaysOfTheWeek[] selectedDays)
{
	// List to return
	var list = new List<DateTime>();

	// Get the start of the week
	var dayOfWeek = (int)startDate.DayOfWeek;

	// Set first date to evaluate to the start date
	var currentDate = startDate;

	// Get selected days and convert to their integer values
	// If no days were specified, then exit.
	var validDays = selectedDays.Select(x => (int)x).ToArray();
	if (!validDays.Any()) return list.ToArray();

	// Must have a valid start and end date, and
	// the end date must be later in time than the start date.
	if (startDate == default(DateTime) || endDate == default(DateTime) ||
		!(endDate >= startDate))
	{
		return list.ToArray();
	}

	// Process
	// 1) Do not exceed the end date
	while (endDate.Subtract(currentDate).TotalDays >= 0)
	{
		// Get the day of the week (value)
		var currentDayOfWeek = (int)currentDate.DayOfWeek;

		// Evaluate against selected says of the week
		if (validDays.Contains(currentDayOfWeek)) list.Add(currentDate);

		// Evaluate the next day
		var nextDay = currentDate.AddDays(1);

		// If the day of the week is not the original, and number of weeks is no greater than 1
		// Set the date as the next day. Otherwise, jump by the number of weeks specified.
		if ((int)nextDay.DayOfWeek != dayOfWeek || numberOfWeeks <= 1)
		{
			currentDate = nextDay;
		}
		else
		{
			var interval = 7 * (numberOfWeeks - 1);
			currentDate = nextDay.AddDays(interval);
		}
	}

	// Return
	return list.ToArray();
}

/// <summary>
/// Gets a list of valid dates related to weeks within a number of occurrences.
/// </summary>
/// <param name="startDate">The start date.</param>
/// <param name="numberOfWeeks">The number of weeks to skip per block of selected days.</param>
/// <param name="selectedDays">The selected days of the week the event occurs.</param>
/// <param name="numberOfOccurrences">The number of occurrences. The default is 1.</param>
/// <param name="maximumYearsAllowed">The maximum years allowed. The default is 10.</param>
/// <returns></returns>
public static DateTime[] GetValidWeeksByOccurrence(
	DateTime startDate, int numberOfWeeks,
	DaysOfTheWeek[] selectedDays, int numberOfOccurrences = 1, int maximumYearsAllowed = 10)
{
	// List to return
	var list = new List<DateTime>();

	// Get the start of the week
	var dayOfWeek = (int)startDate.DayOfWeek;

	// Set first date to evaluate to the start date
	var currentDate = startDate;

	// Get selected days and convert to their integer values
	// If no days were specified, then exit.
	var validDays = selectedDays.Select(x => (int)x).ToArray();
	if (!validDays.Any()) return list.ToArray();

	// Must have a valid start and end date, and
	// the occurrence count must not be exceeded.
	if (startDate == default(DateTime) || list.Count >= numberOfOccurrences)
	{
		return list.ToArray();
	}

	// Process
	// 1) Do not exceed occurrence, and 
	// 2) do not exceed the maximum allowed years in a date 
	// range.
	var maxDate = startDate.AddYears(maximumYearsAllowed);
	while (list.Count < numberOfOccurrences && maxDate >= currentDate)
	{
		// Get the day of the week (value)
		var currentDayOfWeek = (int)currentDate.DayOfWeek;

		// Evaluate against selected says of the week
		if (validDays.Contains(currentDayOfWeek)) list.Add(currentDate);

		// Evaluate the next day
		var nextDay = currentDate.AddDays(1);

		// If the day of the week is not the original, and number of weeks is no greater than 1
		// Set the date as the next day. Otherwise, jump by the number of weeks specified.
		if ((int)nextDay.DayOfWeek != dayOfWeek || numberOfWeeks <= 1)
		{
			currentDate = nextDay;
		}
		else
		{
			var interval = 7 * (numberOfWeeks - 1);
			currentDate = nextDay.AddDays(interval);
		}
	}

	// Return
	return list.ToArray();
}

 

An example of how to use this in a console application:


// Main
static void Main(string[] args)
{
	var startDate = new DateTime(2018, 2, 17);
	var endDate = startDate.AddYears(1);

	// Occurs every 2 weeks on Sunday, Monday, and Thursday not to exceed the end date.
	var dates = ScheduleHelper.GetValidWeeksBySchedule(startDate, endDate, 2,
		new[] { DaysOfTheWeek.Sunday, DaysOfTheWeek.Monday, DaysOfTheWeek.Thursday });
	Console.WriteLine($"By date range ({dates.Length}):");
	foreach (var date in dates)
	{
		Console.WriteLine($"{date:MM/dd/yyy, dddd}");
	}

	// Spacer
	Console.WriteLine("");

	// Occurs up to 30 times on Sunday, Monday, and Thursday no longer than 1 year.
	var dates2 = ScheduleHelper.GetValidWeeksByOccurrence(startDate, 1,
		new[] { Enums.DaysOfTheWeek.Sunday, Enums.DaysOfTheWeek.Monday, Enums.DaysOfTheWeek.Thursday },
		30, 1);
	Console.WriteLine($"By # of occurrences ({dates2.Length}):");
	foreach (var date in dates2)
	{
		Console.WriteLine($"{date:MM/dd/yyyy, dddd}");
	}

	// Hold
	Console.Read();
}

/* Result:
 *
 * By date range (79):
 * 02/18/2018, Sunday
 * 02/19/2018, Monday
 * 02/22/2018, Thursday
 * 03/04/2018, Sunday
 * 03/05/2018, Monday
 * 03/08/2018, Thursday
 * 03/18/2018, Sunday
 * 03/19/2018, Monday
 * 03/22/2018, Thursday
 * 04/01/2018, Sunday
 * 04/02/2018, Monday
 * 04/05/2018, Thursday
 * 04/15/2018, Sunday
 * 04/16/2018, Monday
 * 04/19/2018, Thursday
 * 04/29/2018, Sunday
 * 04/30/2018, Monday
 * 05/03/2018, Thursday
 * 05/13/2018, Sunday
 * 05/14/2018, Monday
 * 05/17/2018, Thursday
 * 05/27/2018, Sunday
 * 05/28/2018, Monday
 * 05/31/2018, Thursday
 * 06/10/2018, Sunday
 * 06/11/2018, Monday
 * 06/14/2018, Thursday
 * 06/24/2018, Sunday
 * 06/25/2018, Monday
 * 06/28/2018, Thursday
 * 07/08/2018, Sunday
 * 07/09/2018, Monday
 * 07/12/2018, Thursday
 * 07/22/2018, Sunday
 * 07/23/2018, Monday
 * 07/26/2018, Thursday
 * 08/05/2018, Sunday
 * 08/06/2018, Monday
 * 08/09/2018, Thursday
 * 08/19/2018, Sunday
 * 08/20/2018, Monday
 * 08/23/2018, Thursday
 * 09/02/2018, Sunday
 * 09/03/2018, Monday
 * 09/06/2018, Thursday
 * 09/16/2018, Sunday
 * 09/17/2018, Monday
 * 09/20/2018, Thursday
 * 09/30/2018, Sunday
 * 10/01/2018, Monday
 * 10/04/2018, Thursday
 * 10/14/2018, Sunday
 * 10/15/2018, Monday
 * 10/18/2018, Thursday
 * 10/28/2018, Sunday
 * 10/29/2018, Monday
 * 11/01/2018, Thursday
 * 11/11/2018, Sunday
 * 11/12/2018, Monday
 * 11/15/2018, Thursday
 * 11/25/2018, Sunday
 * 11/26/2018, Monday
 * 11/29/2018, Thursday
 * 12/09/2018, Sunday
 * 12/10/2018, Monday
 * 12/13/2018, Thursday
 * 12/23/2018, Sunday
 * 12/24/2018, Monday
 * 12/27/2018, Thursday
 * 01/06/2019, Sunday
 * 01/07/2019, Monday
 * 01/10/2019, Thursday
 * 01/20/2019, Sunday
 * 01/21/2019, Monday
 * 01/24/2019, Thursday
 * 02/03/2019, Sunday
 * 02/04/2019, Monday
 * 02/07/2019, Thursday
 * 02/17/2019, Sunday
 * 
 * By # of occurrences (30):
 * 02/18/2018, Sunday
 * 02/19/2018, Monday
 * 02/22/2018, Thursday
 * 02/25/2018, Sunday
 * 02/26/2018, Monday
 * 03/01/2018, Thursday
 * 03/04/2018, Sunday
 * 03/05/2018, Monday
 * 03/08/2018, Thursday
 * 03/11/2018, Sunday
 * 03/12/2018, Monday
 * 03/15/2018, Thursday
 * 03/18/2018, Sunday
 * 03/19/2018, Monday
 * 03/22/2018, Thursday
 * 03/25/2018, Sunday
 * 03/26/2018, Monday
 * 03/29/2018, Thursday
 * 04/01/2018, Sunday
 * 04/02/2018, Monday
 * 04/05/2018, Thursday
 * 04/08/2018, Sunday
 * 04/09/2018, Monday
 * 04/12/2018, Thursday
 * 04/15/2018, Sunday
 * 04/16/2018, Monday
 * 04/19/2018, Thursday
 * 04/22/2018, Sunday
 * 04/23/2018, Monday
 * 04/26/2018, Thursday
 */

 
Assessing Dates By Months

To schedule something in terms of months, we need to assess the number of months between each occurrence, and the day of the month it will occur on.

The source code would look like this:
 


/// <summary>
/// Gets a list of valid dates related to months within a scheduled range.
/// </summary>
/// <param name="startDate">The start date.</param>
/// <param name="endDate">The end date.</param>
/// <param name="numberOfMonths">The number of months to skip per date.</param>
/// <param name="dayOfMonth">The day of the month the event occurs.</param>
/// <returns></returns>
public static DateTime[] GetValidMonthsBySchedule(
	DateTime startDate, DateTime endDate, int numberOfMonths,
	int dayOfMonth)
{
	// List to return
	var list = new List<DateTime>();

	// Set current day as the start date with the time stripped off
	var currentDate = new DateTime(startDate.Year, startDate.Month, dayOfMonth);

	// Must have a valid start and end date, and
	// the end date must be later in time than the start date.
	if (startDate == default(DateTime) || endDate == default(DateTime) ||
		!(endDate >= startDate))
	{
		return list.ToArray();
	}

	// Process
	// 1) Do not exceed the end date
	while (endDate.Subtract(currentDate).TotalDays >= 0)
	{
		// Add to list
		list.Add(currentDate);

		// Get new date
		currentDate = currentDate.AddMonths(numberOfMonths);
	}

	// Return
	return list.ToArray();
}

/// <summary>
/// Gets a list of valid dates related to months within a number of occurrences.
/// </summary>
/// <param name="startDate">The start date.</param>
/// <param name="numberOfMonths">The number of months to skip per date.</param>
/// <param name="dayOfMonth">The day of the month the event occurs.</param>
/// <param name="numberOfOccurrences">The number of occurrences. The default is 1.</param>
/// <param name="maximumYearsAllowed">The maximum years allowed. The default is 10.</param>
/// <returns></returns>
public static DateTime[] GetValidMonthsByOccurrence(
	DateTime startDate, int numberOfMonths,
	int dayOfMonth, int numberOfOccurrences = 1, int maximumYearsAllowed = 10)
{
	// List to return
	var list = new List<DateTime>();

	// Set current day as the start date with the time stripped off
	var currentDate = new DateTime(startDate.Year, startDate.Month, dayOfMonth);

	// Must have a valid start and end date, and the occurrence count must not be exceeded.
	if (startDate == default(DateTime) || list.Count >= numberOfOccurrences)
	{
		return list.ToArray();
	}

	// Process
	// 1) Do not exceed occurrence, and 
	// 2) do not exceed the maximum allowed years in a date range.
	var maxDate = startDate.AddYears(maximumYearsAllowed);
	while (list.Count < numberOfOccurrences && maxDate >= currentDate)
	{
		// Add to list
		list.Add(currentDate);

		// Get new date
		currentDate = currentDate.AddMonths(numberOfMonths);
	}

	// Return
	return list.ToArray();
}

 

An example of how to use this in a console application:


// Main
static void Main(string[] args)
{
	var startDate = new DateTime(2018, 2, 17);
	var endDate = startDate.AddYears(2);

	// Occurs on the 5th day of every month until the end date
	var dates = ScheduleHelper.GetValidMonthsBySchedule(startDate, endDate, 1, 5);
	Console.WriteLine($"By date range ({dates.Length}):");
	foreach (var date in dates)
	{
		Console.WriteLine($"{date:MM/dd/yyy, dddd}");
	}

	// Spacer
	Console.WriteLine("");

	// Occurs on the 5th day of every month until 20 occurrences have been reached.
	var dates2 = ScheduleHelper.GetValidMonthsByOccurrence(startDate, 1, 5, 20, 2);
	Console.WriteLine($"By # of occurrences ({dates2.Length}):");
	foreach (var date in dates2)
	{
		Console.WriteLine($"{date:MM/dd/yyyy, dddd}");
	}

	// Hold
	Console.Read();
}

/* Result:
 *
 * By date range (25):
 * 02/05/2018, Monday
 * 03/05/2018, Monday
 * 04/05/2018, Thursday
 * 05/05/2018, Saturday
 * 06/05/2018, Tuesday
 * 07/05/2018, Thursday
 * 08/05/2018, Sunday
 * 09/05/2018, Wednesday
 * 10/05/2018, Friday
 * 11/05/2018, Monday
 * 12/05/2018, Wednesday
 * 01/05/2019, Saturday
 * 02/05/2019, Tuesday
 * 03/05/2019, Tuesday
 * 04/05/2019, Friday
 * 05/05/2019, Sunday
 * 06/05/2019, Wednesday
 * 07/05/2019, Friday
 * 08/05/2019, Monday
 * 09/05/2019, Thursday
 * 10/05/2019, Saturday
 * 11/05/2019, Tuesday
 * 12/05/2019, Thursday
 * 01/05/2020, Sunday
 * 02/05/2020, Wednesday
 * 
 * By # of occurrences (20):
 * 02/05/2018, Monday
 * 03/05/2018, Monday
 * 04/05/2018, Thursday
 * 05/05/2018, Saturday
 * 06/05/2018, Tuesday
 * 07/05/2018, Thursday
 * 08/05/2018, Sunday
 * 09/05/2018, Wednesday
 * 10/05/2018, Friday
 * 11/05/2018, Monday
 * 12/05/2018, Wednesday
 * 01/05/2019, Saturday
 * 02/05/2019, Tuesday
 * 03/05/2019, Tuesday
 * 04/05/2019, Friday
 * 05/05/2019, Sunday
 * 06/05/2019, Wednesday
 * 07/05/2019, Friday
 * 08/05/2019, Monday
 * 09/05/2019, Thursday
 */

 
Assessing Dates By Years

Finally, to schedule something in terms of years, we need to assess the number of years between each occurrence, the specific month in that year, and the specific day in that month.

The source code would look like this:
 


/// <summary>
/// Gets a list of valid dates related to years within a scheduled range.
/// </summary>
/// <param name="startDate">The start date.</param>
/// <param name="endDate">The end date.</param>
/// <param name="numberOfYears">The number of years to skip per date.</param>
/// <param name="month">The month the event occurs.</param>
/// <param name="day">The day of the month the event occurs.</param>
/// <returns></returns>
public static DateTime[] GetValidYearsBySchedule(
	DateTime startDate, DateTime endDate, int numberOfYears, int month, int day)
{
	// List to return
	var list = new List<DateTime>();

	// Set current day as the start date with the time stripped off
	var currentDate = new DateTime(startDate.Year, month, day);

	// Must have a valid start and end date, and
	// the end date must be later in time than the start date.
	if (startDate == default(DateTime) || endDate == default(DateTime) ||
		!(endDate >= startDate))
	{
		return list.ToArray();
	}

	// Process
	// 1) Do not exceed the end date, and
	while (endDate.Subtract(currentDate).TotalDays >= 0)
	{
		// Add to list
		list.Add(currentDate);

		// Get new date
		currentDate = currentDate.AddYears(numberOfYears);
	}

	// Return
	return list.ToArray();
}

/// <summary>
/// Gets a list of valid dates related to years within a scheduled range.
/// </summary>
/// <param name="startDate">The start date.</param>
/// <param name="numberOfYears">The number of years to skip per date.</param>
/// <param name="month">The month the event occurs.</param>
/// <param name="day">The day of the month the event occurs.</param>
/// <param name="numberOfOccurrences">The number of occurrences. The default is 1.</param>
/// <param name="maximumYearsAllowed">The maximum years allowed. The default is 10.</param>
/// <returns></returns>
public static DateTime[] GetValidYearsByOccurrence(
	DateTime startDate, int numberOfYears,
	int month, int day, int numberOfOccurrences = 1, int maximumYearsAllowed = 10)
{
	// List to return
	var list = new List<DateTime>();

	// Set current day as the start date with the time stripped off
	var currentDate = new DateTime(startDate.Year, month, day);

	// Must have a valid start and end date, and
	// the occurrence count must not be exceeded.
	if ((startDate == default(DateTime)) || list.Count >= numberOfOccurrences)
	{
		return list.ToArray();
	}

	// Process
	// 1) Do not exceed occurrence, and 
	// 2) do not exceed the maximum allowed years in a date range.
	var maxDate = startDate.AddYears(maximumYearsAllowed);
	while (list.Count < numberOfOccurrences && maxDate >= currentDate)
	{
		// Add to list
		list.Add(currentDate);

		// Get new date
		currentDate = currentDate.AddYears(numberOfYears);
	}

	// Return
	return list.ToArray();
}

 

An example of how to use this in a console application:


// Main
static void Main(string[] args)
{
	var startDate = new DateTime(2018, 2, 17);
	var endDate = startDate.AddYears(5);

	// Occurs on the 5th day of February of every year until the end date
	var dates = ScheduleHelper.GetValidYearsBySchedule(startDate, endDate, 1, 2, 5);
	Console.WriteLine($"By date range ({dates.Length}):");
	foreach (var date in dates)
	{
		Console.WriteLine($"{date:MM/dd/yyy, dddd}");
	}

	// Spacer
	Console.WriteLine("");

	// Occurs on the 5th day of February of every year until 50 occurrences have been reached.
	var dates2 = ScheduleHelper.GetValidYearsByOccurrence(startDate, 1, 2, 5, 50, 5);
	Console.WriteLine($"By # of occurrences ({dates2.Length}):");
	foreach (var date in dates2)
	{
		Console.WriteLine($"{date:MM/dd/yyyy, dddd}");
	}

	// Hold
	Console.Read();
}

/* Result:
 *
 * By date range (6):
 * 02/05/2018, Monday
 * 02/05/2019, Tuesday
 * 02/05/2020, Wednesday
 * 02/05/2021, Friday
 * 02/05/2022, Saturday
 * 02/05/2023, Sunday
 * 
 * By # of occurrences (6):
 * 02/05/2018, Monday
 * 02/05/2019, Tuesday
 * 02/05/2020, Wednesday
 * 02/05/2021, Friday
 * 02/05/2022, Saturday
 * 02/05/2023, Sunday
 */

 
And that’s all there really is to it. Once you see the code for yourself you can understand that it’s not nearly as complex as you might have originally thought.

I hope that helped. So, until next time – Happy Coding!

This is the first of a 2-part series on Accessing Monitor Information with C#.

The series will include the following:

  • Part 1: Getting Monitor Handles
  • Part 2: Getting a Monitor associated with a Window Handle

We will be discussing a number of components associated with Native Methods in these posts. If you are concerned about some references not being explicitly established in the demonstrated code, don’t worry: I have posted the source on CodePlex for your usage.

 
The Native Methods
I have stated this in earlier posts, but it bears repeating: “It has been accepted in the .Net community that you should use Native Methods only when the .Net Framework does not offer a managed solution. Manged is always better because it has been configured to be properly managed in memory, and to be recycled by the garbage collector. I tend to use Native Methods most often when I need to interact with an external process and there are no valid hooks in the Framework.

 
For this exercise, we will be using the following Native Methods and structures:

RECT
The RECT structure defines the coordinates of the upper-left and lower-right corners of a rectangle.


/// <summary>
/// Rectangle
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

 
MONITORINFO
The MONITORINFO structure contains information about a display monitor. It is composed of the size of the structure, the RECT representing the area the application exists in the total viewable area (all monitors that make up the desktop space), the RECT representing the working area taken up by the application, and flags associated with special attributes assigned.


/// <summary>
/// Monitor information.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct MONITORINFO
{
    public uint size;
    public RECT monitor;
    public RECT work;
    public uint flags;
}

 
EnumDisplayMonitors
This method enumerates through the display monitors with the assistance of a delegate to process each monitor.


/// <summary>
/// Monitor Enum Delegate
/// </summary>
/// <param name="hMonitor">A handle to the display monitor.</param>
/// <param name="hdcMonitor">A handle to a device context.</param>
/// <param name="lprcMonitor">A pointer to a RECT structure.</param>
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the enumeration function.</param>
/// <returns></returns>
public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor,
    ref RECT lprcMonitor, IntPtr dwData);

/// <summary>
/// Enumerates through the display monitors.
/// </summary>
/// <param name="hdc">A handle to a display device context that defines the visible region of interest.</param>
/// <param name="lprcClip">A pointer to a RECT structure that specifies a clipping rectangle.</param>
/// <param name="lpfnEnum">A pointer to a MonitorEnumProc application-defined callback function.</param>
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the MonitorEnumProc function.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip,
	MonitorEnumDelegate lpfnEnum, IntPtr dwData);

 
GetMonitorInfo
Retrieves the monitor information.


/// <summary>
/// Gets the monitor information.
/// </summary>
/// <param name="hmon">A handle to the display monitor of interest.</param>
/// <param name="mi">A pointer to a MONITORINFO instance created by this method.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool GetMonitorInfo(IntPtr hmon, ref MONITORINFO mi);

 
Next, we need to define MonitorInfoWithHandle, which will store the MONITORINFO and monitor handle for each monitor instance:


/// <summary>
/// Monitor information with handle interface.
/// </summary>
public interface IMonitorInfoWithHandle
{
    IntPtr MonitorHandle { get; }
    MonitorInfo MonitorInfo { get; }
}

/// <summary>
/// Monitor information with handle.
/// </summary>
public class MonitorInfoWithHandle : IMonitorInfoWithHandle
{
    /// <summary>
    /// Gets the monitor handle.
    /// </summary>
    /// <value>
    /// The monitor handle.
    /// </value>
    public IntPtr MonitorHandle { get; private set; }

    /// <summary>
    /// Gets the monitor information.
    /// </summary>
    /// <value>
    /// The monitor information.
    /// </value>
    public MONITORINFO MonitorInfo { get; private set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="MonitorInfoWithHandle"/> class.
    /// </summary>
    /// <param name="monitorHandle">The monitor handle.</param>
    /// <param name="monitorInfo">The monitor information.</param>
    public MonitorInfoWithHandle(IntPtr monitorHandle, MONITORINFO monitorInfo)
    {
        MonitorHandle = monitorHandle;
        MonitorInfo = monitorInfo;
    }
}

 
Now that we have our Native Methods and structures in place, let’s build our explicit MonitorEnumProc which will be used by our enumeration through the monitors:


/// <summary>
/// Monitor Enum Delegate
/// </summary>
/// <param name="hMonitor">A handle to the display monitor.</param>
/// <param name="hdcMonitor">A handle to a device context.</param>
/// <param name="lprcMonitor">A pointer to a RECT structure.</param>
/// <param name="dwData">Application-defined data that EnumDisplayMonitors passes directly to the enumeration function.</param>
/// <returns></returns>
public static bool MonitorEnum(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
{
    var mi = new MONITORINFO();
    mi.size = (uint)Marshal.SizeOf(mi);
    NativeMonitorHelper.GetMonitorInfo(hMonitor, ref mi);

    // Add to monitor info
    _monitorInfos.Add(new MonitorInfoWithHandle(hMonitor, mi));
    return true;
}

Here is what this is doing:

  1. A new MONITORINFO structure is instantiated for the purpose of containing the data discovered by GetMonitorInfo.
  2. The size of the MONITORINFO is determined as a pre-requisite for the instance to be properly populated. Note: We see this a lot with structures used with Native Methods.
  3. GetMonitorInfo is called with a monitor handle provided by EnumDisplayMonitors, as well as our newly created MONITORINFO instance.
  4. A new MonitorInfoWithHandle instance is created with the provided monitor handle and our newly populated MONITORINFO instance, which is then added to a persisted list.

 
Lastly, we need to put together our EnumDisplayMonitors method which will enumerate through all the display monitors using our MonitorEnum delegate:


/// <summary>
/// Gets the monitors.
/// </summary>
/// <returns></returns>
public static MonitorInfoWithHandle[] GetMonitors()
{
    // New List
    _monitorInfos = new List<MonitorInfoWithHandle>();

    // Enumerate monitors
    NativeMonitorHelper.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnum, IntPtr.Zero);

    // Return list
    return _monitorInfos.ToArray();
}

What this does is a lot simpler since the majority of the work is done by MonitorEnum:

  1. The monitor info list is created.
  2. EnumDisplayMonitors is called with our MonitorEnum delegate to retrieve the data for each monitor.
  3. The monitor info array is returned to the caller.

 
And that’s all there is to it. There are a lot of components but it’s actually pretty straight forward.

Next time in Part 2, we will look at how we can discover what monitor a specific process is displayed when it has a valid Main Window Handle.

Until then…

That’s what’s up. We are going to stop windows from stealing focus with C#. No frills, no crap, no more of this nonsense. It’s time to stop.

Q: Why does Windows do this and why isn’t there a setting to fix this?
A: Remember Fred Johnson who lived down the street in the 70’s? You know… that slightly overweight jerk that bullied the smart kids? Well, one day on the way home from school he kicked Billy Gates dog and said something off color about his “parentage”. So, now we need to suffer for Fred’s sins. No need to Wiki this, it’s a true story.

 
Joking Aside

So, back in the early 2000’s we had the Windows XP PowerToy called TweakUI which allowed us to control how initialized windows interacted with the desktop environment. This involved changing the following registry key:

HKEY_CURRENT_USER\Control Panel\Desktop\Foreground\LockTimeout

In the modern versions of Windows, this override no longer works. For a long while, this left us SOL and drifting in space looking for the answer… that is until now.

 
The Solution

In my opinion, the C# community was right in thinking the best approach is to get on the P/Invoke track.

For this solution the key is LockSetForegroundWindow on a timer. For this solution we will use the following methods:


// Lock
public static uint LSFW_LOCK = 1;
public static uint LSFW_UNLOCK = 2;

/// <summary>
/// Locks the set foreground window.
/// </summary>
/// <param name="uLockCode">The u lock code.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool LockSetForegroundWindow(uint uLockCode);

/// <summary>
/// Gets the foreground window.
/// </summary>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();

 
Now that we have identified the P/Invoke piece, let’s look at how we can implement this effectively with said timer in a WPF application. To do this we are going to create a controller to facilitate all our focus needs:


using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Threading;

using Win32Windows = Xcalibur.Win32.Win32ApiHelper.Windows;

namespace Xcalibur.DontInterruptMe
{
    /// <summary>
    /// Window focus controller.
    /// </summary>
    public class FocusController
    {
        #region Members
        
        private readonly DispatcherTimer _timer;
        private IntPtr _currentHandle;
        private bool _isRunning;
        private bool _isStarted;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="FocusController"/> class.
        /// </summary>
        public FocusController()
        {
            this._isRunning = false;
            this._isStarted = false;

            // Set timer
            this._timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
            this._timer.Tick += (s, e) => this.EvaluateAsync(); 

            // Start
            this.Start();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            if (this._isStarted) return;

            // Start timer
            this._timer.IsEnabled = true;
            this._timer.Start();
            this._isStarted = true;
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {
            if (!this._isStarted) return;

            // Stop timer
            this._timer.Stop();
            this._timer.IsEnabled = false;
            this._isStarted = false;

            // Unlock set foreground window
            LockSetForegroundWindow(LSFW_UNLOCK);
        }

        /// <summary>
        /// Evaluates this instance.
        /// </summary>
        private void Evaluate()
        {
            if (_isRunning) return;

            // Set as "running"
            _isRunning = true;

            // Get current
            var activeWindowHandle = GetForegroundWindow();
            if (_currentHandle == activeWindowHandle)
            {
                _isRunning = false;
                return;
            }

            // Store current handle
            _currentHandle = activeWindowHandle;

            // Handle cannot be 0
            if (activeWindowHandle == IntPtr.Zero)
            {
                _isRunning = false;
                return;
            }

            // Get related process
            var processes = Process.GetProcesses();
            var currentProcess = processes.FirstOrDefault(x => x.MainWindowHandle == _currentHandle);

            // currentProcess must exist, and the MainWindowTitle must be valid.
            if (currentProcess == null || string.IsNullOrEmpty(currentProcess.MainWindowTitle))
            {
                _isRunning = false;
                return;
            }

            // Lock set foreground window
            LockSetForegroundWindow(LSFW_LOCK);

            // Set as "not running"
            _isRunning = false;
        }

        /// <summary>
        /// Evaluates the asynchronous.
        /// </summary>
        /// <returns></returns>
        private async Task EvaluateAsync()
        {
            await Task.Run(() => this.Evaluate());
        }

        #endregion

        #region P/Invoke 

        // Lock
        public static uint LSFW_LOCK = 1;
        public static uint LSFW_UNLOCK = 2;

        /// <summary>
        /// Locks the set foreground window.
        /// </summary>
        /// <param name="uLockCode">The u lock code.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern bool LockSetForegroundWindow(uint uLockCode);

        /// <summary>
        /// Gets the foreground window.
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

        #endregion
    }
}

 
The last piece is the implementation of the FocusController from App.xaml:


using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using Hardcodet.Wpf.TaskbarNotification;

using Microsoft.Win32;
using Application = System.Windows.Application;

namespace Xcalibur.DontInterruptMe
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        #region Members

        private static Mutex _instanceMutex;
        private FocusController _focusController;

        #endregion

        #region Methods

        /// <summary>
        /// Checks if application is already running.
        /// </summary>
        /// <returns></returns>
        private static bool StartInstance()
        {
            // Set mutex
            _instanceMutex = new Mutex(true, Constants.ApplicationKey);

            // Check if already running
            bool isAlreadyInUse = false;
            try
            {
                isAlreadyInUse = !_instanceMutex.WaitOne(TimeSpan.Zero, true);
            }
            catch (AbandonedMutexException)
            {
                KillInstance();
                isAlreadyInUse = false;
            }
            catch (Exception)
            {
                _instanceMutex.Close();
                isAlreadyInUse = false;
            }
            return isAlreadyInUse;
        }

        /// <summary>
        /// Kills the instance.
        /// </summary>
        /// <param name="code">The code.</param>
        private static void KillInstance(int code = 0)
        {
            if (_instanceMutex == null) return;

            // Owning application should release mutex
            if (code == 0)
            {
                try
                {
                    _instanceMutex.ReleaseMutex();
                }
                catch (Exception) { }
            }
            _instanceMutex.Close();
        }

        #endregion

        #region Events

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Application.Startup" /> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.StartupEventArgs" /> that contains the 
        /// event data.</param>
        protected override void OnStartup(StartupEventArgs e)
        {
            // Check if running
            if (StartInstance())
            {
                // Already running, Exit
                Current.Shutdown(1);
            }

            // Invoke focus controller
            this._focusController = new FocusController();

            // Base
            base.OnStartup(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Application.Exit" /> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.Windows.ExitEventArgs" /> that contains the 
        /// event data.</param>
        protected override void OnExit(ExitEventArgs e)
        {
            if (this._focusController != null)
            {
                // Gracefully exit
                this._focusController.Stop();
            }

            // Kill instance
            KillInstance(e.ApplicationExitCode);

            // Base
            base.OnExit(e);
        }

        #endregion
    }
}

 
You will notice I added the RunOnce Mutex Solution from our last post Restricting WPF Applications to run only once with a Mutex to avoid any potential issues with multiple instances.

 
Future Changes

In the next version of this application I will be tossing Process.GetProcesses() from the Evaluate method for a much faster P/Invoke solution I am planning to use in the next version of Astronomy.

If you want the complete, free product, download Don’t Interrupt Me! now.

Otherwise my friends, Happy Coding!

In A different take on Deep Copy I talked a bit about the different approaches to how you can accomplish performing a Deep Copy of a model in C#. In this article I want to revisit the topic briefly and demonstrate two methods to handle this concern.

Q: Why not use Object.MemberwiseClone?
A: You can, but it only performs a shallow copy of your model. If you have a more complex model, you will need something that can dig deeper and map all your properties.

The first approach is our Deep Copy method done with serialization. If you have been around the Internet a bit, you will recognize it:


/// <summary>
/// Performs a Deep Copy of an object through serialization.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="oSource">The o source.</param>
/// <returns></returns>
public static T DeepCopy<T>(this T oSource)
{
    using (var ms = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, oSource);
        ms.Position = 0;
        return (T)formatter.Deserialize(ms);
    }
}

This is the cleanest way to copy a model.

1) A MemoryStream is created.
2) A BinaryFormatter serializes the source into the MemoryStream.
3) The MemoryStream position is reset for reading purposes.
4) A deserialized object is cast to type T and returned.

A simple example:


var myNewObject = myObject.DeepCopy();

 
This works great in many cases but not in a Framework like Silverlight. Given, not a lot of us use that anymore, but you could still run into a scenario where serialization is not possible. In that case, we are going to create a new instance of the model we intend to copy. Then, we will map properties between the 2 models.


/// <summary>
/// Copies all public properties from one class to another.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="destination">The destination.</param>
/// <exception cref="System.Exception">Source and destination cannot be null and must be 
/// of the same type!</exception>
public static void MapProperties<T>(this T source, T destination) where T : class
{
    // Source and destination must exist.
    if ((source == null || destination == null)) return;

    // Get properties
    var propertyInfos = source.GetType().GetProperties();
    if (!propertyInfos.Any()) return;

    // Process only public properties
    foreach (var propInfo in propertyInfos.Where(x => x.CanWrite))
    {
        // Get value from source and assign to destination.
        var value = propInfo.GetValue(source, null);
        if (value == null) continue;

        // Evaluate
        var valType = value.GetType();

        // Collections
        if (valType.InheritsFrom<ICollection>())
        {
            var sourceCollection = value as IList;
            if (sourceCollection == null) continue;

            // Create new instance of collection
            IList destinationCollection = null;
            destinationCollection = (valType.BaseType == typeof(Array))
                ? Array.CreateInstance(valType.GetElementType(), sourceCollection.Count)
                : (IList)Activator.CreateInstance(valType, null);
            if (destinationCollection == null) continue;

            // Map properties
            foreach (var item in sourceCollection)
            {
                // New item instance
                var newItem = HasDefaultConstructor(item.GetType()) 
                    ? Activator.CreateInstance(item.GetType(), null) 
                    : item;

                // Map properties
                item.MapProperties(newItem);

                // Add to destination collection
                if (valType.BaseType == typeof(Array))
                {
                    destinationCollection[sourceCollection.IndexOf(item)] = newItem;
                }
                else
                {
                    destinationCollection.Add(newItem);
                }
            }

            // Add new collection to destination
            propInfo.SetValue(destination, destinationCollection, null);
        }
        else
        {
            propInfo.SetValue(destination, value, null);
        }

        // Check for properties and propagate if they exist.
        var newPropInfos = value.GetType().GetProperties();
        if (!newPropInfos.Any()) continue;

        // Copy properties for each child where necessary.
        var childSource = source.GetType().GetProperty(propInfo.Name);
        var childDestination = destination.GetType().GetProperty(propInfo.Name);
        childSource.MapProperties(childDestination);
    }
}

/// <summary>
/// Determines whether the type has a default contructor.
/// </summary>
/// <param name="type">The type.</param>
/// <returns></returns>
private static bool HasDefaultConstructor(Type type)
{
    return
        type.GetConstructor(Type.EmptyTypes) != null ||
        type.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
            .Any(x => x.GetParameters().All(p => p.IsOptional));
}

Here is what is going on:

1) We get the properties associated with the class type.
2) We evaluate the public properties.
3) Using reflection, we retrieve the value from the source and apply it to the destination.
4) If the value is not null, we drill down further for more public properties recursively.
Note: Collections need a bit of extra work to ensure we don’t simply copy over their object instances.
5) Repeat until the entire model has been traversed.

A simple example:


SomeType myNewObject = new SomeType();

// Assumption: "myObject" is of type "SomeType"
myObject.MapProperties(myNewObject);

In conclusion: if you can’t serialize, map the properties between the 2 models.

That’s all for now.

In Effective Extensions – Extending Enums in C# we covered how to use a few useful methods to extend Enums in C#. This time we are going to look at Extending Models in C#.

UPDATED 8/29/2014: Added a few more GetPropertyName overloads as well as GetPropertyNames.

Our first method is our initial GetPropertyName which retrieves a property name from a simple expression:


/// <summary>
/// Gets the property name from an expression.
/// </summary>
/// <param name="expression">The expression to evaluate.</param>
/// <returns></returns>
public static string GetPropertyName(this Expression expression)
{
    // Get property name
    switch (expression.NodeType)
    {
        case ExpressionType.Call:
            var callExp = expression as MethodCallExpression;
            if (callExp == null || !callExp.Arguments.Any()) return "";

            // Build body on first argument
            var callBody = callExp.Arguments.First() as MemberExpression;

            // Get property name
            return callBody == null ? "" : callBody.Member.Name;
            break;

        case ExpressionType.AndAlso:
        case ExpressionType.OrElse:
            var andOrExp = expression as BinaryExpression;
            if (andOrExp == null) return "";

            // Build body on last argument
            var andOrBody = andOrExp.Right as MemberExpression;

            // Get property name
            return andOrBody == null ? "" : andOrBody.Member.Name;
            break;

        default:
            // Member
            var body = expression as MemberExpression;

            // Body found
            if (body != null) return body.Member.Name;

            // Get body
            var ubody = (UnaryExpression)expression;
            body = ubody.Operand as MemberExpression;
            return (body != null) ? body.Member.Name : "";
            break;
    }
}

The goal of GetPropertyName is to find the relevant MemberExpression and get the property name associated with it. This is not always a simple task and different Expression types have to be evaluated in unique ways.

Let’s break this down into each case:

  1. The first block deals with a MethodCallExpression. You typically see these with expressions like this:
    x => x.MyCollection.Contains(5). We need to mine this down to the first argument to get the MemberExpression.
  2. The second and third blocks deal with complex expressions using && (AndAlso) or || (OrElse) between 2 or more statements. An example might be like this: x => x.IsActive && x.MyValue == 5. The Right value will hold the right-most, singular value as a MemberExpression, where the Left value will hold the expression less the Right. So, to properly take an expression like this apart, you would need to iterate through until Left is just a MemberExpression.
  3. The last block is a simple MemberExpression evaluation. When all else fails, go this route. Sometimes, casting our expression as a MemberExpression won’t work, so we attempt to cast it as a UnaryExpression and cast it’s Operand property as a MemberExpression.

 

Now, let’s look at some GetPropertyName overloads that will use our initial method to evaluate complex expressions:


/// <summary>
/// Gets the property name from an expression.
/// Example: this would be used for expressions in the format: "() => Name"
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression to evaluate.</param>
/// <returns></returns>
public static string GetPropertyName<T>(this Expression<Func<T>> expression)
{
    return expression.Body.GetPropertyName();
}

/// <summary>
/// Gets the property name from an expression.
/// Example: this would be used for expressions in the format: "() => IsActive"
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression.</param>
/// <returns></returns>
public static string GetPropertyName<T>(this Expression<Func<T, bool>> expression)
{
    return expression.Body.GetPropertyName();
}

/// <summary>
/// Gets the property name from an expression.
/// Example: this would be used for expressions in the format: "x => x.Name"
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="expression">The exp.</param>
/// <returns></returns>
public static string GetPropertyName<T, TProperty>(this Expression<Func<T, TProperty>> expression)
{
    return expression.Body.GetPropertyName();
}

  • The first method retrieves a property name from a simple expression such as: () => Name.
  • The second method retrieves a property name from a boolean expression such as: () => IsActive.
  • The third method retrieves a property name from a property-based expression such as: x => x.Name.

 

Q: Okay, so how do we get all the property names of a multi-statement expression that uses && or ||?
A: In order to do that, we need to do a little more work and return an array.

Let’s look at our follow-up method GetPropertyNames:


/// <summary>
/// Gets multiple property names from a complex expression.
/// Example: this would be used for expressions in the format: "x => x.Name &amp;&amp; x.IsActive"
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression.</param>
/// <returns></returns>
public static IList<string> GetPropertyNames<T>(this Expression<Func<T, bool>> expression)
{
    var names = new List<string>();

    // Body
    var expBody = expression.Body;

    // Get property name
    if (expBody is BinaryExpression)
    {
        // If a complex expression, we need to disassemble it from Right to Left,
        // Or, we simply need to get the property from the Left.
        if (expBody.IsComplexBinaryExpression())
        {
            names.AddRange(expBody.GetPropertiesFromBinary());
        }
        else
        {
            names.Add(expBody.GetPropertyNameFromSimpleBinaryExpression());
        }
    }

    else
    {
        // Get a singular property name
        names.Add(expression.GetPropertyName());
    }

    // Return unique list
    return names.GroupBy(x => x).Select(y => y.Key).ToList();
}

/// <summary>
/// Gets the properties from binary.
/// </summary>
/// <param name="expression">The expression.</param>
/// <returns></returns>
private static IEnumerable<string> GetPropertiesFromBinary(this Expression expression)
{
    var names = new List<string>();

    var currentExp = expression;
    while (currentExp is BinaryExpression)
    {
        // Cast expression
        var exp = (currentExp as BinaryExpression);

        // Body
        if (exp.IsComplexBinaryExpression())
        {
            // If "Right" is a BinaryExpression, get it's components, or
            // Just get the property name of the "Right" expression.
            var right = exp.Right as BinaryExpression;
            if (right != null)
            {
                names.AddRange(right.GetPropertiesFromBinary());
            }
            else
            {
                names.Add(exp.Right.GetPropertyName());
            }
        }

        else
        {
            // Add to list
            names.Add(exp.GetPropertyNameFromSimpleBinaryExpression());
        }

        // Next expression
        currentExp = exp.Left;
    }

    // Get last expression
    names.Add(currentExp.GetPropertyName());

    // Return
    return names;
}

/// <summary>
/// Determines whether [is complex binary expression] [the specified exp].
/// </summary>
/// <param name="exp">The exp.</param>
/// <returns></returns>
private static bool IsComplexBinaryExpression(this Expression exp)
{
    var expBody = exp as BinaryExpression;
    if (expBody == null) return false;

    // Evaluate
    return (expBody.NodeType == ExpressionType.AndAlso || expBody.NodeType == ExpressionType.OrElse);
}

/// <summary>
/// Gets the property name from simple binary expression.
/// </summary>
/// <param name="exp">The exp.</param>
/// <returns></returns>
private static string GetPropertyNameFromSimpleBinaryExpression(this Expression exp)
{
    var expBody = exp as BinaryExpression;
    if (expBody == null) return "";

    // Body
    var body = expBody.Left as MemberExpression;

    // Get property name
    return (body != null) ? body.Member.Name : "";
}

Here, we are doing what we discussed earlier, which is iterating through our expression, storing the Right value on each pass until the Left is just a MemberExpression. The result is a list of all properties involved in the expression.
The next method we will evaluate is DeepCopy which completely clones a model through serialization:


/// <summary>
/// Deeps the copy.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="oSource">The o source.</param>
/// <returns></returns>
public static T DeepCopy<T>(this T oSource)
{
    using (var ms = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, oSource);
        ms.Position = 0;
        return (T)formatter.Deserialize(ms);
    }
}

As you can see, this serializes the source with a BinaryFormatter and injects it into a MemoryStream. This is considered the most effective way to clone an object.

 

Finally, we have an IsNull method that performs a Deep Null check, which means it inspects each element in an object tree to determine if it is valid:


/// <summary>
/// Checks if property or field and all parent instances if they are null.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent">The parent.</param>
/// <param name="lambda">The lambda expression.</param>
/// <returns><c>true</c> if null; otherwise, <c>false</c>.</returns>
/// <remarks>Only properties and/or fields are allowed for all parts of the expression.</remarks>
public static bool IsNull<T>(this object parent, Expression<Func<T>> lambda)
{
    var memberParts = new List<MemberInfo>();
    var expression = lambda.Body;

    // ExpressionType.Constant occurs once we get to the parent
    while (expression.NodeType != ExpressionType.Constant)
    {
        var memberExpression = (MemberExpression)expression;
        memberParts.Add(memberExpression.Member);
        expression = memberExpression.Expression;
    }

    // Parts are extracted in reverse order, so this corrects it
    memberParts.Reverse();

    // 'instance' keeps track of the instance associated with 'member' below
    var instance = parent;
    while (memberParts.Any())
    {
        // Set the current member for evaluation on this loop
        var member = memberParts.First();

        // Break down how this is evaluated by property vs. field
        switch (member.MemberType)
        {
            case MemberTypes.Property:
                var property = (PropertyInfo)member;

                // Gets the value by invoking the property on the instance
                object value = property.GetValue(instance, null);

                // Compares value with null
                if (value == null) return true;

                // If not null, set 'instance' to the value for the next loop
                instance = value;
                break;

            case MemberTypes.Field:
                var field = (FieldInfo)member;

                // Gets the value by v the field on the instance
                value = field.GetValue(instance);

                // Compares value with null
                if (value == null) return true;

                // If not null, set 'instance' to the value for the next loop
                instance = value;
                break;

            default:
                // Method type members, and others, are not supported
                throw new InvalidOperationException("IsNull: MemberType must be Property or Field.");
        }

        // If not null, remove the first element for the next loop
        memberParts.RemoveAt(0);
    }

    // No nulls were found
    return false;
}

As demonstrated, this method inspects each element from greatest parent down until reaching our target element to ensure all elements in the tree are not null.

The usage would be like this:


// Why do this:
var test = (parent == null || parent.MyProperty == null || parent.MyProperty.ChildProperty == null);

// When you can do this:
var test = parent.IsNull(()=> parent.MyProperty.ChildProperty);

// Check
// "test" is true if parent is null.

 

And that wraps this series on extensions.

Happy coding.

In Effective Extensions – Extending Types in C# we covered how to use a few useful methods to extend Types in C#. This time we are going to look at extending enums in C#.

Q: So, why extend enums?
A: Because they are typically inflexible and require some funky casting to get what you need from simple data types such as strings and integers.

Here is our example enum:


/// <summary>
/// Thread-state type.
/// </summary>
public enum ThreadStateType
{
    [Description("Initialized")]
    Started = 0,

    [Description("Ready")]
    Waiting = 1,

    [Description("Running")]
    Processor = 2,

    [Description("Standby")]
    PreProcessor = 3,

    [Description("Terminated")]
    Ended = 4,

    [Description("Wait")]
    NotReady = 5,

    [Description("Transition")]
    WaitingForResource = 6,

    [Description("Unknown")]
    UndeterminedState = 7
}

 

You will notice that each value in the enum has a Description attribute. This can be extremely effective in allowing us to get a more meaningful value or message from an enum without having to do complex conversions. Let’s look at those.

 
ToDescription

Q: So, how do we get those descriptions?
A: With a “ToDescription” extension like the one below:


/// <summary>
/// Return the description tied to an enum.
/// </summary>
/// <param name="en">The en.</param>
/// <returns></returns>
public static string ToDescription(this Enum en)
{
    // ext method
    var type = en.GetType();
    var memberInfo = type.GetMember(en.ToString());

    // No members
    if (memberInfo.Length <= 0) return en.ToString();

    // Get attributes
    var attributes =
        memberInfo.First().GetCustomAttributes(typeof(DescriptionAttribute), false);

    // Assess attributes
    return attributes.Length > 0 
        ? ((DescriptionAttribute)attributes.First()).Description : en.ToString();
}

 
It’s not pretty, but extremely useful. It can be used like this:


// Get enum
ThreadStateType myType = ThreadStateType.Processor;

// Get description from enum
Console.Write(myType.ToDescription());

// Prints: Running

 
GetEnumFromDescription

Q: Okay, so what if I only have the enum description and want the enum value back?
A: We can do that too:


/// <summary>
/// Process a string and return an Enum value that has that description.
/// </summary>
/// <param name="value">string representing the enum's description</param>
/// <typeparam name="T">The type of Enum</typeparam>
/// <returns>Enum value corresponding to the string description</returns>
/// <exception cref="ArgumentException">T must be an enumerated type</exception>
public static T GetEnumFromDescription<T>(string value) where T : struct, IConvertible
{
	// Must be an enum
	if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");

	// Evaluate
	foreach (var val in Enum.GetValues(typeof(T)))
	{
		if (((Enum)val).ToDescription() != value) continue;
		if (val != null) return ((T)val);
	}

	// Return
	return default(T);
}

 
And that can be used like this:


// Use GetEnumFromDescription to get an enum from a string representing its description.
ThreadStateType myType = EnumExtensions.GetEnumFromDescription<ThreadStateType>("Running");

// Returns: ThreadStateType.Processor

 
ToEnum

Q: And what if I have a string representing the name of the enum and want it’s value?
A: This isn’t typical unless you did a ToString() to the enum, but we can convert that back too:


/// <summary>
/// Process a string and return an Enum value for it
/// </summary>
/// <param name="str">string representing the enum</param>
/// <typeparam name="T">The type of Enum</typeparam>
/// <returns>Enum value corresponding to the string value</returns>
/// <exception cref="ArgumentException">T must be an enumerated type</exception>
public static T ToEnum<T>(this String str) where T : struct, IConvertible
{
    if (!typeof(T).IsEnum)
        throw new ArgumentException("T must be an enumerated type");

    foreach (var value in Enum.GetValues(typeof(T)))
    {
        if (value.ToString() == str) return (T)value;
    }

    return default(T);
}

 
An example of this would be:


// Get the "Processor" enum from the string "Processor"
ThreadStateType myType = ("Processor").ToEnum<ThreadStateType>();

// Returns: ThreadStateType.Processor

 

And that’s really all there is to it. These are simple extensions for otherwise complex derivations but it should help you get over your fear of using enums in C#.

Until next time…

In Making a Better ObservableCollection Part 1 – Extensions, I talked about ways we can extend ObservableCollections to make them more useful when working with the MVVM pattern and WPF. This time we are going to talk about extending Types in C# so that they are even more powerful than before.
 

GetPropertyExtended

One thing I have always found annoying about Type.GetProperty(“MyProperty”) is that it never observes inherited properties; only the exact class you are investigating. We can easily solve this by using the following code:


/// <summary>
/// Gets the property from the type or base types.
/// Note: Type.GetProperty only looks at the actual type.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public static PropertyInfo GetPropertyExtended(this Type type, string propertyName)
{
    // Get property
    var property = type.GetProperty(propertyName);
    if (property != null) return property;

    // Base types
    var baseTypes = type.GetInterfaces();
    foreach (var baseType in baseTypes)
    {
        property = baseType.GetProperty(propertyName);
        if (property != null) return property;
    }

    // Nothing found
    return null;
}

 
This will investigate all interfaces associated with the target class and return the first match.

 

InheritsFrom

Another thing I would like to know is when a class inherits from another class and I would like to do this in a simple bool statement. That can be accomplished with the following code:


/// <summary>
/// Inherits from.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="type">The type.</param>
/// <returns></returns>
public static bool InheritsFrom<T>(this Type type)
{
    return type.InheritsFrom(typeof(T));
}

/// <summary>
/// Inherits from.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="targetType">Type of the target.</param>
/// <returns></returns>
public static bool InheritsFrom(this Type type, Type targetType)
{
    return type.GetInterface(targetType.ToString()) != null;
}

 
All we are doing here is checking to see if the target type is a base for our class. This method can be incredibly helpful when you are quickly trying to discover parentage and only want to use interfaces.

 

IsObservableCollection

This speaks for itself. We use this extension to determine if the following property or member we are dealing with is an ObservableCollection. This can be extremely helpful when making determinations around the CollectionChanged event but you might start with a less complex object like an IList or an ICollection.


/// <summary>
/// Determines whether [is observable collection] [the specified candidate].
/// </summary>
/// <param name="type">The type.</param>
/// <returns></returns>
public static bool IsObservableCollection(this Type type)
{
    // Evaluate
    return type.IsGenericType && !type.IsGenericTypeDefinition &&
        (type.GetGenericTypeDefinition() == targetType);
}

 
Next time, we will talk about extending Enums.

Happy coding…

In this post I am going to introduce the idea of subscribing to property change events in our Model Base.

Note: Please read Part I before continuing here.
Note: Please read Part II before continuing here.

The purpose of this is to cover a potentially annoying situation:

Scenario: Let’s say you have a ViewModel, and inside that you have another ViewModel acting as a property like this:


    private MyCustomObject _customObjectInstance;

    public MyCustomObject CustomObjectInstance
    {
        get
        {
            return _customObjectInstance;
        }

        set
        {
            SetValue("CustomObjectInstance", ref _customObjectInstance, ref value);
        }
    }

Okay, now you want your UI bound to the current ViewModel to change every time CustomObjectInstance.MeterReading changes.

Q: (Panic moment) So, how do I do that without breaking my wonderful abstraction?
A: Implementing an ability to Subscribe to a nested property. All it takes is a little reflection and patience.

The idea is that we tell that property to fire a specific Action whenever it is changed in our ViewModelBase.

The first thing we will need to add is a private list of subscriptions:


/// <summary>
/// Subscription list.
/// </summary>
private readonly List<Tuple<string, Action<object>>> _subscriptions;

Instead of creating yet another custom object, I decided to use a Tuple because they are convenient.

  • The string value will serve as the name of the property you want to subscribe to.
  • The Action will serve as the Action you wish to call when the property is changed. The object parameter allows you to return an object if needed.

Next, we make sure to create a new instance of _subscriptions in the Constructor:


/// <summary>
/// Default constructor.
/// </summary>
protected ViewModelBase()
{
    _subscriptions = new List<Tuple<string, Action<object>>>();
}

Now we will expose a Subscribe method.

        /// <summary>
        /// Subscribes an action to a specific property that will be called
        /// during that property's OnPropertyChanged event.
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="onChange"></param>
        public void Subscribe(string propertyName, Action<object> onChange)
        {
            // Verify property
            var propInfo = this.GetType().GetProperty(propertyName);

            // If valid, add to subscription pool.
            if (propInfo != null)
            {
                _subscriptions.Add(
                    new Tuple<string, Action<object>>(propertyName, onChange));
            }
            else
            {
                // Invalid property name provided.
                throw new Exception(
                    "Property "" + propertyName + "" could not be " +
                    "found for type "" + this.GetType().ToString() + ""!");
            }
        }

This idea here is fairly simple:

  • We pass our property name and intended Action that will fire OnPropertyChanged.
  • If the property name is not valid, we will throw an exception to ensure we didn’t pass invalid information into our Subscribe method.

Q: Alright, now we have a nice Tuple-list full of property names and Actions. Now what?
A: Glad you asked. Here comes the hard part:


        /// <summary>
        /// Processes existing subscriptions matching the provided property name.
        /// </summary>
        /// <param name="propertyName"></param>
        private void ProcessSubscriptions(string propertyName)
        {
            // Get matching subscriptions
            var subList =
                (from p in _subscriptions
                 where p.Item1 == propertyName
                 select p).ToList();

            // Check if any matches were found.
            if (subList.Any())
            {
                // Process actions
                foreach (var sub in subList)
                {
                    // Evaluate action
                    var onChange = sub.Item2;
                    if (onChange != null)
                    {
                        // Get property value by name
                        var propInfo = this.GetType().GetProperty(propertyName);
                        var propValue = propInfo.GetValue(this, null);

                        // Invoke action
                        onChange(propValue);
                    }
                }
            }
        }

ProcessSubscriptions does the following:

  • Looks up a specific property by name in _subscriptions and gets a list of all entries that are registered for that property.
  • Loop: If a specific entry has a valid Action assigned to it, it will use reflection to get that property value and pass it to the action (as out object parameter mentioned earlier).

So, the last piece is making sure ProcessSubscriptions is fired when the property has been changed. And that is as easy as augmenting our trusted OnPropertyChanged method:


        /// <summary>
        /// Calls the PropertyChanged event
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="onChanged"></param>
        protected void OnPropertyChanged(string propertyName, Action onChanged = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                // Call handler
                handler(this, new PropertyChangedEventArgs(propertyName));

                // Subscriptions
                ProcessSubscriptions(propertyName);

                // On changed
                if (onChanged != null)
                {
                    onChanged();
                }
            }
        }

No worries if your specific property being changed is without entries. ProcessSubscriptions only acts on what is present in _subscriptions, so no entries means it just moves on.

Here is how you would use it in your parent ViewModel:


CustomObjectInstance.Subscribe("MeterReading", obj => MyActionThatDoesStuff());

Now, every time CustomObjectInstance.MeterReading is updated, the MyActionThatDoesStuff Action will be called allowing you to always have the latest values from your nested properties.

Here is our new ViewModelBase in it’s entirety:


    /// <summary>
    /// Extends the INotifyPropertyChanged interface to the class properties.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #region Members

        /// <summary>
        /// Subscription list.
        /// </summary>
        private readonly List<Tuple<string, Action<object>>> _subscriptions;

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        protected ViewModelBase()
        {
            _subscriptions = new List<Tuple<string, Action<object>>>();
        }

        #endregion

        #region Methods

        /// <summary>
        /// To be used within the "set" accessor in each property.
        /// This invokes the OnPropertyChanged method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <param name="newValue"></param>
        /// <param name="onChanged"></param>
        protected void SetValue<T>(string name, ref T value, ref  T newValue,
            Action onChanged = null)
        {
            if (newValue != null)
            {
                if (!newValue.Equals(value))
                {
                    value = newValue;
                    OnPropertyChanged(name, onChanged);
                }
            }
            else
            {
                value = default(T);
            }
        }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// The PropertyChanged event handler.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Calls the PropertyChanged event
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="onChanged"></param>
        protected void OnPropertyChanged(string propertyName, Action onChanged = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                // Call handler
                handler(this, new PropertyChangedEventArgs(propertyName));

                // Subscriptions
                ProcessSubscriptions(propertyName);

                // On changed
                if (onChanged != null)
                {
                    onChanged();
                }
            }
        }

        /// <summary>
        /// Subscribes an action to a specific property that will be called
        /// during that property's OnPropertyChanged event.
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="onChange"></param>
        public void Subscribe(string propertyName, Action<object> onChange)
        {
            // Verify property
            var propInfo = this.GetType().GetProperty(propertyName);

            // If valid, add to subscription pool.
            if (propInfo != null)
            {
                _subscriptions.Add(
                    new Tuple<string, Action<object>>(propertyName, onChange));
            }
            else
            {
                // Invalid property name provided.
                throw new Exception(
                    "Property "" + propertyName + "" could not be " +
                    "found for type "" + this.GetType().ToString() + ""!");
            }
        }
        
        /// <summary>
        /// Clears the subscriptions.
        /// </summary>
        public void ClearSubscriptions()
        {
            _subscriptions.Clear();
        }

        /// <summary>
        /// Processes existing subscriptions matching the provided property name.
        /// </summary>
        /// <param name="propertyName"></param>
        private void ProcessSubscriptions(string propertyName)
        {
            // Get matching subscriptions
            var subList =
                (from p in _subscriptions
                 where p.Item1 == propertyName
                 select p).ToList();

            // Check if any matches were found.
            if (subList.Any())
            {
                // Process actions
                foreach (var sub in subList)
                {
                    // Evaluate action
                    var onChange = sub.Item2;
                    if (onChange != null)
                    {
                        // Get property value by name
                        var propInfo = this.GetType().GetProperty(propertyName);
                        var propValue = propInfo.GetValue(this, null);

                        // Invoke action
                        onChange(propValue);
                    }
                }
            }
        }

        #endregion
    }

I hope this has been helpful for you.