All posts tagged 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 second of a 2-part series on Accessing Monitor Information with C#.

The series will include the following:

Like I said in Part 1 this uses Native Methods so don’t act surprised if I drop a few in this post.

Note: if easily startled by unmanaged code, please seek the help of a medical professional. Otherwise, read on.

So, to continue, we are going to start by using our GetMonitors method to grab the current set of Monitors associated with our computer:


private static IList<MonitorInfoWithHandle> _monitorInfos;

/// <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();
}

Now, we won’t be using this method directly in this post, but you should observe the results to understand which handles are associated with each monitor on your machine. For this purposes of this exercise, we will be looking from the process-side and determining which monitor handle it relates to.

So, to continue we look to the native method EnumWindows:

 
EnumWindows
Enumerates all top-level windows on the screen by passing the handle to each window, in turn, to an application-defined callback function. EnumWindows continues until the last top-level window is enumerated or the callback function returns false.

 
EnumWindowsProc
An application-defined callback function used with the EnumWindows or EnumDesktopWindows function. It receives top-level window handles. The WNDENUMPROC type defines a pointer to this callback function. EnumWindowsProc is a placeholder for the application-defined function name.


/// <summary>
/// EnumWindows Processor (delegate)
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="lParam">The l parameter.</param>
/// <returns></returns>
public delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lParam);

/// <summary>
/// Enums the windows.
/// </summary>
/// <param name="enumWindowsProcessorDelegate">The enum windows processor delegate.</param>
/// <param name="lParam">The l parameter.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool EnumWindows(EnumWindowsProc enumWindowsProcessorDelegate, IntPtr lParam);

The key to using this method is to provide it a delegate which will act on each instance discovered and help us extract the results.

To get the monitor related to a window handle we need to first get the RECT the window occupies with GetWindowRect.

 
GetWindowRect
Retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.


/// <summary>
/// Gets the rectangle representing the frame of a window.
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="rectangle">The rectangle.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr windowHandle, ref RECT rectangle);

 
Next we need to derive the monitor from the RECT found in our last operation and we will do that using MonitorFromRect:

MonitorFromRect
The MonitorFromRect function retrieves a handle to the display monitor that has the largest area of intersection with a specified rectangle.


/// <summary>
/// Monitors from rect.
/// </summary>
/// <param name="rectPointer">The RECT pointer.</param>
/// <param name="flags">The flags.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromRect([In] ref RECT rectPointer, uint flags);

 
So, now let’s put that all together with an example:


#region Members

private const uint MONITOR_DEFAULTTONEAREST = 0x00000002;
private IList<WindowAndMonitorHandle> _windowAndMonitorHandles;

#endregion

#region Methods

/// <summary>
/// Retrieves a list of all main window handles and their associated process id's.
/// </summary>
/// <returns></returns>
public static WindowAndMonitorHandle[] GetWindowAndMonitorHandles()
{
	// new list
	_windowAndMonitorHandles = new List<WindowAndMonitorHandle>();

	// Enumerate windows
	WindowHelper.EnumWindows(EnumTheWindows, IntPtr.Zero);

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

/// <summary>
/// Enumerates through each window.
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="lParam">The l parameter.</param>
/// <returns></returns>
private static bool EnumTheWindows(IntPtr windowHandle, IntPtr lParam)
{
	// Get window area
	var rect = new RECT();
	MonitorHelper.GetWindowRect(windowHandle, ref rect);

	// Get current monitor
	var monitorHandle = MonitorHelper.MonitorFromRect(ref rect, MONITOR_DEFAULTTONEAREST);

	// Add to enumerated windows
	_windowAndMonitorHandles.Add(new WindowAndMonitorHandle(windowHandle, monitorHandle));
	return true;
}

#endregion

#region Native Methods

/// <summary>
/// EnumWindows Processor (delegate)
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="lParam">The lparameter.</param>
/// <returns></returns>
public delegate bool EnumWindowsProc(IntPtr windowHandle, IntPtr lParam);

/// <summary>
/// Enums the windows.
/// </summary>
/// <param name="enumWindowsProcessorDelegate">The enum windows processor delegate.</param>
/// <param name="lParam">The lparameter.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool EnumWindows(EnumWindowsProc enumWindowsProcessorDelegate, IntPtr lParam);

/// <summary>
/// Gets the rectangle representing the frame of a window.
/// </summary>
/// <param name="windowHandle">The window handle.</param>
/// <param name="rectangle">The rectangle.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr windowHandle, ref RECT rectangle);

/// <summary>
/// Monitors from rect.
/// </summary>
/// <param name="rectPointer">The RECT pointer.</param>
/// <param name="flags">The flags.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromRect([In] ref RECT rectPointer, uint flags);

#endregion

/// <summary>
/// A simple class to group our handles.
/// </summary>
/// <returns></returns>
public class WindowAndMonitorHandle
{
	public IntPtr WindowHandle { get; }
	public IntPtr MonitorHandle { get; }
	
	public WindowAndMonitorHandle(IntPtr windowHandle, IntPtr monitorHandle)
	{
		WindowHandle = windowHandle;
		MonitorHandle = monitorHandle;
	}
}

 
Does it seem like a lot of work? Yes, definitely. With native methods, PAIN = GAIN. Don’t concern yourself with how much code it takes, but focus on what you are actually gaining. If you compare the monitor handle for each WindowAndMonitorHandle with what you returned from GetMonitors you should immediately be able to relate a window handle to a monitor.

So, until next time…

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!

Welcome to Part 3 of our series Making a Better ObservableCollection.

If you missed Making a Better ObservableCollection Part 2 – Cross Threading you can get to it here.

We started by showing you a few neat extensions in Part 1 and then followed it up with cross-threading in Part 2. Now that we have an understanding of how to extend and offload the work to another thread, let’s cover another very important feature: Sorting.

We start by creating a custom comparer as learned here:


/// <summary>
/// Customer Sort Comparer.
/// Original Source:
/// http://blogs.msdn.com/b/jgoldb/archive/2008/08/28/improving-microsoft-datagrid-ctp-sorting-performance-part-2.aspx
/// </summary>
/// <typeparam name="T"></typeparam>
internal class CustomSortComparer<T> : ICustomSortComparer<T>
{
    #region Members

    /// <summary>
    /// A two argument delegate for comparing two objects.
    /// </summary>
    /// <param name="arg1">The arg1.</param>
    /// <param name="arg2">The arg2.</param>
    /// <returns></returns>
    protected delegate int TwoArgDelegate(T arg1, T arg2);

    /// <summary>
    /// A two argument delegate instance.
    /// </summary>
    private TwoArgDelegate _myCompare;

    #endregion

    #region Methods

    /// <summary>
    /// Sorts the specified target collection.
    /// </summary>
    /// <param name="targetCollection">The target collection.</param>
    /// <param name="propertyName">Name of the property.</param>
    /// <param name="direction">The direction.</param>
    public void Sort(ObservableCollection<T> targetCollection, string propertyName, 
        ListSortDirection direction)
    {
        // Sort comparer
        var sortComparer = new InternalSorting(propertyName, direction);

        // Sort
        var sortedCollection = targetCollection.OrderBy(x => x, sortComparer).ToList();

        // Synch
        targetCollection.SynchCollection(sortedCollection, true);
    }

    /// <summary>
    /// Performs custom sorting operation.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    /// <param name="direction">The direction.</param>
    internal void CustomSort(string propertyName, ListSortDirection direction)
    {
        int dir = (direction == ListSortDirection.Ascending) ? 1 : -1;

        // Set a delegate to be called by IComparer.Compare
        _myCompare = (a, b) => ReflectionCompareTo(a, b, propertyName) * dir;
    }

    /// <summary>
    /// Custom compareTo function to compare 2 objects derived using Reflection.
    /// If an aliasProperty is provided, the sort is performed on that property
    /// instead.
    /// This is ideal for columns with data types that need to be sorted by another
    /// data type.
    /// i.e. Images that need value associations, or strings with numeric entries.
    /// </summary>
    /// <param name="a">A.</param>
    /// <param name="b">The b.</param>
    /// <param name="propertyName">Name of the property.</param>
    /// <returns></returns>
    private static int ReflectionCompareTo(object a, object b, String propertyName)
    {
        // Get property value for "a"
        PropertyInfo aPropInfo = a.GetType().GetProperty(propertyName);
        var aValue = aPropInfo.GetValue(a, null);
        if (aValue == null) return 0;

        // Get property value for "b"
        PropertyInfo bPropInfo = b.GetType().GetProperty(propertyName);
        var bValue = bPropInfo.GetValue(b, null);
        if (bValue == null) return 0;

        // CompareTo method
        MethodInfo compareToMethod =
            aPropInfo.PropertyType.GetMethod("CompareTo", new[] { aPropInfo.PropertyType });
        if (compareToMethod == null) return 0;

        // Get result
        var compareResult = compareToMethod.Invoke(aValue, new[] { bValue });
        return Convert.ToInt32(compareResult);
    }

    #endregion

    #region ICompare

    /// <summary>
    /// Compares two objects and returns a value indicating whether one is less 
    /// than, equal to, or greater than the other.
    /// </summary>
    /// <param name="x">The first object to compare.</param>
    /// <param name="y">The second object to compare.</param>
    /// <returns>
    /// Value
    /// Condition
    /// Less than zero
    /// <paramref name="x" /> is less than <paramref name="y" />.
    /// Zero
    /// <paramref name="x" /> equals <paramref name="y" />.
    /// Greater than zero
    /// <paramref name="x" /> is greater than <paramref name="y" />.
    /// </returns>
    public int Compare(T x, T y)
    {
        return _myCompare(x, y);
    }

    #endregion

    #region InternalSorting

    /// <summary>
    /// Custom IComparer class to perform custom sorting.
    /// </summary>
    private class InternalSorting : CustomSortComparer<T>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="InternalSorting"/> class.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="direction">The direction.</param>
        public InternalSorting(string propertyName, ListSortDirection direction)
        {
            CustomSort(propertyName, direction);
        }
    }

    #endregion
}

Q: So, what is going on with this comparer?
A: Here is a summary:

  • Sort is called against an ObservableCollection, a property name, and a sort direction.
  • A sort comparer is derived by calling an internal class which invokes the CustomSort method.
  • The CustomSort method assesses the sort direction as an integer and then uses reflection to compare each set of values (ReflectionCompareTo).
  • A sorted collection is created against the target collection with the sort comparer applied.
  • The target collection is synched against the sorted collection.

Next, we augment our ObservableCollectionEx class:


/// <summary>
/// Extends the ObservableCollection object:
/// 1. Allows cross-thread updating to offload UI operations.
/// 2. Allows full sorting capabilities without affecting the UI thread.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObservableCollectionEx<T> : ObservableCollection<T>, IObservableCollectionEx<T>
{
    #region Members

    private readonly ICustomSortComparer<T> _sortComparer;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the 
    /// <see cref="ObservableCollectionEx{T}"/> class.
    /// </summary>
    public ObservableCollectionEx()
    {
        _sortComparer = new CustomSortComparer<T>();
    }

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="ObservableCollectionEx{T}" /> class.
    /// </summary>
    /// <param name="collection">The collection.</param>
    public ObservableCollectionEx(IEnumerable<T> collection) : this()
    {
        this.AddRange(collection);
    }

    #endregion

    #region Methods

    /// <summary>
    /// Sorts the observable collection by the property and sort direction.
    /// </summary>
    /// <param name="propertyName">The property within the ObservableCollectionExtender object
    /// to sort by.</param>
    /// <param name="direction">The desired sort direction.</param>
    public void Sort(string propertyName, ListSortDirection direction)
    {
        if (!this.Any()) return;
        _sortComparer.Sort(this, propertyName, direction);
    }

    /// <summary>
    /// Sorts the specified expression.
    /// </summary>
    /// <typeparam name="TProperty">The type of the property.</typeparam>
    /// <param name="expression">The expression.</param>
    /// <param name="direction">The direction.</param>
    public void Sort<TProperty>(Expression<Func<T, TProperty>> expression, 
    ListSortDirection direction)
    {
        if (!this.Any()) return;
        Sort(expression.GetPropertyName(), direction);
    }

    #endregion

    #region Events

    /// <summary>
    /// Source: New Things I Learned
    /// Title: Have worker thread update ObservableCollection that is bound to a ListCollectionView
    /// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx
    /// Note: Improved for clarity and the following of proper coding standards.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)  
    {
        // Use BlockReentrancy
        using (BlockReentrancy())
        {
            var eventHandler = CollectionChanged;
            if (eventHandler == null) return;

            // Only proceed if handler exists.
            Delegate[] delegates = eventHandler.GetInvocationList();

            // Walk through invocation list.
            foreach (var @delegate in delegates)
            {
                var handler = (NotifyCollectionChangedEventHandler)@delegate;
                var currentDispatcher = handler.Target as DispatcherObject;

                // If the subscriber is a DispatcherObject and different thread.
                if ((currentDispatcher != null) &amp;&amp; (!currentDispatcher.CheckAccess()))
                {
                    // Invoke handler in the target dispatcher's thread.
                    currentDispatcher.Dispatcher.Invoke(
                        DispatcherPriority.DataBind, handler, this, e);
                }

                else
                {
                    // Execute as-is
                    handler(this, e);
                }
            }
        }
    }
}

/// <summary>
/// Overridden NotifyCollectionChangedEventHandler event.
/// </summary>
public override event NotifyCollectionChangedEventHandler CollectionChanged;

#endregion

What’s new is our 2 Sort methods. One uses a property name and the other an expression to strongly define the property. Both accomplish calling the sort comparer we just built earlier.

Q: So, why do all this? What was the point?
A: Custom sort comparers will give us the ability to increase the sorting performance of our ObservableCollections when bound to controls. It will also allow us to perform this work in an async thread instead of having to create a CollectionViewSource on the UI thread which will negatively impact the user experience during updates.

Next time, we will look at applying this to a DataGrid to greatly improve sort performance and reliability.

Welcome to Part 2 of our series Making a Better ObservableCollection. If you missed Making a Better ObservableCollection Part 1 – Extensions you can get to it here.

In this next section I am going to share a version of my ObservableCollectionEx that allows cross-threading. The idea here is to have an ObservableCollection which you can update from an Async thread so as not to impact the owning thread. This is especially useful in WPF when you don’t wish to block the UI thread while performing collection updates.

Let’s see the code:


/// <summary>
/// Initializes a new instance of the 
/// <see cref="ObservableCollectionEx{T}"/> class.
/// </summary> 
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="ObservableCollectionEx{T}" /> class.
    /// </summary>
    public ObservableCollectionEx()
    {
    }

    ///
    /// Initializes a new instance of the
    ///  class.
    ///
    ///The collection.
    public ObservableCollectionEx(IEnumerable<T> collection) : this()
    {
        this.AddRange(collection);
    }

    #endregion

    #region Events

    /// <summary>
    /// Source: New Things I Learned
    /// Title: Have worker thread update ObservableCollection that is bound to a ListCollectionView
    /// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx
    /// Note: Improved for clarity and the following of proper coding standards.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        // Use BlockReentrancy
        using (BlockReentrancy())
        {
            var eventHandler = CollectionChanged;
            if (eventHandler == null) return;

            // Only proceed if handler exists.
            Delegate[] delegates = eventHandler.GetInvocationList();

            // Walk through invocation list.
            foreach (var @delegate in delegates)
            {
                var handler = (NotifyCollectionChangedEventHandler)@delegate;
                var currentDispatcher = handler.Target as DispatcherObject;

                // If the subscriber is a DispatcherObject and different thread.
                if ((currentDispatcher != null) &amp;&amp; (!currentDispatcher.CheckAccess()))
                {
                    // Invoke handler in the target dispatcher's thread.
                    currentDispatcher.Dispatcher.Invoke(
                        DispatcherPriority.DataBind, handler, this, e);
                }

                else
                {
                    // Execute as-is
                    handler(this, e);
                }
            }
        }
    }

    /// <summary>
    /// Overridden NotifyCollectionChangedEventHandler event.
    /// </summary>
    public override event NotifyCollectionChangedEventHandler CollectionChanged;

    #endregion
}

The constructors are pretty straight forward. We want to have an empty constructor and one that allows an immediate “AddRange” of an IEnumerable just like List and ObservableCollection allow, but that is not really the point of this post.

The main feature here is a slightly reformatted version of a wonderful post from a blog called New Things I Learned which covers cross-thread access with an ObservableCollection.

So, what does this code do?

  1. First we use BlockReentracy to prevent changes to the collection while we are evaluating it.
  2. Next, we get the Invocation List from the CollectionChanged event handler. This is the list of delegates subscribing to the event.
  3. Evaluate each delegate by retrieving it’s Target and casting it to a DispatcherObject.
  4. Make sure the DispatcherObject is valid and that the current thread has access.
  5. Invoke the delegate in the DispatcherObject’s thread.

It’s not the simplest code to follow when you aren’t overly familiar with working with event delegates but it gets the job done quite well.

Next time, we will talk about using a custom SortComparer to improve DataGrid performance.

Until next time.

It’s actually kind of difficult to imagine writing anything in WPF without using at least one ObservableCollection or several instances of INotifyPropertyChanged (INPC). And it’s important to note that neither of these are perfect out of the box. I have spent a bit of time talking about ways to streamline your INPC implementations in the 3-part series “Creating a Model Base“, and plan to have another update to that soon. This topic will focus on the ObservableCollection.

Here are a few reasons why we use it:

  • When controls bind to them, Add, Move, and Remove actions automatically update in the UI without any need to directly update those controls.
  • It has a CollectionChanged event allowing us to leverage when items are added, moved, or removed.

Essentially, it let’s us know when our collection gets updated and allows us to leverage the UI without much effort.

Q: So, why extend it?
A: Because there are some extras all good collections should have to make our lives easier.

For this article, we’ll start simple with a few extensions, then move on to multi-threading, then sorting.

 
Apply

One extension I have always wanted to see in any collection is the ability to commit changes to a list in one line. That is what Apply is good for.

So, normally if you wanted to set all the items in a collection to true (assuming the model has a property called “IsSelected”), you would do the following:


foreach (var item in collection)
{
    item.IsSelected = true;
}

With Apply I could write the same code like this:


collection.Apply(x => x.IsSelected = true);

The code is relatively simple:


/// <summary>
/// Applies the specified changes to the collection.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="predicate">The predicate.</param>
public static void Apply<T>(this IEnumerable<T> items, Action<T> predicate)
{
    foreach (var item in items)
    {
        predicate(item);
    }
}

It’s our familiar for loop that processes each item with the same predicate in a lambda expression.

 
AddRange and RemoveRange

I am of the belief every collection should allow you to add or remove multiple items at a time. We already have this functionality in List, so why not here? It’s actually not hard at all with our friend >Apply:


/// <summary>
/// Adds the range.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="collection">The collection.</param>
public static void AddRange<T>(this IList<T> items, IEnumerable<T> collection)
{
    // Add range to local items
    collection.Apply(items.Add);
}

/// <summary>
/// Removes the range.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="collection">The collection.</param>
public static void RemoveRange<T>(this IList<T> items, IEnumerable<T> collection)
{
    // Remove range from local items
    collection.Apply(p => items.Remove(p));
}

If we want to add multiple items to our collection, for example, all items that are selected in a new collection, all we would have to do is this:


collection.AddRange(newCollection.Where(p => p.IsSelected));

If we want to remove the unselected items listed in the new collection, we would do this:


collection.RemoveRange(newCollection.Where(p => p.IsSelected == false));

 
 
SynchCollection

Another common need is to be able to synch the items of 2 collections. This is often used as a way to maintain the instance of the target collection having it update itself based on the contents of another collection.

Our first method matches the item content in our target collection based on the provided source:


/// <summary>
/// Synches the collection items to the target collection items.
/// This does not observe sort order.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="collection">The collection.</param>
public static void SynchCollection<T>(this IList<T> items, IEnumerable<T> collection)
{
    // Evaluate
    if (collection == null) return;

    // Make a list
    var list = collection.ToList();

    // Add items not in FilteredViewItems that are in list
    items.AddRange(list.Where(p => items.IndexOf(p) == -1).ToList());

    // Remove items from FilteredViewItems not in list
    items.RemoveRange(items.Where(p => list.IndexOf(p) == -1).ToList());
}

AddRange and RemoveRange are a big help here; allowing us to tidy up our code quite a bit.

Q: What about sorting?
A: We need to do a little more work for this, but with an ObservableCollection instead of a List as our target this is possible.


/// <summary>
/// Synches the collection items to the target collection items.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items">The items.</param>
/// <param name="collection">The collection.</param>
/// <param name="canSort">if set to <c>true</c> [can sort].</param>
public static void SynchCollection<T>(this ObservableCollection<T> items, 
    IList<T> collection, bool canSort = false)
{
    // Synch collection
    SynchCollection(items, collection.AsEnumerable());

    // Sort collection
    if (!canSort) return;

    // Update indexes as needed
    for (var i = 0; i < collection.Count; i++)
    {
        // Index of new location
        int index = items.IndexOf(collection[i]);
        if (index == i) continue;

        // Move item to new index if it has changed.
        items.Move(index, i);
    }
}

Since we are talking sorting, we should be talking about ObservableCollections. We go ahead and use our previous SynchCollection method to handle the synch. After that, all we need to do is compare indexes between collections and move items as needed.

Note: This collection does not take a sort comparer because it assumes the source collection is already sorted. You should handle this type of operation independently.

And that’s all there is to it. Next time we will talk about Multithreading an ObservableCollection to offload the work from the UI thread.

If you have ideas for other extensions you would like to see, comment below.

Until next time.

Note: This is a continuation of the very popular article Customizing the WPF MenuItem in XAML.

The source code for this solution (Visual Studio 2012) can be downloaded HERE

Q: I have a WPF submenu but I want to limit it’s height. How do I do that?
A: As mentioned in the earlier post you need to override the default Aero MenuItem template and components for WPF.

Q: So, where do we need to modify the style to fix this?
A: There are actually 2 areas in the style that require modification.

These are lines 180 – 199 of the Style (as seen in the original post linked above):


<ContentControl Name="SubMenuBorder"
			Template="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=SubmenuContent}}"
			IsTabStop="false">
	<ScrollViewer Name="SubMenuScrollViewer" CanContentScroll="true" MaxHeight="400"
				  Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=MenuScrollViewer}}">
		<Grid RenderOptions.ClearTypeHint="Enabled">
			<Canvas Height="0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top">
				<Rectangle
				Height="{Binding ElementName=SubMenuBorder,Path=ActualHeight}" 
				Width="{Binding ElementName=SubMenuBorder,Path=ActualWidth}" 
				Fill="{StaticResource SubMenuBackgroundBrush}" />
			</Canvas>
			<ItemsPresenter Name="ItemsPresenter" Margin="2"
						KeyboardNavigation.TabNavigation="Cycle"
						KeyboardNavigation.DirectionalNavigation="Cycle"
						SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
						Grid.IsSharedSizeScope="true"/>
		</Grid>
	</ScrollViewer>
</ContentControl>

Notice on line 183 (line 4), we added the MaxHeight=”400″

We repeat the again in the code between lines 457 and 476 of the Style (as seen in the original post linked above):


<ContentControl Name="SubMenuBorder"
				Template="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=SubmenuContent}}"
				IsTabStop="false">
	<ScrollViewer Name="SubMenuScrollViewer" CanContentScroll="true" MaxHeight="400"
				  Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type FrameworkElement}, ResourceId=MenuScrollViewer}}">
		<Grid RenderOptions.ClearTypeHint="Enabled">
			<Canvas Height="0" Width="0" HorizontalAlignment="Left" VerticalAlignment="Top">
				<Rectangle
				Height="{Binding ElementName=SubMenuBorder,Path=ActualHeight}" 
				Width="{Binding ElementName=SubMenuBorder,Path=ActualWidth}" 
				Fill="{StaticResource SubMenuBackgroundBrush}" />
			</Canvas>
			<ItemsPresenter Name="ItemsPresenter" Margin="2"
						KeyboardNavigation.TabNavigation="Cycle"
						KeyboardNavigation.DirectionalNavigation="Cycle"
						SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
						Grid.IsSharedSizeScope="true"/>
		</Grid>
	</ScrollViewer>
</ContentControl>

Notice on line 460 (line 4), we added the MaxHeight=”400″

The blocks are virtually identical except the first one affects the initial Submenu and the second handles the child Submenu.

The intent here is to control the MaxHeight with a property so, we are going to do just that.

My Project file layout:

Project file layout in VS2012

Important files:

  • MenuItemEnhanced.xaml: Our new and improved MenuItem control (now with submenu height features!)
  • MenuItemEnhanced.xaml.cs: Code-behind for our new and improved MenuItem control.
  • Styles.xaml: A ResourceDictionary containing the MenuItem style mentioned above in it’s entirety.

All right. We saw the initial style sheet in it’s “glory”. Lets look at the code for the new control.

The MenuItemEnhanced XAML:


<MenuItem x:Class="Xcalibur.UI.Menu.Controls.MenuItemEnhanced"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <MenuItem.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Xcalibur.UI.Menu;component/Styles/Styles.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </MenuItem.Resources>
</MenuItem>

Nothing overly remarkable here. We created a new control that is based on MenuItem.

Now the code-behind:


using System.Windows;
using System.Windows.Controls;

namespace Xcalibur.UI.Menu.Controls
{
    /// <summary>
    /// An extended MenuItem
    /// </summary>
    public partial class MenuItemEnhanced : MenuItem
    {
        #region Properties
        
        /// <summary>
        /// Gets or sets the height of the max submenu.
        /// </summary>
        /// <value>
        /// The height of the max submenu.
        /// </value>
        public double MaxSubmenuHeight
        {
            get { return (double)GetValue(MaxSubmenuHeightProperty); }
            set { SetValue(MaxSubmenuHeightProperty, value); }
        }

        /// <summary>
        /// Gets or sets the height of the max submenu (Dependency Property)
        /// </summary>
        public static readonly DependencyProperty MaxSubmenuHeightProperty =
            DependencyProperty.Register("MaxSubmenuHeight", typeof(double), 
            typeof(MenuItemEnhanced), new PropertyMetadata(400.0));
        
        #endregion

        #region Constructors
        
        /// <summary>
        /// Initializes a new instance of the <see cref="MenuItemEnhanced"/> class.
        /// </summary>
        public MenuItemEnhanced()
        {
            this.InitializeComponent();
        }

        #endregion
    }
}

Here, we created a dependency property called MaxSubmenuHeight of type double. The default height in this case is 400.0 since we used that height in the last post. Nothing more needed to be done here.

The kicker is updating our ResourceDictionary to observe our new, awesome DependencyProperty. To do that, we will go back to those 2 sections of XAML in the style sheet and change this:

MaxHeight=”400″

to this:

MaxHeight=”{Binding Path=MaxSubmenuHeight,RelativeSource={RelativeSource TemplatedParent}}”

That’s all there is to it.

And now for your viewing pleasure (as included in the project linked above), here is an example. The initial menu has been capped at 600px and it’s submenu at 200px:


<Window x:Class="TestBed.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:menu="clr-namespace:Xcalibur.UI.Menu.Controls;assembly=Xcalibur.UI.Menu"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Menu>
            <menu:MenuItemEnhanced Header="Test Menu" MaxSubmenuHeight="600">
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Test Item 1" />
                <menu:MenuItemEnhanced Header="Submenu" MaxSubmenuHeight="200">
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                    <menu:MenuItemEnhanced Header="Test Item 1" />
                </menu:MenuItemEnhanced>
            </menu:MenuItemEnhanced>
        </Menu>
    </Grid>
</Window>

And that looks like this:

Screenshot of our MenuItemEnhanced control in action

That’s all for now. Hope I helped you solve this annoying issue.

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.

In this article, we are going to look at calling an Action when our property changes.

Note: Please read Part I before continuing here.

Alright, so let’s look at our ViewModelBase from Part I:


    /// <summary>
    /// Extends the INotifyPropertyChanged interface to the class properties.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #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>
        protected void SetValue<T>(string name, ref T value, ref  T newValue)
        {
            if (newValue != null)
            {
                if (!newValue.Equals(value))
                {
                    value = newValue;
                    OnPropertyChanged(name);
                }
            }
            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>
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }

Q: Okay, so what now?
A: Glad you asked. Let’s augment the above class:


    /// <summary>
    /// Extends the INotifyPropertyChanged interface to the class properties.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #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>
        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>
        protected void OnPropertyChanged(string propertyName, Action onChanged = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));

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

        #endregion
    }

Essentially, we added an Action as a parameter to the SetValue method. This is then passed whenever the OnPropertyChanged method is called and executed after the event fires (if it is not null).

Using it is as easy as this:


    private string phoneNumberValue = String.Empty;

    public string PhoneNumber
    {
        get
        {
            return this.phoneNumberValue;
        }

        set
        {
            SetValue("PhoneNumber", ref this.phoneNumberValue, ref value, () => { UpdateSomeOtherUISection(); });
        }
    }

It’s not a common need, but sometimes something like this can really get you out of a bind when you need to update other areas of your application when this property changes.

Try it sometime.